mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 13:03:42 +00:00
feat(coding-agent): ResourceLoader, package management, and /reload command (#645)
- Add ResourceLoader interface and DefaultResourceLoader implementation - Add PackageManager for npm/git extension sources with install/remove/update - Add session.reload() and session.bindExtensions() APIs - Add /reload command in interactive mode - Add CLI flags: --skill, --theme, --prompt-template, --no-themes, --no-prompt-templates - Add pi install/remove/update commands for extension management - Refactor settings.json to use arrays for skills, prompts, themes - Remove legacy SkillsSettings source flags and filters - Update SDK examples and documentation for ResourceLoader pattern - Add theme registration and loadThemeFromPath for dynamic themes - Add getShellEnv to include bin dir in PATH for bash commands
This commit is contained in:
parent
866d21c252
commit
b846a4bfcf
51 changed files with 2724 additions and 1852 deletions
|
|
@ -115,10 +115,22 @@ Additional paths via `settings.json`:
|
|||
|
||||
```json
|
||||
{
|
||||
"extensions": ["/path/to/extension.ts", "/path/to/extension/dir"]
|
||||
"extensions": [
|
||||
"npm:@foo/bar@1.0.0",
|
||||
"git:github.com/user/repo@v1",
|
||||
"/path/to/extension.ts",
|
||||
"/path/to/extension/dir"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Use `pi install` and `pi remove` to manage extension sources in settings:
|
||||
|
||||
```bash
|
||||
pi install npm:@foo/bar@1.0.0
|
||||
pi remove npm:@foo/bar
|
||||
```
|
||||
|
||||
**Discovery rules:**
|
||||
|
||||
1. **Direct files:** `extensions/*.ts` or `*.js` → loaded directly
|
||||
|
|
@ -146,13 +158,17 @@ Additional paths via `settings.json`:
|
|||
"zod": "^3.0.0"
|
||||
},
|
||||
"pi": {
|
||||
"extensions": ["./src/safety-gates.ts", "./src/custom-tools.ts"]
|
||||
"extensions": ["./src/safety-gates.ts", "./src/custom-tools.ts"],
|
||||
"skills": ["./skills/"],
|
||||
"prompts": ["./prompts/"],
|
||||
"themes": ["./themes/dark.json"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `package.json` approach enables:
|
||||
- Multiple extensions from one package
|
||||
- Skills, prompts, and themes declared alongside extensions
|
||||
- Third-party npm dependencies (resolved via jiti)
|
||||
- Nested source structure (no depth limit within the package)
|
||||
- Deployment to and installation from npm
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ See [examples/sdk/](../examples/sdk/) for working examples from minimal to full
|
|||
## Quick Start
|
||||
|
||||
```typescript
|
||||
import { createAgentSession, discoverAuthStorage, discoverModels, SessionManager } from "@mariozechner/pi-coding-agent";
|
||||
import { AuthStorage, createAgentSession, ModelRegistry, SessionManager } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
// Set up credential storage and model registry
|
||||
const authStorage = discoverAuthStorage();
|
||||
const modelRegistry = discoverModels(authStorage);
|
||||
const authStorage = new AuthStorage();
|
||||
const modelRegistry = new ModelRegistry(authStorage);
|
||||
|
||||
const { session } = await createAgentSession({
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
|
|
@ -51,20 +51,17 @@ The SDK is included in the main package. No separate installation needed.
|
|||
|
||||
The main factory function. Creates an `AgentSession` with configurable options.
|
||||
|
||||
**Philosophy:** "Omit to discover, provide to override."
|
||||
- Omit an option → pi discovers/loads from standard locations
|
||||
- Provide an option → your value is used, discovery skipped for that option
|
||||
`createAgentSession()` uses a `ResourceLoader` to supply extensions, skills, prompt templates, themes, and context files. If you do not provide one, it uses `DefaultResourceLoader` with standard discovery.
|
||||
|
||||
```typescript
|
||||
import { createAgentSession } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
// Minimal: all defaults (discovers everything from cwd and ~/.pi/agent)
|
||||
// Minimal: defaults with DefaultResourceLoader
|
||||
const { session } = await createAgentSession();
|
||||
|
||||
// Custom: override specific options
|
||||
const { session } = await createAgentSession({
|
||||
model: myModel,
|
||||
systemPrompt: "You are helpful.",
|
||||
tools: [readTool, bashTool],
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
});
|
||||
|
|
@ -251,7 +248,7 @@ session.subscribe((event) => {
|
|||
|
||||
```typescript
|
||||
const { session } = await createAgentSession({
|
||||
// Working directory for project-local discovery
|
||||
// Working directory for DefaultResourceLoader discovery
|
||||
cwd: process.cwd(), // default
|
||||
|
||||
// Global config directory
|
||||
|
|
@ -259,14 +256,14 @@ const { session } = await createAgentSession({
|
|||
});
|
||||
```
|
||||
|
||||
`cwd` is used for:
|
||||
`cwd` is used by `DefaultResourceLoader` for:
|
||||
- Project extensions (`.pi/extensions/`)
|
||||
- Project skills (`.pi/skills/`)
|
||||
- Project prompts (`.pi/prompts/`)
|
||||
- Context files (`AGENTS.md` walking up from cwd)
|
||||
- Session directory naming
|
||||
|
||||
`agentDir` is used for:
|
||||
`agentDir` is used by `DefaultResourceLoader` for:
|
||||
- Global extensions (`extensions/`)
|
||||
- Global skills (`skills/`)
|
||||
- Global prompts (`prompts/`)
|
||||
|
|
@ -276,14 +273,16 @@ const { session } = await createAgentSession({
|
|||
- Credentials (`auth.json`)
|
||||
- Sessions (`sessions/`)
|
||||
|
||||
When you pass a custom `ResourceLoader`, `cwd` and `agentDir` no longer control resource discovery. They still influence session naming and tool path resolution.
|
||||
|
||||
### Model
|
||||
|
||||
```typescript
|
||||
import { getModel } from "@mariozechner/pi-ai";
|
||||
import { discoverAuthStorage, discoverModels } from "@mariozechner/pi-coding-agent";
|
||||
import { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
const authStorage = discoverAuthStorage();
|
||||
const modelRegistry = discoverModels(authStorage);
|
||||
const authStorage = new AuthStorage();
|
||||
const modelRegistry = new ModelRegistry(authStorage);
|
||||
|
||||
// Find specific built-in model (doesn't check if API key exists)
|
||||
const opus = getModel("anthropic", "claude-opus-4-5");
|
||||
|
|
@ -327,11 +326,11 @@ API key resolution priority (handled by AuthStorage):
|
|||
4. Fallback resolver (for custom provider keys from `models.json`)
|
||||
|
||||
```typescript
|
||||
import { AuthStorage, ModelRegistry, discoverAuthStorage, discoverModels } from "@mariozechner/pi-coding-agent";
|
||||
import { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
// Default: uses ~/.pi/agent/auth.json and ~/.pi/agent/models.json
|
||||
const authStorage = discoverAuthStorage();
|
||||
const modelRegistry = discoverModels(authStorage);
|
||||
const authStorage = new AuthStorage();
|
||||
const modelRegistry = new ModelRegistry(authStorage);
|
||||
|
||||
const { session } = await createAgentSession({
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
|
|
@ -360,16 +359,17 @@ const simpleRegistry = new ModelRegistry(authStorage);
|
|||
|
||||
### System Prompt
|
||||
|
||||
Use a `ResourceLoader` to override the system prompt:
|
||||
|
||||
```typescript
|
||||
const { session } = await createAgentSession({
|
||||
// Replace entirely
|
||||
systemPrompt: "You are a helpful assistant.",
|
||||
|
||||
// Or modify default (receives default, returns modified)
|
||||
systemPrompt: (defaultPrompt) => {
|
||||
return `${defaultPrompt}\n\n## Additional Rules\n- Be concise`;
|
||||
},
|
||||
import { createAgentSession, DefaultResourceLoader } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
const loader = new DefaultResourceLoader({
|
||||
systemPromptOverride: () => "You are a helpful assistant.",
|
||||
});
|
||||
await loader.reload();
|
||||
|
||||
const { session } = await createAgentSession({ resourceLoader: loader });
|
||||
```
|
||||
|
||||
> See [examples/sdk/03-custom-prompt.ts](../examples/sdk/03-custom-prompt.ts)
|
||||
|
|
@ -462,61 +462,45 @@ const { session } = await createAgentSession({
|
|||
});
|
||||
```
|
||||
|
||||
Custom tools passed via `customTools` are combined with extension-registered tools. Extensions discovered from `~/.pi/agent/extensions/` and `.pi/extensions/` can also register tools via `pi.registerTool()`.
|
||||
Custom tools passed via `customTools` are combined with extension-registered tools. Extensions loaded by the ResourceLoader can also register tools via `pi.registerTool()`.
|
||||
|
||||
> See [examples/sdk/05-tools.ts](../examples/sdk/05-tools.ts)
|
||||
|
||||
### Extensions
|
||||
|
||||
By default, extensions are discovered from multiple locations:
|
||||
- `~/.pi/agent/extensions/` (global)
|
||||
- `.pi/extensions/` (project-local)
|
||||
- Paths listed in `settings.json` `"extensions"` array
|
||||
Extensions are loaded by the `ResourceLoader`. `DefaultResourceLoader` discovers extensions from `~/.pi/agent/extensions/`, `.pi/extensions/`, and settings.json extension sources.
|
||||
|
||||
```typescript
|
||||
import { createAgentSession, type ExtensionFactory } from "@mariozechner/pi-coding-agent";
|
||||
import { createAgentSession, DefaultResourceLoader } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
// Inline extension factory
|
||||
const myExtension: ExtensionFactory = (pi) => {
|
||||
pi.on("tool_call", async (event, ctx) => {
|
||||
console.log(`Tool: ${event.toolName}`);
|
||||
});
|
||||
|
||||
pi.registerCommand("hello", {
|
||||
description: "Say hello",
|
||||
handler: async (args, ctx) => {
|
||||
ctx.ui.notify("Hello!", "info");
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Pass inline extensions (skips file discovery)
|
||||
const { session } = await createAgentSession({
|
||||
extensions: [myExtension],
|
||||
});
|
||||
|
||||
// Add paths to load (merged with discovery)
|
||||
const { session } = await createAgentSession({
|
||||
const loader = new DefaultResourceLoader({
|
||||
additionalExtensionPaths: ["/path/to/my-extension.ts"],
|
||||
extensionFactories: [
|
||||
(pi) => {
|
||||
pi.on("agent_start", () => {
|
||||
console.log("[Inline Extension] Agent starting");
|
||||
});
|
||||
},
|
||||
],
|
||||
});
|
||||
await loader.reload();
|
||||
|
||||
// Disable extension discovery entirely
|
||||
const { session } = await createAgentSession({
|
||||
extensions: [],
|
||||
});
|
||||
const { session } = await createAgentSession({ resourceLoader: loader });
|
||||
```
|
||||
|
||||
Extensions can register tools, subscribe to events, add commands, and more. See [extensions.md](extensions.md) for the full API.
|
||||
|
||||
**Event Bus:** Extensions can communicate via `pi.events`. Pass a shared `eventBus` to `createAgentSession()` if you need to emit/listen from outside:
|
||||
**Event Bus:** Extensions can communicate via `pi.events`. Pass a shared `eventBus` to `DefaultResourceLoader` if you need to emit or listen from outside:
|
||||
|
||||
```typescript
|
||||
import { createAgentSession, createEventBus } from "@mariozechner/pi-coding-agent";
|
||||
import { createEventBus, DefaultResourceLoader } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
const eventBus = createEventBus();
|
||||
const { session } = await createAgentSession({ eventBus });
|
||||
const loader = new DefaultResourceLoader({
|
||||
eventBus,
|
||||
});
|
||||
await loader.reload();
|
||||
|
||||
// Listen for events from extensions
|
||||
eventBus.on("my-extension:status", (data) => console.log(data));
|
||||
```
|
||||
|
||||
|
|
@ -525,14 +509,13 @@ eventBus.on("my-extension:status", (data) => console.log(data));
|
|||
### Skills
|
||||
|
||||
```typescript
|
||||
import { createAgentSession, discoverSkills, type Skill } from "@mariozechner/pi-coding-agent";
|
||||
import {
|
||||
createAgentSession,
|
||||
DefaultResourceLoader,
|
||||
type Skill,
|
||||
} from "@mariozechner/pi-coding-agent";
|
||||
|
||||
// Discover and filter
|
||||
const { skills: allSkills, warnings } = discoverSkills();
|
||||
const filtered = allSkills.filter(s => s.name.includes("search"));
|
||||
|
||||
// Custom skill
|
||||
const mySkill: Skill = {
|
||||
const customSkill: Skill = {
|
||||
name: "my-skill",
|
||||
description: "Custom instructions",
|
||||
filePath: "/path/to/SKILL.md",
|
||||
|
|
@ -540,20 +523,15 @@ const mySkill: Skill = {
|
|||
source: "custom",
|
||||
};
|
||||
|
||||
const { session } = await createAgentSession({
|
||||
skills: [...filtered, mySkill],
|
||||
const loader = new DefaultResourceLoader({
|
||||
skillsOverride: (current) => ({
|
||||
skills: [...current.skills, customSkill],
|
||||
diagnostics: current.diagnostics,
|
||||
}),
|
||||
});
|
||||
await loader.reload();
|
||||
|
||||
// Disable skills
|
||||
const { session } = await createAgentSession({
|
||||
skills: [],
|
||||
});
|
||||
|
||||
// Discovery with settings filter
|
||||
const { skills } = discoverSkills(process.cwd(), undefined, {
|
||||
ignoredSkills: ["browser-*"], // glob patterns to exclude
|
||||
includeSkills: ["search-*"], // glob patterns to include (empty = all)
|
||||
});
|
||||
const { session } = await createAgentSession({ resourceLoader: loader });
|
||||
```
|
||||
|
||||
> See [examples/sdk/04-skills.ts](../examples/sdk/04-skills.ts)
|
||||
|
|
@ -561,26 +539,19 @@ const { skills } = discoverSkills(process.cwd(), undefined, {
|
|||
### Context Files
|
||||
|
||||
```typescript
|
||||
import { createAgentSession, discoverContextFiles } from "@mariozechner/pi-coding-agent";
|
||||
import { createAgentSession, DefaultResourceLoader } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
// Discover AGENTS.md files
|
||||
const discovered = discoverContextFiles();
|
||||
|
||||
// Add custom context
|
||||
const { session } = await createAgentSession({
|
||||
contextFiles: [
|
||||
...discovered,
|
||||
{
|
||||
path: "/virtual/AGENTS.md",
|
||||
content: "# Guidelines\n\n- Be concise\n- Use TypeScript",
|
||||
},
|
||||
],
|
||||
const loader = new DefaultResourceLoader({
|
||||
agentsFilesOverride: (current) => ({
|
||||
agentsFiles: [
|
||||
...current.agentsFiles,
|
||||
{ path: "/virtual/AGENTS.md", content: "# Guidelines\n\n- Be concise" },
|
||||
],
|
||||
}),
|
||||
});
|
||||
await loader.reload();
|
||||
|
||||
// Disable context files
|
||||
const { session } = await createAgentSession({
|
||||
contextFiles: [],
|
||||
});
|
||||
const { session } = await createAgentSession({ resourceLoader: loader });
|
||||
```
|
||||
|
||||
> See [examples/sdk/07-context-files.ts](../examples/sdk/07-context-files.ts)
|
||||
|
|
@ -588,9 +559,11 @@ const { session } = await createAgentSession({
|
|||
### Slash Commands
|
||||
|
||||
```typescript
|
||||
import { createAgentSession, discoverPromptTemplates, type PromptTemplate } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
const discovered = discoverPromptTemplates();
|
||||
import {
|
||||
createAgentSession,
|
||||
DefaultResourceLoader,
|
||||
type PromptTemplate,
|
||||
} from "@mariozechner/pi-coding-agent";
|
||||
|
||||
const customCommand: PromptTemplate = {
|
||||
name: "deploy",
|
||||
|
|
@ -599,9 +572,15 @@ const customCommand: PromptTemplate = {
|
|||
content: "# Deploy\n\n1. Build\n2. Test\n3. Deploy",
|
||||
};
|
||||
|
||||
const { session } = await createAgentSession({
|
||||
promptTemplates: [...discovered, customCommand],
|
||||
const loader = new DefaultResourceLoader({
|
||||
promptsOverride: (current) => ({
|
||||
prompts: [...current.prompts, customCommand],
|
||||
diagnostics: current.diagnostics,
|
||||
}),
|
||||
});
|
||||
await loader.reload();
|
||||
|
||||
const { session } = await createAgentSession({ resourceLoader: loader });
|
||||
```
|
||||
|
||||
> See [examples/sdk/08-prompt-templates.ts](../examples/sdk/08-prompt-templates.ts)
|
||||
|
|
@ -719,62 +698,31 @@ Settings load from two locations and merge:
|
|||
1. Global: `~/.pi/agent/settings.json`
|
||||
2. Project: `<cwd>/.pi/settings.json`
|
||||
|
||||
Project overrides global. Nested objects merge keys. Setters only modify global (project is read-only for version control).
|
||||
Project overrides global. Nested objects merge keys. Setters modify global settings by default.
|
||||
|
||||
> See [examples/sdk/10-settings.ts](../examples/sdk/10-settings.ts)
|
||||
|
||||
## Discovery Functions
|
||||
## ResourceLoader
|
||||
|
||||
All discovery functions accept optional `cwd` and `agentDir` parameters.
|
||||
Use `DefaultResourceLoader` to discover extensions, skills, prompts, themes, and context files.
|
||||
|
||||
```typescript
|
||||
import { getModel } from "@mariozechner/pi-ai";
|
||||
import {
|
||||
AuthStorage,
|
||||
ModelRegistry,
|
||||
discoverAuthStorage,
|
||||
discoverModels,
|
||||
discoverSkills,
|
||||
discoverExtensions,
|
||||
discoverContextFiles,
|
||||
discoverPromptTemplates,
|
||||
loadSettings,
|
||||
buildSystemPrompt,
|
||||
createEventBus,
|
||||
DefaultResourceLoader,
|
||||
getAgentDir,
|
||||
} from "@mariozechner/pi-coding-agent";
|
||||
|
||||
// Auth and Models
|
||||
const authStorage = discoverAuthStorage(); // ~/.pi/agent/auth.json
|
||||
const modelRegistry = discoverModels(authStorage); // + ~/.pi/agent/models.json
|
||||
const allModels = modelRegistry.getAll(); // All models (built-in + custom)
|
||||
const available = await modelRegistry.getAvailable(); // Only models with API keys
|
||||
const model = modelRegistry.find("provider", "id"); // Find specific model
|
||||
const builtIn = getModel("anthropic", "claude-opus-4-5"); // Built-in only
|
||||
|
||||
// Skills
|
||||
const { skills, warnings } = discoverSkills(cwd, agentDir, skillsSettings);
|
||||
|
||||
// Extensions (async - loads TypeScript)
|
||||
// Pass eventBus to share pi.events across extensions
|
||||
const eventBus = createEventBus();
|
||||
const { extensions, errors } = await discoverExtensions(eventBus, cwd, agentDir);
|
||||
|
||||
// Context files
|
||||
const contextFiles = discoverContextFiles(cwd, agentDir);
|
||||
|
||||
// Prompt templates
|
||||
const templates = discoverPromptTemplates(cwd, agentDir);
|
||||
|
||||
// Settings (global + project merged)
|
||||
const settings = loadSettings(cwd, agentDir);
|
||||
|
||||
// Build system prompt manually
|
||||
const prompt = buildSystemPrompt({
|
||||
skills,
|
||||
contextFiles,
|
||||
appendPrompt: "Additional instructions",
|
||||
const loader = new DefaultResourceLoader({
|
||||
cwd,
|
||||
agentDir: getAgentDir(),
|
||||
});
|
||||
await loader.reload();
|
||||
|
||||
const extensions = loader.getExtensions();
|
||||
const skills = loader.getSkills();
|
||||
const prompts = loader.getPrompts();
|
||||
const themes = loader.getThemes();
|
||||
const contextFiles = loader.getAgentsFiles().agentsFiles;
|
||||
```
|
||||
|
||||
## Return Value
|
||||
|
|
@ -808,12 +756,12 @@ import { Type } from "@sinclair/typebox";
|
|||
import {
|
||||
AuthStorage,
|
||||
createAgentSession,
|
||||
DefaultResourceLoader,
|
||||
ModelRegistry,
|
||||
SessionManager,
|
||||
SettingsManager,
|
||||
readTool,
|
||||
bashTool,
|
||||
type ExtensionFactory,
|
||||
type ToolDefinition,
|
||||
} from "@mariozechner/pi-coding-agent";
|
||||
|
||||
|
|
@ -828,14 +776,6 @@ if (process.env.MY_KEY) {
|
|||
// Model registry (no custom models.json)
|
||||
const modelRegistry = new ModelRegistry(authStorage);
|
||||
|
||||
// Inline extension
|
||||
const auditExtension: ExtensionFactory = (pi) => {
|
||||
pi.on("tool_call", async (event) => {
|
||||
console.log(`[Audit] ${event.toolName}`);
|
||||
return undefined;
|
||||
});
|
||||
};
|
||||
|
||||
// Inline tool
|
||||
const statusTool: ToolDefinition = {
|
||||
name: "status",
|
||||
|
|
@ -857,24 +797,27 @@ const settingsManager = SettingsManager.inMemory({
|
|||
retry: { enabled: true, maxRetries: 2 },
|
||||
});
|
||||
|
||||
const loader = new DefaultResourceLoader({
|
||||
cwd: process.cwd(),
|
||||
agentDir: "/custom/agent",
|
||||
settingsManager,
|
||||
systemPromptOverride: () => "You are a minimal assistant. Be concise.",
|
||||
});
|
||||
await loader.reload();
|
||||
|
||||
const { session } = await createAgentSession({
|
||||
cwd: process.cwd(),
|
||||
agentDir: "/custom/agent",
|
||||
|
||||
|
||||
model,
|
||||
thinkingLevel: "off",
|
||||
authStorage,
|
||||
modelRegistry,
|
||||
|
||||
systemPrompt: "You are a minimal assistant. Be concise.",
|
||||
|
||||
|
||||
tools: [readTool, bashTool],
|
||||
customTools: [statusTool],
|
||||
extensions: [auditExtension],
|
||||
skills: [],
|
||||
contextFiles: [],
|
||||
promptTemplates: [],
|
||||
|
||||
resourceLoader: loader,
|
||||
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
settingsManager,
|
||||
});
|
||||
|
|
@ -976,21 +919,13 @@ createAgentSession
|
|||
// Auth and Models
|
||||
AuthStorage
|
||||
ModelRegistry
|
||||
discoverAuthStorage
|
||||
discoverModels
|
||||
|
||||
// Discovery
|
||||
discoverSkills
|
||||
discoverExtensions
|
||||
discoverContextFiles
|
||||
discoverPromptTemplates
|
||||
|
||||
// Event Bus (for shared extension communication)
|
||||
// Resource loading
|
||||
DefaultResourceLoader
|
||||
type ResourceLoader
|
||||
createEventBus
|
||||
|
||||
// Helpers
|
||||
loadSettings
|
||||
buildSystemPrompt
|
||||
|
||||
// Session management
|
||||
SessionManager
|
||||
|
|
@ -1016,8 +951,6 @@ type ExtensionAPI
|
|||
type ToolDefinition
|
||||
type Skill
|
||||
type PromptTemplate
|
||||
type Settings
|
||||
type SkillsSettings
|
||||
type Tool
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -141,64 +141,43 @@ Run the extraction script:
|
|||
|
||||
Skills are discovered from these locations (later wins on name collision):
|
||||
|
||||
1. `~/.codex/skills/**/SKILL.md` (Codex CLI, recursive)
|
||||
2. `~/.claude/skills/*/SKILL.md` (Claude Code user, one level)
|
||||
3. `<cwd>/.claude/skills/*/SKILL.md` (Claude Code project, one level)
|
||||
4. `~/.pi/agent/skills/**/SKILL.md` (Pi user, recursive)
|
||||
5. `<cwd>/.pi/skills/**/SKILL.md` (Pi project, recursive)
|
||||
1. `~/.pi/agent/skills/` (global)
|
||||
2. `<cwd>/.pi/skills/` (project)
|
||||
3. Paths listed in `settings.json` under `skills`
|
||||
4. CLI `--skill` paths (additive even with `--no-skills`)
|
||||
|
||||
Discovery rules for each directory:
|
||||
- Direct `.md` files in the root
|
||||
- Recursive `SKILL.md` files under subdirectories
|
||||
|
||||
## Configuration
|
||||
|
||||
Configure skill loading in `~/.pi/agent/settings.json`:
|
||||
Configure skill paths in `~/.pi/agent/settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"skills": {
|
||||
"enabled": true,
|
||||
"enableCodexUser": true,
|
||||
"enableClaudeUser": true,
|
||||
"enableClaudeProject": true,
|
||||
"enablePiUser": true,
|
||||
"enablePiProject": true,
|
||||
"enableSkillCommands": true,
|
||||
"customDirectories": ["~/my-skills-repo"],
|
||||
"ignoredSkills": ["deprecated-skill"],
|
||||
"includeSkills": ["git-*", "docker"]
|
||||
}
|
||||
"skills": ["~/my-skills-repo", "/path/to/skill/SKILL.md"],
|
||||
"enableSkillCommands": true
|
||||
}
|
||||
```
|
||||
|
||||
Use project-local settings in `<cwd>/.pi/settings.json` to scope skills to a single project.
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `enabled` | `true` | Master toggle for all skills |
|
||||
| `enableCodexUser` | `true` | Load from `~/.codex/skills/` |
|
||||
| `enableClaudeUser` | `true` | Load from `~/.claude/skills/` |
|
||||
| `enableClaudeProject` | `true` | Load from `<cwd>/.claude/skills/` |
|
||||
| `enablePiUser` | `true` | Load from `~/.pi/agent/skills/` |
|
||||
| `enablePiProject` | `true` | Load from `<cwd>/.pi/skills/` |
|
||||
| `skills` | `[]` | Additional skill file or directory paths |
|
||||
| `enableSkillCommands` | `true` | Register skills as `/skill:name` commands |
|
||||
| `customDirectories` | `[]` | Additional directories to scan (supports `~` expansion) |
|
||||
| `ignoredSkills` | `[]` | Glob patterns to exclude (e.g., `["deprecated-*", "test-skill"]`) |
|
||||
| `includeSkills` | `[]` | Glob patterns to include (empty = all; e.g., `["git-*", "docker"]`) |
|
||||
|
||||
**Note:** `ignoredSkills` takes precedence over both `includeSkills` in settings and the `--skills` CLI flag. A skill matching any ignore pattern will be excluded regardless of include patterns.
|
||||
### CLI Additions
|
||||
|
||||
### CLI Filtering
|
||||
|
||||
Use `--skills` to filter skills for a specific invocation:
|
||||
Use `--skill` to add skills for a specific invocation:
|
||||
|
||||
```bash
|
||||
# Only load specific skills
|
||||
pi --skills git,docker
|
||||
|
||||
# Glob patterns
|
||||
pi --skills "git-*,docker-*"
|
||||
|
||||
# All skills matching a prefix
|
||||
pi --skills "aws-*"
|
||||
pi --skill ~/my-skills/terraform
|
||||
pi --skill ./skills/custom-skill/SKILL.md
|
||||
```
|
||||
|
||||
This overrides the `includeSkills` setting for the current session.
|
||||
Use `--no-skills` to disable automatic discovery (CLI `--skill` paths still load).
|
||||
|
||||
## How Skills Work
|
||||
|
||||
|
|
@ -224,9 +203,7 @@ Toggle skill commands via `/settings` or in `settings.json`:
|
|||
|
||||
```json
|
||||
{
|
||||
"skills": {
|
||||
"enableSkillCommands": true
|
||||
}
|
||||
"enableSkillCommands": true
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -285,12 +262,6 @@ cd /path/to/brave-search && npm install
|
|||
\`\`\`
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
|
||||
**Claude Code**: Pi reads skills from `~/.claude/skills/*/SKILL.md`. The `allowed-tools` and `model` frontmatter fields are ignored.
|
||||
|
||||
**Codex CLI**: Pi reads skills from `~/.codex/skills/` recursively. Hidden files/directories and symlinks are skipped.
|
||||
|
||||
## Skill Repositories
|
||||
|
||||
For inspiration and ready-to-use skills:
|
||||
|
|
@ -305,13 +276,4 @@ CLI:
|
|||
pi --no-skills
|
||||
```
|
||||
|
||||
Settings (`~/.pi/agent/settings.json`):
|
||||
```json
|
||||
{
|
||||
"skills": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use the granular `enable*` flags to disable individual sources (e.g., `enableClaudeUser: false` to skip `~/.claude/skills`).
|
||||
Use `--no-skills` to disable automatic discovery while keeping explicit `--skill` paths.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue