@mariozechner/pi-coding-agent 0.34.1 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +224 -18
- package/README.md +233 -105
- package/dist/cli/args.d.ts +3 -4
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +13 -18
- package/dist/cli/args.js.map +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +39 -50
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +166 -197
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +3 -3
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +6 -5
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/event-bus.d.ts +9 -0
- package/dist/core/event-bus.d.ts.map +1 -0
- package/dist/core/event-bus.js +25 -0
- package/dist/core/event-bus.js.map +1 -0
- package/dist/core/exec.d.ts +1 -1
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js +1 -1
- package/dist/core/exec.js.map +1 -1
- package/dist/core/extensions/index.d.ts +10 -0
- package/dist/core/extensions/index.d.ts.map +1 -0
- package/dist/core/extensions/index.js +9 -0
- package/dist/core/extensions/index.js.map +1 -0
- package/dist/core/extensions/loader.d.ts +21 -0
- package/dist/core/extensions/loader.d.ts.map +1 -0
- package/dist/core/extensions/loader.js +400 -0
- package/dist/core/extensions/loader.js.map +1 -0
- package/dist/core/extensions/runner.d.ts +88 -0
- package/dist/core/extensions/runner.d.ts.map +1 -0
- package/dist/core/{hooks → extensions}/runner.js +52 -141
- package/dist/core/extensions/runner.js.map +1 -0
- package/dist/core/extensions/types.d.ts +461 -0
- package/dist/core/extensions/types.d.ts.map +1 -0
- package/dist/core/{hooks → extensions}/types.js +7 -4
- package/dist/core/extensions/types.js.map +1 -0
- package/dist/core/extensions/wrapper.d.ts +25 -0
- package/dist/core/extensions/wrapper.d.ts.map +1 -0
- package/dist/core/{hooks/tool-wrapper.js → extensions/wrapper.js} +39 -24
- package/dist/core/extensions/wrapper.js.map +1 -0
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/messages.d.ts +7 -7
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +4 -4
- package/dist/core/messages.js.map +1 -1
- package/dist/core/prompt-templates.d.ts +40 -0
- package/dist/core/prompt-templates.d.ts.map +1 -0
- package/dist/core/{slash-commands.js → prompt-templates.js} +31 -31
- package/dist/core/prompt-templates.js.map +1 -0
- package/dist/core/sdk.d.ts +29 -52
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +111 -211
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +17 -17
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +25 -10
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +3 -6
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +4 -11
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +4 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -6
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +36 -33
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts +7 -2
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +93 -4
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +1 -1
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +2 -2
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +4 -4
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts +18 -0
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/{hook-message.js → custom-message.js} +3 -3
- package/dist/modes/interactive/components/custom-message.js.map +1 -0
- package/dist/modes/interactive/components/dynamic-border.d.ts +2 -2
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js +2 -2
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/{hook-editor.d.ts → extension-editor.d.ts} +3 -3
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -0
- package/dist/modes/interactive/components/{hook-editor.js → extension-editor.js} +4 -4
- package/dist/modes/interactive/components/extension-editor.js.map +1 -0
- package/dist/modes/interactive/components/{hook-input.d.ts → extension-input.d.ts} +3 -3
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -0
- package/dist/modes/interactive/components/{hook-input.js → extension-input.js} +3 -3
- package/dist/modes/interactive/components/extension-input.js.map +1 -0
- package/dist/modes/interactive/components/{hook-selector.d.ts → extension-selector.d.ts} +3 -3
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/{hook-selector.js → extension-selector.js} +3 -3
- package/dist/modes/interactive/components/extension-selector.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts +3 -3
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +8 -8
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +3 -3
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +9 -9
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +37 -44
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +143 -189
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +10 -33
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +3 -3
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +3 -3
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +2 -2
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +33 -57
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +16 -16
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/extensions.md +1053 -0
- package/docs/rpc.md +4 -4
- package/docs/sdk.md +62 -93
- package/docs/session.md +22 -19
- package/docs/skills.md +1 -1
- package/docs/tui.md +1 -1
- package/examples/README.md +9 -15
- package/examples/extensions/README.md +141 -0
- package/examples/{hooks → extensions}/auto-commit-on-exit.ts +3 -3
- package/examples/extensions/chalk-logger.ts +26 -0
- package/examples/{hooks → extensions}/confirm-destructive.ts +3 -3
- package/examples/{hooks → extensions}/custom-compaction.ts +6 -6
- package/examples/{hooks → extensions}/dirty-repo-guard.ts +8 -4
- package/examples/{hooks → extensions}/file-trigger.ts +3 -3
- package/examples/{hooks → extensions}/git-checkpoint.ts +3 -3
- package/examples/{hooks → extensions}/handoff.ts +3 -3
- package/examples/extensions/hello.ts +25 -0
- package/examples/{hooks → extensions}/permission-gate.ts +3 -3
- package/examples/{hooks → extensions}/pirate.ts +5 -5
- package/examples/{hooks → extensions}/plan-mode.ts +6 -6
- package/examples/{hooks → extensions}/protected-paths.ts +3 -3
- package/examples/{hooks → extensions}/qna.ts +3 -3
- package/examples/{custom-tools/question/index.ts → extensions/question.ts} +13 -17
- package/examples/{hooks → extensions}/snake.ts +3 -3
- package/examples/{hooks → extensions}/status-line.ts +3 -3
- package/examples/{custom-tools → extensions}/subagent/README.md +15 -15
- package/examples/{custom-tools → extensions}/subagent/index.ts +22 -43
- package/examples/{custom-tools/todo/index.ts → extensions/todo.ts} +122 -39
- package/examples/{hooks → extensions}/tools.ts +5 -5
- package/examples/extensions/with-deps/index.ts +40 -0
- package/examples/extensions/with-deps/package-lock.json +31 -0
- package/examples/extensions/with-deps/package.json +16 -0
- package/examples/sdk/01-minimal.ts +1 -1
- package/examples/sdk/05-tools.ts +7 -41
- package/examples/sdk/06-extensions.ts +81 -0
- package/examples/sdk/08-prompt-templates.ts +42 -0
- package/examples/sdk/12-full-control.ts +10 -29
- package/examples/sdk/README.md +5 -5
- package/package.json +4 -4
- package/dist/core/custom-tools/index.d.ts +0 -7
- package/dist/core/custom-tools/index.d.ts.map +0 -1
- package/dist/core/custom-tools/index.js +0 -6
- package/dist/core/custom-tools/index.js.map +0 -1
- package/dist/core/custom-tools/loader.d.ts +0 -30
- package/dist/core/custom-tools/loader.d.ts.map +0 -1
- package/dist/core/custom-tools/loader.js +0 -276
- package/dist/core/custom-tools/loader.js.map +0 -1
- package/dist/core/custom-tools/types.d.ts +0 -144
- package/dist/core/custom-tools/types.d.ts.map +0 -1
- package/dist/core/custom-tools/types.js +0 -8
- package/dist/core/custom-tools/types.js.map +0 -1
- package/dist/core/custom-tools/wrapper.d.ts +0 -15
- package/dist/core/custom-tools/wrapper.d.ts.map +0 -1
- package/dist/core/custom-tools/wrapper.js +0 -23
- package/dist/core/custom-tools/wrapper.js.map +0 -1
- package/dist/core/hooks/index.d.ts +0 -6
- package/dist/core/hooks/index.d.ts.map +0 -1
- package/dist/core/hooks/index.js +0 -6
- package/dist/core/hooks/index.js.map +0 -1
- package/dist/core/hooks/loader.d.ts +0 -146
- package/dist/core/hooks/loader.d.ts.map +0 -1
- package/dist/core/hooks/loader.js +0 -275
- package/dist/core/hooks/loader.js.map +0 -1
- package/dist/core/hooks/runner.d.ts +0 -173
- package/dist/core/hooks/runner.d.ts.map +0 -1
- package/dist/core/hooks/runner.js.map +0 -1
- package/dist/core/hooks/tool-wrapper.d.ts +0 -17
- package/dist/core/hooks/tool-wrapper.d.ts.map +0 -1
- package/dist/core/hooks/tool-wrapper.js.map +0 -1
- package/dist/core/hooks/types.d.ts +0 -767
- package/dist/core/hooks/types.d.ts.map +0 -1
- package/dist/core/hooks/types.js.map +0 -1
- package/dist/core/slash-commands.d.ts +0 -40
- package/dist/core/slash-commands.d.ts.map +0 -1
- package/dist/core/slash-commands.js.map +0 -1
- package/dist/modes/interactive/components/hook-editor.d.ts.map +0 -1
- package/dist/modes/interactive/components/hook-editor.js.map +0 -1
- package/dist/modes/interactive/components/hook-input.d.ts.map +0 -1
- package/dist/modes/interactive/components/hook-input.js.map +0 -1
- package/dist/modes/interactive/components/hook-message.d.ts +0 -18
- package/dist/modes/interactive/components/hook-message.d.ts.map +0 -1
- package/dist/modes/interactive/components/hook-message.js.map +0 -1
- package/dist/modes/interactive/components/hook-selector.d.ts.map +0 -1
- package/dist/modes/interactive/components/hook-selector.js.map +0 -1
- package/docs/custom-tools.md +0 -514
- package/docs/extension-loading.md +0 -1004
- package/docs/hooks.md +0 -979
- package/docs/session-tree-plan.md +0 -441
- package/examples/custom-tools/README.md +0 -114
- package/examples/custom-tools/hello/index.ts +0 -21
- package/examples/hooks/README.md +0 -60
- package/examples/hooks/todo/index.ts +0 -134
- package/examples/sdk/06-hooks.ts +0 -61
- package/examples/sdk/08-slash-commands.ts +0 -42
- /package/examples/{custom-tools → extensions}/subagent/agents/planner.md +0 -0
- /package/examples/{custom-tools → extensions}/subagent/agents/reviewer.md +0 -0
- /package/examples/{custom-tools → extensions}/subagent/agents/scout.md +0 -0
- /package/examples/{custom-tools → extensions}/subagent/agents/worker.md +0 -0
- /package/examples/{custom-tools → extensions}/subagent/agents.ts +0 -0
- /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/implement-and-review.md +0 -0
- /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/implement.md +0 -0
- /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/scout-and-plan.md +0 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extensions Configuration
|
|
3
|
+
*
|
|
4
|
+
* Extensions intercept agent events and can register custom tools.
|
|
5
|
+
* They provide a unified system for extensions, custom tools, commands, and more.
|
|
6
|
+
*
|
|
7
|
+
* Extension files are discovered from:
|
|
8
|
+
* - ~/.pi/agent/extensions/
|
|
9
|
+
* - <cwd>/.pi/extensions/
|
|
10
|
+
* - Paths specified in settings.json "extensions" array
|
|
11
|
+
* - Paths passed via --extension CLI flag
|
|
12
|
+
*
|
|
13
|
+
* An extension is a TypeScript file that exports a default function:
|
|
14
|
+
* export default function (pi: ExtensionAPI) { ... }
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { createAgentSession, SessionManager } from "@mariozechner/pi-coding-agent";
|
|
18
|
+
|
|
19
|
+
// Extensions are loaded from disk, not passed inline to createAgentSession.
|
|
20
|
+
// Use the discovery mechanism:
|
|
21
|
+
// 1. Place extension files in ~/.pi/agent/extensions/ or .pi/extensions/
|
|
22
|
+
// 2. Add paths to settings.json: { "extensions": ["./my-extension.ts"] }
|
|
23
|
+
// 3. Use --extension flag: pi --extension ./my-extension.ts
|
|
24
|
+
|
|
25
|
+
// To add additional extension paths beyond discovery:
|
|
26
|
+
const { session } = await createAgentSession({
|
|
27
|
+
additionalExtensionPaths: ["./my-logging-extension.ts", "./my-safety-extension.ts"],
|
|
28
|
+
sessionManager: SessionManager.inMemory(),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
session.subscribe((event) => {
|
|
32
|
+
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
|
33
|
+
process.stdout.write(event.assistantMessageEvent.delta);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
await session.prompt("List files in the current directory.");
|
|
38
|
+
console.log();
|
|
39
|
+
|
|
40
|
+
// Example extension file (./my-logging-extension.ts):
|
|
41
|
+
/*
|
|
42
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
43
|
+
|
|
44
|
+
export default function (pi: ExtensionAPI) {
|
|
45
|
+
pi.on("agent_start", async () => {
|
|
46
|
+
console.log("[Extension] Agent starting");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
pi.on("tool_call", async (event) => {
|
|
50
|
+
console.log(\`[Extension] Tool: \${event.toolName}\`);
|
|
51
|
+
// Return { block: true, reason: "..." } to block execution
|
|
52
|
+
return undefined;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
pi.on("agent_end", async (event) => {
|
|
56
|
+
console.log(\`[Extension] Done, \${event.messages.length} messages\`);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Register a custom tool
|
|
60
|
+
pi.registerTool({
|
|
61
|
+
name: "my_tool",
|
|
62
|
+
label: "My Tool",
|
|
63
|
+
description: "Does something useful",
|
|
64
|
+
parameters: Type.Object({
|
|
65
|
+
input: Type.String(),
|
|
66
|
+
}),
|
|
67
|
+
execute: async (_toolCallId, params, _onUpdate, _ctx, _signal) => ({
|
|
68
|
+
content: [{ type: "text", text: \`Processed: \${params.input}\` }],
|
|
69
|
+
details: {},
|
|
70
|
+
}),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Register a command
|
|
74
|
+
pi.registerCommand("mycommand", {
|
|
75
|
+
description: "Do something",
|
|
76
|
+
handler: async (args, ctx) => {
|
|
77
|
+
ctx.ui.notify(\`Command executed with: \${args}\`);
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
*/
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt Templates
|
|
3
|
+
*
|
|
4
|
+
* File-based templates that inject content when invoked with /templatename.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
createAgentSession,
|
|
9
|
+
discoverPromptTemplates,
|
|
10
|
+
type PromptTemplate,
|
|
11
|
+
SessionManager,
|
|
12
|
+
} from "@mariozechner/pi-coding-agent";
|
|
13
|
+
|
|
14
|
+
// Discover templates from cwd/.pi/prompts/ and ~/.pi/agent/prompts/
|
|
15
|
+
const discovered = discoverPromptTemplates();
|
|
16
|
+
console.log("Discovered prompt templates:");
|
|
17
|
+
for (const template of discovered) {
|
|
18
|
+
console.log(` /${template.name}: ${template.description}`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Define custom templates
|
|
22
|
+
const deployTemplate: PromptTemplate = {
|
|
23
|
+
name: "deploy",
|
|
24
|
+
description: "Deploy the application",
|
|
25
|
+
source: "(custom)",
|
|
26
|
+
content: `# Deploy Instructions
|
|
27
|
+
|
|
28
|
+
1. Build: npm run build
|
|
29
|
+
2. Test: npm test
|
|
30
|
+
3. Deploy: npm run deploy`,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Use discovered + custom templates
|
|
34
|
+
await createAgentSession({
|
|
35
|
+
promptTemplates: [...discovered, deployTemplate],
|
|
36
|
+
sessionManager: SessionManager.inMemory(),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
console.log(`Session created with ${discovered.length + 1} prompt templates`);
|
|
40
|
+
|
|
41
|
+
// Disable prompt templates:
|
|
42
|
+
// promptTemplates: []
|
|
@@ -6,21 +6,22 @@
|
|
|
6
6
|
* IMPORTANT: When providing `tools` with a custom `cwd`, use the tool factory
|
|
7
7
|
* functions (createReadTool, createBashTool, etc.) to ensure tools resolve
|
|
8
8
|
* paths relative to your cwd.
|
|
9
|
+
*
|
|
10
|
+
* NOTE: Extensions (extensions, custom tools) are always loaded via discovery.
|
|
11
|
+
* To use custom extensions, place them in the extensions directory or
|
|
12
|
+
* pass paths via additionalExtensionPaths.
|
|
9
13
|
*/
|
|
10
14
|
|
|
11
15
|
import { getModel } from "@mariozechner/pi-ai";
|
|
12
16
|
import {
|
|
13
17
|
AuthStorage,
|
|
14
|
-
type CustomTool,
|
|
15
18
|
createAgentSession,
|
|
16
19
|
createBashTool,
|
|
17
20
|
createReadTool,
|
|
18
|
-
type HookFactory,
|
|
19
21
|
ModelRegistry,
|
|
20
22
|
SessionManager,
|
|
21
23
|
SettingsManager,
|
|
22
24
|
} from "@mariozechner/pi-coding-agent";
|
|
23
|
-
import { Type } from "@sinclair/typebox";
|
|
24
25
|
|
|
25
26
|
// Custom auth storage location
|
|
26
27
|
const authStorage = new AuthStorage("/tmp/my-agent/auth.json");
|
|
@@ -33,27 +34,7 @@ if (process.env.MY_ANTHROPIC_KEY) {
|
|
|
33
34
|
// Model registry with no custom models.json
|
|
34
35
|
const modelRegistry = new ModelRegistry(authStorage);
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
const auditHook: HookFactory = (api) => {
|
|
38
|
-
api.on("tool_call", async (event) => {
|
|
39
|
-
console.log(`[Audit] ${event.toolName}`);
|
|
40
|
-
return undefined;
|
|
41
|
-
});
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// Inline custom tool
|
|
45
|
-
const statusTool: CustomTool = {
|
|
46
|
-
name: "status",
|
|
47
|
-
label: "Status",
|
|
48
|
-
description: "Get system status",
|
|
49
|
-
parameters: Type.Object({}),
|
|
50
|
-
execute: async () => ({
|
|
51
|
-
content: [{ type: "text", text: `Uptime: ${process.uptime()}s, Node: ${process.version}` }],
|
|
52
|
-
details: {},
|
|
53
|
-
}),
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const model = getModel("anthropic", "claude-opus-4-5");
|
|
37
|
+
const model = getModel("anthropic", "claude-sonnet-4-20250514");
|
|
57
38
|
if (!model) throw new Error("Model not found");
|
|
58
39
|
|
|
59
40
|
// In-memory settings with overrides
|
|
@@ -73,14 +54,14 @@ const { session } = await createAgentSession({
|
|
|
73
54
|
authStorage,
|
|
74
55
|
modelRegistry,
|
|
75
56
|
systemPrompt: `You are a minimal assistant.
|
|
76
|
-
Available: read, bash
|
|
57
|
+
Available: read, bash. Be concise.`,
|
|
77
58
|
// Use factory functions with the same cwd to ensure path resolution works correctly
|
|
78
59
|
tools: [createReadTool(cwd), createBashTool(cwd)],
|
|
79
|
-
|
|
80
|
-
|
|
60
|
+
// Extensions are loaded from disk - use additionalExtensionPaths to add custom ones
|
|
61
|
+
// additionalExtensionPaths: ["./my-extension.ts"],
|
|
81
62
|
skills: [],
|
|
82
63
|
contextFiles: [],
|
|
83
|
-
|
|
64
|
+
promptTemplates: [],
|
|
84
65
|
sessionManager: SessionManager.inMemory(),
|
|
85
66
|
settingsManager,
|
|
86
67
|
});
|
|
@@ -91,5 +72,5 @@ session.subscribe((event) => {
|
|
|
91
72
|
}
|
|
92
73
|
});
|
|
93
74
|
|
|
94
|
-
await session.prompt("
|
|
75
|
+
await session.prompt("List files in the current directory.");
|
|
95
76
|
console.log();
|
package/examples/sdk/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Programmatic usage of pi-coding-agent via `createAgentSession()`.
|
|
|
11
11
|
| `03-custom-prompt.ts` | Replace or modify system prompt |
|
|
12
12
|
| `04-skills.ts` | Discover, filter, or replace skills |
|
|
13
13
|
| `05-tools.ts` | Built-in tools, custom tools |
|
|
14
|
-
| `06-
|
|
14
|
+
| `06-extensions.ts` | Logging, blocking, result modification |
|
|
15
15
|
| `07-context-files.ts` | AGENTS.md context files |
|
|
16
16
|
| `08-slash-commands.ts` | File-based slash commands |
|
|
17
17
|
| `09-api-keys-and-oauth.ts` | API key resolution, OAuth config |
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
discoverAuthStorage,
|
|
37
37
|
discoverModels,
|
|
38
38
|
discoverSkills,
|
|
39
|
-
|
|
39
|
+
discoverExtensions,
|
|
40
40
|
discoverCustomTools,
|
|
41
41
|
discoverContextFiles,
|
|
42
42
|
discoverSlashCommands,
|
|
@@ -89,7 +89,7 @@ const { session } = await createAgentSession({
|
|
|
89
89
|
systemPrompt: "You are helpful.",
|
|
90
90
|
tools: [readTool, bashTool],
|
|
91
91
|
customTools: [{ tool: myTool }],
|
|
92
|
-
|
|
92
|
+
extensions: [{ factory: myExtension }],
|
|
93
93
|
skills: [],
|
|
94
94
|
contextFiles: [],
|
|
95
95
|
slashCommands: [],
|
|
@@ -119,8 +119,8 @@ await session.prompt("Hello");
|
|
|
119
119
|
| `tools` | `codingTools` | Built-in tools |
|
|
120
120
|
| `customTools` | Discovered | Replaces discovery |
|
|
121
121
|
| `additionalCustomToolPaths` | `[]` | Merge with discovery |
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
122
|
+
| `extensions` | Discovered | Replaces discovery |
|
|
123
|
+
| `additionalExtensionPaths` | `[]` | Merge with discovery |
|
|
124
124
|
| `skills` | Discovered | Skills for prompt |
|
|
125
125
|
| `contextFiles` | Discovered | AGENTS.md files |
|
|
126
126
|
| `slashCommands` | Discovered | File commands |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mariozechner/pi-coding-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@crosscopy/clipboard": "^0.2.8",
|
|
42
|
-
"@mariozechner/pi-agent-core": "^0.
|
|
43
|
-
"@mariozechner/pi-ai": "^0.
|
|
44
|
-
"@mariozechner/pi-tui": "^0.
|
|
42
|
+
"@mariozechner/pi-agent-core": "^0.35.0",
|
|
43
|
+
"@mariozechner/pi-ai": "^0.35.0",
|
|
44
|
+
"@mariozechner/pi-tui": "^0.35.0",
|
|
45
45
|
"chalk": "^5.5.0",
|
|
46
46
|
"cli-highlight": "^2.1.11",
|
|
47
47
|
"diff": "^8.0.2",
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Custom tools module.
|
|
3
|
-
*/
|
|
4
|
-
export { discoverAndLoadCustomTools, loadCustomTools } from "./loader.js";
|
|
5
|
-
export type { AgentToolResult, AgentToolUpdateCallback, CustomTool, CustomToolAPI, CustomToolContext, CustomToolFactory, CustomToolResult, CustomToolSessionEvent, CustomToolsLoadResult, CustomToolUIContext, ExecResult, LoadedCustomTool, RenderResultOptions, } from "./types.js";
|
|
6
|
-
export { wrapCustomTool, wrapCustomTools } from "./wrapper.js";
|
|
7
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/custom-tools/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC1E,YAAY,EACX,eAAe,EACf,uBAAuB,EACvB,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,sBAAsB,EACtB,qBAAqB,EACrB,mBAAmB,EACnB,UAAU,EACV,gBAAgB,EAChB,mBAAmB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC","sourcesContent":["/**\n * Custom tools module.\n */\n\nexport { discoverAndLoadCustomTools, loadCustomTools } from \"./loader.js\";\nexport type {\n\tAgentToolResult,\n\tAgentToolUpdateCallback,\n\tCustomTool,\n\tCustomToolAPI,\n\tCustomToolContext,\n\tCustomToolFactory,\n\tCustomToolResult,\n\tCustomToolSessionEvent,\n\tCustomToolsLoadResult,\n\tCustomToolUIContext,\n\tExecResult,\n\tLoadedCustomTool,\n\tRenderResultOptions,\n} from \"./types.js\";\nexport { wrapCustomTool, wrapCustomTools } from \"./wrapper.js\";\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/custom-tools/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAgB1E,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC","sourcesContent":["/**\n * Custom tools module.\n */\n\nexport { discoverAndLoadCustomTools, loadCustomTools } from \"./loader.js\";\nexport type {\n\tAgentToolResult,\n\tAgentToolUpdateCallback,\n\tCustomTool,\n\tCustomToolAPI,\n\tCustomToolContext,\n\tCustomToolFactory,\n\tCustomToolResult,\n\tCustomToolSessionEvent,\n\tCustomToolsLoadResult,\n\tCustomToolUIContext,\n\tExecResult,\n\tLoadedCustomTool,\n\tRenderResultOptions,\n} from \"./types.js\";\nexport { wrapCustomTool, wrapCustomTools } from \"./wrapper.js\";\n"]}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Custom tool loader - loads TypeScript tool modules using jiti.
|
|
3
|
-
*
|
|
4
|
-
* For Bun compiled binaries, custom tools that import from @mariozechner/* packages
|
|
5
|
-
* are not supported because Bun's plugin system doesn't intercept imports from
|
|
6
|
-
* external files loaded at runtime. Users should use the npm-installed version
|
|
7
|
-
* for custom tools that depend on pi packages.
|
|
8
|
-
*/
|
|
9
|
-
import type { CustomToolsLoadResult } from "./types.js";
|
|
10
|
-
/**
|
|
11
|
-
* Load all tools from configuration.
|
|
12
|
-
* @param paths - Array of tool file paths
|
|
13
|
-
* @param cwd - Current working directory for resolving relative paths
|
|
14
|
-
* @param builtInToolNames - Names of built-in tools to check for conflicts
|
|
15
|
-
*/
|
|
16
|
-
export declare function loadCustomTools(paths: string[], cwd: string, builtInToolNames: string[]): Promise<CustomToolsLoadResult>;
|
|
17
|
-
/**
|
|
18
|
-
* Discover and load tools from standard locations:
|
|
19
|
-
* 1. agentDir/tools/*.ts (global)
|
|
20
|
-
* 2. cwd/.pi/tools/*.ts (project-local)
|
|
21
|
-
*
|
|
22
|
-
* Plus any explicitly configured paths from settings or CLI.
|
|
23
|
-
*
|
|
24
|
-
* @param configuredPaths - Explicit paths from settings.json and CLI --tool flags
|
|
25
|
-
* @param cwd - Current working directory
|
|
26
|
-
* @param builtInToolNames - Names of built-in tools to check for conflicts
|
|
27
|
-
* @param agentDir - Agent config directory. Default: from getAgentDir()
|
|
28
|
-
*/
|
|
29
|
-
export declare function discoverAndLoadCustomTools(configuredPaths: string[], cwd: string, builtInToolNames: string[], agentDir?: string): Promise<CustomToolsLoadResult>;
|
|
30
|
-
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/core/custom-tools/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAaH,OAAO,KAAK,EAAoC,qBAAqB,EAAoB,MAAM,YAAY,CAAC;AAyL5G;;;;;GAKG;AACH,wBAAsB,eAAe,CACpC,KAAK,EAAE,MAAM,EAAE,EACf,GAAG,EAAE,MAAM,EACX,gBAAgB,EAAE,MAAM,EAAE,GACxB,OAAO,CAAC,qBAAqB,CAAC,CA+ChC;AAgCD;;;;;;;;;;;GAWG;AACH,wBAAsB,0BAA0B,CAC/C,eAAe,EAAE,MAAM,EAAE,EACzB,GAAG,EAAE,MAAM,EACX,gBAAgB,EAAE,MAAM,EAAE,EAC1B,QAAQ,GAAE,MAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CA2BhC","sourcesContent":["/**\n * Custom tool loader - loads TypeScript tool modules using jiti.\n *\n * For Bun compiled binaries, custom tools that import from @mariozechner/* packages\n * are not supported because Bun's plugin system doesn't intercept imports from\n * external files loaded at runtime. Users should use the npm-installed version\n * for custom tools that depend on pi packages.\n */\n\nimport * as fs from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createJiti } from \"jiti\";\nimport { getAgentDir, isBunBinary } from \"../../config.js\";\nimport { theme } from \"../../modes/interactive/theme/theme.js\";\nimport type { ExecOptions } from \"../exec.js\";\nimport { execCommand } from \"../exec.js\";\nimport type { HookUIContext } from \"../hooks/types.js\";\nimport type { CustomToolAPI, CustomToolFactory, CustomToolsLoadResult, LoadedCustomTool } from \"./types.js\";\n\n// Create require function to resolve module paths at runtime\nconst require = createRequire(import.meta.url);\n\n// Lazily computed aliases - resolved at runtime to handle global installs\nlet _aliases: Record<string, string> | null = null;\nfunction getAliases(): Record<string, string> {\n\tif (_aliases) return _aliases;\n\n\tconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\tconst packageIndex = path.resolve(__dirname, \"../..\", \"index.js\");\n\n\t// For typebox, we need the package root directory (not the entry file)\n\t// because jiti's alias is prefix-based: imports like \"@sinclair/typebox/compiler\"\n\t// get the alias prepended. If we alias to the entry file (.../build/cjs/index.js),\n\t// then \"@sinclair/typebox/compiler\" becomes \".../build/cjs/index.js/compiler\" (invalid).\n\t// By aliasing to the package root, it becomes \".../typebox/compiler\" which resolves correctly.\n\tconst typeboxEntry = require.resolve(\"@sinclair/typebox\");\n\tconst typeboxRoot = typeboxEntry.replace(/\\/build\\/cjs\\/index\\.js$/, \"\");\n\n\t_aliases = {\n\t\t\"@mariozechner/pi-coding-agent\": packageIndex,\n\t\t\"@mariozechner/pi-tui\": require.resolve(\"@mariozechner/pi-tui\"),\n\t\t\"@mariozechner/pi-ai\": require.resolve(\"@mariozechner/pi-ai\"),\n\t\t\"@sinclair/typebox\": typeboxRoot,\n\t};\n\treturn _aliases;\n}\n\nconst UNICODE_SPACES = /[\\u00A0\\u2000-\\u200A\\u202F\\u205F\\u3000]/g;\n\nfunction normalizeUnicodeSpaces(str: string): string {\n\treturn str.replace(UNICODE_SPACES, \" \");\n}\n\nfunction expandPath(p: string): string {\n\tconst normalized = normalizeUnicodeSpaces(p);\n\tif (normalized.startsWith(\"~/\")) {\n\t\treturn path.join(os.homedir(), normalized.slice(2));\n\t}\n\tif (normalized.startsWith(\"~\")) {\n\t\treturn path.join(os.homedir(), normalized.slice(1));\n\t}\n\treturn normalized;\n}\n\n/**\n * Resolve tool path.\n * - Absolute paths used as-is\n * - Paths starting with ~ expanded to home directory\n * - Relative paths resolved from cwd\n */\nfunction resolveToolPath(toolPath: string, cwd: string): string {\n\tconst expanded = expandPath(toolPath);\n\n\tif (path.isAbsolute(expanded)) {\n\t\treturn expanded;\n\t}\n\n\t// Relative paths resolved from cwd\n\treturn path.resolve(cwd, expanded);\n}\n\n/**\n * Create a no-op UI context for headless modes.\n */\nfunction createNoOpUIContext(): HookUIContext {\n\treturn {\n\t\tselect: async () => undefined,\n\t\tconfirm: async () => false,\n\t\tinput: async () => undefined,\n\t\tnotify: () => {},\n\t\tsetStatus: () => {},\n\t\tsetWidget: () => {},\n\t\tsetTitle: () => {},\n\t\tcustom: async () => undefined as never,\n\t\tsetEditorText: () => {},\n\t\tgetEditorText: () => \"\",\n\t\teditor: async () => undefined,\n\t\tget theme() {\n\t\t\treturn theme;\n\t\t},\n\t};\n}\n\n/**\n * Load a tool in Bun binary mode.\n *\n * Since Bun plugins don't work for dynamically loaded external files,\n * custom tools that import from @mariozechner/* packages won't work.\n * Tools that only use standard npm packages (installed in the tool's directory)\n * may still work.\n */\nasync function loadToolWithBun(\n\tresolvedPath: string,\n\tsharedApi: CustomToolAPI,\n): Promise<{ tools: LoadedCustomTool[] | null; error: string | null }> {\n\ttry {\n\t\t// Try to import directly - will work for tools without @mariozechner/* imports\n\t\tconst module = await import(resolvedPath);\n\t\tconst factory = (module.default ?? module) as CustomToolFactory;\n\n\t\tif (typeof factory !== \"function\") {\n\t\t\treturn { tools: null, error: \"Tool must export a default function\" };\n\t\t}\n\n\t\tconst toolResult = await factory(sharedApi);\n\t\tconst toolsArray = Array.isArray(toolResult) ? toolResult : [toolResult];\n\n\t\tconst loadedTools: LoadedCustomTool[] = toolsArray.map((tool) => ({\n\t\t\tpath: resolvedPath,\n\t\t\tresolvedPath,\n\t\t\ttool,\n\t\t}));\n\n\t\treturn { tools: loadedTools, error: null };\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\n\t\t// Check if it's a module resolution error for our packages\n\t\tif (message.includes(\"Cannot find module\") && message.includes(\"@mariozechner/\")) {\n\t\t\treturn {\n\t\t\t\ttools: null,\n\t\t\t\terror:\n\t\t\t\t\t`${message}\\n` +\n\t\t\t\t\t\"Note: Custom tools importing from @mariozechner/* packages are not supported in the standalone binary.\\n\" +\n\t\t\t\t\t\"Please install pi via npm: npm install -g @mariozechner/pi-coding-agent\",\n\t\t\t};\n\t\t}\n\n\t\treturn { tools: null, error: `Failed to load tool: ${message}` };\n\t}\n}\n\n/**\n * Load a single tool module using jiti (or Bun.build for compiled binaries).\n */\nasync function loadTool(\n\ttoolPath: string,\n\tcwd: string,\n\tsharedApi: CustomToolAPI,\n): Promise<{ tools: LoadedCustomTool[] | null; error: string | null }> {\n\tconst resolvedPath = resolveToolPath(toolPath, cwd);\n\n\t// Use Bun.build for compiled binaries since jiti can't resolve bundled modules\n\tif (isBunBinary) {\n\t\treturn loadToolWithBun(resolvedPath, sharedApi);\n\t}\n\n\ttry {\n\t\t// Create jiti instance for TypeScript/ESM loading\n\t\t// Use aliases to resolve package imports since tools are loaded from user directories\n\t\t// (e.g. ~/.pi/agent/tools) but import from packages installed with pi-coding-agent\n\t\tconst jiti = createJiti(import.meta.url, {\n\t\t\talias: getAliases(),\n\t\t});\n\n\t\t// Import the module\n\t\tconst module = await jiti.import(resolvedPath, { default: true });\n\t\tconst factory = module as CustomToolFactory;\n\n\t\tif (typeof factory !== \"function\") {\n\t\t\treturn { tools: null, error: \"Tool must export a default function\" };\n\t\t}\n\n\t\t// Call factory with shared API\n\t\tconst result = await factory(sharedApi);\n\n\t\t// Handle single tool or array of tools\n\t\tconst toolsArray = Array.isArray(result) ? result : [result];\n\n\t\tconst loadedTools: LoadedCustomTool[] = toolsArray.map((tool) => ({\n\t\t\tpath: toolPath,\n\t\t\tresolvedPath,\n\t\t\ttool,\n\t\t}));\n\n\t\treturn { tools: loadedTools, error: null };\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn { tools: null, error: `Failed to load tool: ${message}` };\n\t}\n}\n\n/**\n * Load all tools from configuration.\n * @param paths - Array of tool file paths\n * @param cwd - Current working directory for resolving relative paths\n * @param builtInToolNames - Names of built-in tools to check for conflicts\n */\nexport async function loadCustomTools(\n\tpaths: string[],\n\tcwd: string,\n\tbuiltInToolNames: string[],\n): Promise<CustomToolsLoadResult> {\n\tconst tools: LoadedCustomTool[] = [];\n\tconst errors: Array<{ path: string; error: string }> = [];\n\tconst seenNames = new Set<string>(builtInToolNames);\n\n\t// Shared API object - all tools get the same instance\n\tconst sharedApi: CustomToolAPI = {\n\t\tcwd,\n\t\texec: (command: string, args: string[], options?: ExecOptions) =>\n\t\t\texecCommand(command, args, options?.cwd ?? cwd, options),\n\t\tui: createNoOpUIContext(),\n\t\thasUI: false,\n\t};\n\n\tfor (const toolPath of paths) {\n\t\tconst { tools: loadedTools, error } = await loadTool(toolPath, cwd, sharedApi);\n\n\t\tif (error) {\n\t\t\terrors.push({ path: toolPath, error });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (loadedTools) {\n\t\t\tfor (const loadedTool of loadedTools) {\n\t\t\t\t// Check for name conflicts\n\t\t\t\tif (seenNames.has(loadedTool.tool.name)) {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\tpath: toolPath,\n\t\t\t\t\t\terror: `Tool name \"${loadedTool.tool.name}\" conflicts with existing tool`,\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tseenNames.add(loadedTool.tool.name);\n\t\t\t\ttools.push(loadedTool);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\ttools,\n\t\terrors,\n\t\tsetUIContext(uiContext, hasUI) {\n\t\t\tsharedApi.ui = uiContext;\n\t\t\tsharedApi.hasUI = hasUI;\n\t\t},\n\t};\n}\n\n/**\n * Discover tool files from a directory.\n * Only loads index.ts files from subdirectories (e.g., tools/mytool/index.ts).\n */\nfunction discoverToolsInDir(dir: string): string[] {\n\tif (!fs.existsSync(dir)) {\n\t\treturn [];\n\t}\n\n\tconst tools: string[] = [];\n\n\ttry {\n\t\tconst entries = fs.readdirSync(dir, { withFileTypes: true });\n\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.isDirectory() || entry.isSymbolicLink()) {\n\t\t\t\t// Check for index.ts in subdirectory\n\t\t\t\tconst indexPath = path.join(dir, entry.name, \"index.ts\");\n\t\t\t\tif (fs.existsSync(indexPath)) {\n\t\t\t\t\ttools.push(indexPath);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\treturn [];\n\t}\n\n\treturn tools;\n}\n\n/**\n * Discover and load tools from standard locations:\n * 1. agentDir/tools/*.ts (global)\n * 2. cwd/.pi/tools/*.ts (project-local)\n *\n * Plus any explicitly configured paths from settings or CLI.\n *\n * @param configuredPaths - Explicit paths from settings.json and CLI --tool flags\n * @param cwd - Current working directory\n * @param builtInToolNames - Names of built-in tools to check for conflicts\n * @param agentDir - Agent config directory. Default: from getAgentDir()\n */\nexport async function discoverAndLoadCustomTools(\n\tconfiguredPaths: string[],\n\tcwd: string,\n\tbuiltInToolNames: string[],\n\tagentDir: string = getAgentDir(),\n): Promise<CustomToolsLoadResult> {\n\tconst allPaths: string[] = [];\n\tconst seen = new Set<string>();\n\n\t// Helper to add paths without duplicates\n\tconst addPaths = (paths: string[]) => {\n\t\tfor (const p of paths) {\n\t\t\tconst resolved = path.resolve(p);\n\t\t\tif (!seen.has(resolved)) {\n\t\t\t\tseen.add(resolved);\n\t\t\t\tallPaths.push(p);\n\t\t\t}\n\t\t}\n\t};\n\n\t// 1. Global tools: agentDir/tools/\n\tconst globalToolsDir = path.join(agentDir, \"tools\");\n\taddPaths(discoverToolsInDir(globalToolsDir));\n\n\t// 2. Project-local tools: cwd/.pi/tools/\n\tconst localToolsDir = path.join(cwd, \".pi\", \"tools\");\n\taddPaths(discoverToolsInDir(localToolsDir));\n\n\t// 3. Explicitly configured paths (can override/add)\n\taddPaths(configuredPaths.map((p) => resolveToolPath(p, cwd)));\n\n\treturn loadCustomTools(allPaths, cwd, builtInToolNames);\n}\n"]}
|
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Custom tool loader - loads TypeScript tool modules using jiti.
|
|
3
|
-
*
|
|
4
|
-
* For Bun compiled binaries, custom tools that import from @mariozechner/* packages
|
|
5
|
-
* are not supported because Bun's plugin system doesn't intercept imports from
|
|
6
|
-
* external files loaded at runtime. Users should use the npm-installed version
|
|
7
|
-
* for custom tools that depend on pi packages.
|
|
8
|
-
*/
|
|
9
|
-
import * as fs from "node:fs";
|
|
10
|
-
import { createRequire } from "node:module";
|
|
11
|
-
import * as os from "node:os";
|
|
12
|
-
import * as path from "node:path";
|
|
13
|
-
import { fileURLToPath } from "node:url";
|
|
14
|
-
import { createJiti } from "jiti";
|
|
15
|
-
import { getAgentDir, isBunBinary } from "../../config.js";
|
|
16
|
-
import { theme } from "../../modes/interactive/theme/theme.js";
|
|
17
|
-
import { execCommand } from "../exec.js";
|
|
18
|
-
// Create require function to resolve module paths at runtime
|
|
19
|
-
const require = createRequire(import.meta.url);
|
|
20
|
-
// Lazily computed aliases - resolved at runtime to handle global installs
|
|
21
|
-
let _aliases = null;
|
|
22
|
-
function getAliases() {
|
|
23
|
-
if (_aliases)
|
|
24
|
-
return _aliases;
|
|
25
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
26
|
-
const packageIndex = path.resolve(__dirname, "../..", "index.js");
|
|
27
|
-
// For typebox, we need the package root directory (not the entry file)
|
|
28
|
-
// because jiti's alias is prefix-based: imports like "@sinclair/typebox/compiler"
|
|
29
|
-
// get the alias prepended. If we alias to the entry file (.../build/cjs/index.js),
|
|
30
|
-
// then "@sinclair/typebox/compiler" becomes ".../build/cjs/index.js/compiler" (invalid).
|
|
31
|
-
// By aliasing to the package root, it becomes ".../typebox/compiler" which resolves correctly.
|
|
32
|
-
const typeboxEntry = require.resolve("@sinclair/typebox");
|
|
33
|
-
const typeboxRoot = typeboxEntry.replace(/\/build\/cjs\/index\.js$/, "");
|
|
34
|
-
_aliases = {
|
|
35
|
-
"@mariozechner/pi-coding-agent": packageIndex,
|
|
36
|
-
"@mariozechner/pi-tui": require.resolve("@mariozechner/pi-tui"),
|
|
37
|
-
"@mariozechner/pi-ai": require.resolve("@mariozechner/pi-ai"),
|
|
38
|
-
"@sinclair/typebox": typeboxRoot,
|
|
39
|
-
};
|
|
40
|
-
return _aliases;
|
|
41
|
-
}
|
|
42
|
-
const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
|
|
43
|
-
function normalizeUnicodeSpaces(str) {
|
|
44
|
-
return str.replace(UNICODE_SPACES, " ");
|
|
45
|
-
}
|
|
46
|
-
function expandPath(p) {
|
|
47
|
-
const normalized = normalizeUnicodeSpaces(p);
|
|
48
|
-
if (normalized.startsWith("~/")) {
|
|
49
|
-
return path.join(os.homedir(), normalized.slice(2));
|
|
50
|
-
}
|
|
51
|
-
if (normalized.startsWith("~")) {
|
|
52
|
-
return path.join(os.homedir(), normalized.slice(1));
|
|
53
|
-
}
|
|
54
|
-
return normalized;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Resolve tool path.
|
|
58
|
-
* - Absolute paths used as-is
|
|
59
|
-
* - Paths starting with ~ expanded to home directory
|
|
60
|
-
* - Relative paths resolved from cwd
|
|
61
|
-
*/
|
|
62
|
-
function resolveToolPath(toolPath, cwd) {
|
|
63
|
-
const expanded = expandPath(toolPath);
|
|
64
|
-
if (path.isAbsolute(expanded)) {
|
|
65
|
-
return expanded;
|
|
66
|
-
}
|
|
67
|
-
// Relative paths resolved from cwd
|
|
68
|
-
return path.resolve(cwd, expanded);
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Create a no-op UI context for headless modes.
|
|
72
|
-
*/
|
|
73
|
-
function createNoOpUIContext() {
|
|
74
|
-
return {
|
|
75
|
-
select: async () => undefined,
|
|
76
|
-
confirm: async () => false,
|
|
77
|
-
input: async () => undefined,
|
|
78
|
-
notify: () => { },
|
|
79
|
-
setStatus: () => { },
|
|
80
|
-
setWidget: () => { },
|
|
81
|
-
setTitle: () => { },
|
|
82
|
-
custom: async () => undefined,
|
|
83
|
-
setEditorText: () => { },
|
|
84
|
-
getEditorText: () => "",
|
|
85
|
-
editor: async () => undefined,
|
|
86
|
-
get theme() {
|
|
87
|
-
return theme;
|
|
88
|
-
},
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Load a tool in Bun binary mode.
|
|
93
|
-
*
|
|
94
|
-
* Since Bun plugins don't work for dynamically loaded external files,
|
|
95
|
-
* custom tools that import from @mariozechner/* packages won't work.
|
|
96
|
-
* Tools that only use standard npm packages (installed in the tool's directory)
|
|
97
|
-
* may still work.
|
|
98
|
-
*/
|
|
99
|
-
async function loadToolWithBun(resolvedPath, sharedApi) {
|
|
100
|
-
try {
|
|
101
|
-
// Try to import directly - will work for tools without @mariozechner/* imports
|
|
102
|
-
const module = await import(resolvedPath);
|
|
103
|
-
const factory = (module.default ?? module);
|
|
104
|
-
if (typeof factory !== "function") {
|
|
105
|
-
return { tools: null, error: "Tool must export a default function" };
|
|
106
|
-
}
|
|
107
|
-
const toolResult = await factory(sharedApi);
|
|
108
|
-
const toolsArray = Array.isArray(toolResult) ? toolResult : [toolResult];
|
|
109
|
-
const loadedTools = toolsArray.map((tool) => ({
|
|
110
|
-
path: resolvedPath,
|
|
111
|
-
resolvedPath,
|
|
112
|
-
tool,
|
|
113
|
-
}));
|
|
114
|
-
return { tools: loadedTools, error: null };
|
|
115
|
-
}
|
|
116
|
-
catch (err) {
|
|
117
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
118
|
-
// Check if it's a module resolution error for our packages
|
|
119
|
-
if (message.includes("Cannot find module") && message.includes("@mariozechner/")) {
|
|
120
|
-
return {
|
|
121
|
-
tools: null,
|
|
122
|
-
error: `${message}\n` +
|
|
123
|
-
"Note: Custom tools importing from @mariozechner/* packages are not supported in the standalone binary.\n" +
|
|
124
|
-
"Please install pi via npm: npm install -g @mariozechner/pi-coding-agent",
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
return { tools: null, error: `Failed to load tool: ${message}` };
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Load a single tool module using jiti (or Bun.build for compiled binaries).
|
|
132
|
-
*/
|
|
133
|
-
async function loadTool(toolPath, cwd, sharedApi) {
|
|
134
|
-
const resolvedPath = resolveToolPath(toolPath, cwd);
|
|
135
|
-
// Use Bun.build for compiled binaries since jiti can't resolve bundled modules
|
|
136
|
-
if (isBunBinary) {
|
|
137
|
-
return loadToolWithBun(resolvedPath, sharedApi);
|
|
138
|
-
}
|
|
139
|
-
try {
|
|
140
|
-
// Create jiti instance for TypeScript/ESM loading
|
|
141
|
-
// Use aliases to resolve package imports since tools are loaded from user directories
|
|
142
|
-
// (e.g. ~/.pi/agent/tools) but import from packages installed with pi-coding-agent
|
|
143
|
-
const jiti = createJiti(import.meta.url, {
|
|
144
|
-
alias: getAliases(),
|
|
145
|
-
});
|
|
146
|
-
// Import the module
|
|
147
|
-
const module = await jiti.import(resolvedPath, { default: true });
|
|
148
|
-
const factory = module;
|
|
149
|
-
if (typeof factory !== "function") {
|
|
150
|
-
return { tools: null, error: "Tool must export a default function" };
|
|
151
|
-
}
|
|
152
|
-
// Call factory with shared API
|
|
153
|
-
const result = await factory(sharedApi);
|
|
154
|
-
// Handle single tool or array of tools
|
|
155
|
-
const toolsArray = Array.isArray(result) ? result : [result];
|
|
156
|
-
const loadedTools = toolsArray.map((tool) => ({
|
|
157
|
-
path: toolPath,
|
|
158
|
-
resolvedPath,
|
|
159
|
-
tool,
|
|
160
|
-
}));
|
|
161
|
-
return { tools: loadedTools, error: null };
|
|
162
|
-
}
|
|
163
|
-
catch (err) {
|
|
164
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
165
|
-
return { tools: null, error: `Failed to load tool: ${message}` };
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Load all tools from configuration.
|
|
170
|
-
* @param paths - Array of tool file paths
|
|
171
|
-
* @param cwd - Current working directory for resolving relative paths
|
|
172
|
-
* @param builtInToolNames - Names of built-in tools to check for conflicts
|
|
173
|
-
*/
|
|
174
|
-
export async function loadCustomTools(paths, cwd, builtInToolNames) {
|
|
175
|
-
const tools = [];
|
|
176
|
-
const errors = [];
|
|
177
|
-
const seenNames = new Set(builtInToolNames);
|
|
178
|
-
// Shared API object - all tools get the same instance
|
|
179
|
-
const sharedApi = {
|
|
180
|
-
cwd,
|
|
181
|
-
exec: (command, args, options) => execCommand(command, args, options?.cwd ?? cwd, options),
|
|
182
|
-
ui: createNoOpUIContext(),
|
|
183
|
-
hasUI: false,
|
|
184
|
-
};
|
|
185
|
-
for (const toolPath of paths) {
|
|
186
|
-
const { tools: loadedTools, error } = await loadTool(toolPath, cwd, sharedApi);
|
|
187
|
-
if (error) {
|
|
188
|
-
errors.push({ path: toolPath, error });
|
|
189
|
-
continue;
|
|
190
|
-
}
|
|
191
|
-
if (loadedTools) {
|
|
192
|
-
for (const loadedTool of loadedTools) {
|
|
193
|
-
// Check for name conflicts
|
|
194
|
-
if (seenNames.has(loadedTool.tool.name)) {
|
|
195
|
-
errors.push({
|
|
196
|
-
path: toolPath,
|
|
197
|
-
error: `Tool name "${loadedTool.tool.name}" conflicts with existing tool`,
|
|
198
|
-
});
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
seenNames.add(loadedTool.tool.name);
|
|
202
|
-
tools.push(loadedTool);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
return {
|
|
207
|
-
tools,
|
|
208
|
-
errors,
|
|
209
|
-
setUIContext(uiContext, hasUI) {
|
|
210
|
-
sharedApi.ui = uiContext;
|
|
211
|
-
sharedApi.hasUI = hasUI;
|
|
212
|
-
},
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Discover tool files from a directory.
|
|
217
|
-
* Only loads index.ts files from subdirectories (e.g., tools/mytool/index.ts).
|
|
218
|
-
*/
|
|
219
|
-
function discoverToolsInDir(dir) {
|
|
220
|
-
if (!fs.existsSync(dir)) {
|
|
221
|
-
return [];
|
|
222
|
-
}
|
|
223
|
-
const tools = [];
|
|
224
|
-
try {
|
|
225
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
226
|
-
for (const entry of entries) {
|
|
227
|
-
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
228
|
-
// Check for index.ts in subdirectory
|
|
229
|
-
const indexPath = path.join(dir, entry.name, "index.ts");
|
|
230
|
-
if (fs.existsSync(indexPath)) {
|
|
231
|
-
tools.push(indexPath);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
catch {
|
|
237
|
-
return [];
|
|
238
|
-
}
|
|
239
|
-
return tools;
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Discover and load tools from standard locations:
|
|
243
|
-
* 1. agentDir/tools/*.ts (global)
|
|
244
|
-
* 2. cwd/.pi/tools/*.ts (project-local)
|
|
245
|
-
*
|
|
246
|
-
* Plus any explicitly configured paths from settings or CLI.
|
|
247
|
-
*
|
|
248
|
-
* @param configuredPaths - Explicit paths from settings.json and CLI --tool flags
|
|
249
|
-
* @param cwd - Current working directory
|
|
250
|
-
* @param builtInToolNames - Names of built-in tools to check for conflicts
|
|
251
|
-
* @param agentDir - Agent config directory. Default: from getAgentDir()
|
|
252
|
-
*/
|
|
253
|
-
export async function discoverAndLoadCustomTools(configuredPaths, cwd, builtInToolNames, agentDir = getAgentDir()) {
|
|
254
|
-
const allPaths = [];
|
|
255
|
-
const seen = new Set();
|
|
256
|
-
// Helper to add paths without duplicates
|
|
257
|
-
const addPaths = (paths) => {
|
|
258
|
-
for (const p of paths) {
|
|
259
|
-
const resolved = path.resolve(p);
|
|
260
|
-
if (!seen.has(resolved)) {
|
|
261
|
-
seen.add(resolved);
|
|
262
|
-
allPaths.push(p);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
// 1. Global tools: agentDir/tools/
|
|
267
|
-
const globalToolsDir = path.join(agentDir, "tools");
|
|
268
|
-
addPaths(discoverToolsInDir(globalToolsDir));
|
|
269
|
-
// 2. Project-local tools: cwd/.pi/tools/
|
|
270
|
-
const localToolsDir = path.join(cwd, ".pi", "tools");
|
|
271
|
-
addPaths(discoverToolsInDir(localToolsDir));
|
|
272
|
-
// 3. Explicitly configured paths (can override/add)
|
|
273
|
-
addPaths(configuredPaths.map((p) => resolveToolPath(p, cwd)));
|
|
274
|
-
return loadCustomTools(allPaths, cwd, builtInToolNames);
|
|
275
|
-
}
|
|
276
|
-
//# sourceMappingURL=loader.js.map
|