@oh-my-pi/pi-coding-agent 1.337.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 +1228 -0
- package/README.md +1041 -0
- package/docs/compaction.md +403 -0
- package/docs/custom-tools.md +541 -0
- package/docs/extension-loading.md +1004 -0
- package/docs/hooks.md +867 -0
- package/docs/rpc.md +1040 -0
- package/docs/sdk.md +994 -0
- package/docs/session-tree-plan.md +441 -0
- package/docs/session.md +240 -0
- package/docs/skills.md +290 -0
- package/docs/theme.md +637 -0
- package/docs/tree.md +197 -0
- package/docs/tui.md +341 -0
- package/examples/README.md +21 -0
- package/examples/custom-tools/README.md +124 -0
- package/examples/custom-tools/hello/index.ts +20 -0
- package/examples/custom-tools/question/index.ts +84 -0
- package/examples/custom-tools/subagent/README.md +172 -0
- package/examples/custom-tools/subagent/agents/planner.md +37 -0
- package/examples/custom-tools/subagent/agents/reviewer.md +35 -0
- package/examples/custom-tools/subagent/agents/scout.md +50 -0
- package/examples/custom-tools/subagent/agents/worker.md +24 -0
- package/examples/custom-tools/subagent/agents.ts +156 -0
- package/examples/custom-tools/subagent/commands/implement-and-review.md +10 -0
- package/examples/custom-tools/subagent/commands/implement.md +10 -0
- package/examples/custom-tools/subagent/commands/scout-and-plan.md +9 -0
- package/examples/custom-tools/subagent/index.ts +1002 -0
- package/examples/custom-tools/todo/index.ts +212 -0
- package/examples/hooks/README.md +56 -0
- package/examples/hooks/auto-commit-on-exit.ts +49 -0
- package/examples/hooks/confirm-destructive.ts +59 -0
- package/examples/hooks/custom-compaction.ts +116 -0
- package/examples/hooks/dirty-repo-guard.ts +52 -0
- package/examples/hooks/file-trigger.ts +41 -0
- package/examples/hooks/git-checkpoint.ts +53 -0
- package/examples/hooks/handoff.ts +150 -0
- package/examples/hooks/permission-gate.ts +34 -0
- package/examples/hooks/protected-paths.ts +30 -0
- package/examples/hooks/qna.ts +119 -0
- package/examples/hooks/snake.ts +343 -0
- package/examples/hooks/status-line.ts +40 -0
- package/examples/sdk/01-minimal.ts +22 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +44 -0
- package/examples/sdk/04-skills.ts +44 -0
- package/examples/sdk/05-tools.ts +90 -0
- package/examples/sdk/06-hooks.ts +61 -0
- package/examples/sdk/07-context-files.ts +36 -0
- package/examples/sdk/08-slash-commands.ts +42 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +55 -0
- package/examples/sdk/10-settings.ts +38 -0
- package/examples/sdk/11-sessions.ts +48 -0
- package/examples/sdk/12-full-control.ts +95 -0
- package/examples/sdk/README.md +154 -0
- package/package.json +81 -0
- package/src/cli/args.ts +246 -0
- package/src/cli/file-processor.ts +72 -0
- package/src/cli/list-models.ts +104 -0
- package/src/cli/plugin-cli.ts +650 -0
- package/src/cli/session-picker.ts +41 -0
- package/src/cli.ts +10 -0
- package/src/commands/init.md +20 -0
- package/src/config.ts +159 -0
- package/src/core/agent-session.ts +1900 -0
- package/src/core/auth-storage.ts +236 -0
- package/src/core/bash-executor.ts +196 -0
- package/src/core/compaction/branch-summarization.ts +343 -0
- package/src/core/compaction/compaction.ts +742 -0
- package/src/core/compaction/index.ts +7 -0
- package/src/core/compaction/utils.ts +154 -0
- package/src/core/custom-tools/index.ts +21 -0
- package/src/core/custom-tools/loader.ts +248 -0
- package/src/core/custom-tools/types.ts +169 -0
- package/src/core/custom-tools/wrapper.ts +28 -0
- package/src/core/exec.ts +129 -0
- package/src/core/export-html/index.ts +211 -0
- package/src/core/export-html/template.css +781 -0
- package/src/core/export-html/template.html +54 -0
- package/src/core/export-html/template.js +1185 -0
- package/src/core/export-html/vendor/highlight.min.js +1213 -0
- package/src/core/export-html/vendor/marked.min.js +6 -0
- package/src/core/hooks/index.ts +16 -0
- package/src/core/hooks/loader.ts +312 -0
- package/src/core/hooks/runner.ts +434 -0
- package/src/core/hooks/tool-wrapper.ts +99 -0
- package/src/core/hooks/types.ts +773 -0
- package/src/core/index.ts +52 -0
- package/src/core/mcp/client.ts +158 -0
- package/src/core/mcp/config.ts +154 -0
- package/src/core/mcp/index.ts +45 -0
- package/src/core/mcp/loader.ts +68 -0
- package/src/core/mcp/manager.ts +181 -0
- package/src/core/mcp/tool-bridge.ts +148 -0
- package/src/core/mcp/transports/http.ts +316 -0
- package/src/core/mcp/transports/index.ts +6 -0
- package/src/core/mcp/transports/stdio.ts +252 -0
- package/src/core/mcp/types.ts +220 -0
- package/src/core/messages.ts +189 -0
- package/src/core/model-registry.ts +317 -0
- package/src/core/model-resolver.ts +393 -0
- package/src/core/plugins/doctor.ts +59 -0
- package/src/core/plugins/index.ts +38 -0
- package/src/core/plugins/installer.ts +189 -0
- package/src/core/plugins/loader.ts +338 -0
- package/src/core/plugins/manager.ts +672 -0
- package/src/core/plugins/parser.ts +105 -0
- package/src/core/plugins/paths.ts +32 -0
- package/src/core/plugins/types.ts +190 -0
- package/src/core/sdk.ts +760 -0
- package/src/core/session-manager.ts +1128 -0
- package/src/core/settings-manager.ts +443 -0
- package/src/core/skills.ts +437 -0
- package/src/core/slash-commands.ts +248 -0
- package/src/core/system-prompt.ts +439 -0
- package/src/core/timings.ts +25 -0
- package/src/core/tools/ask.ts +211 -0
- package/src/core/tools/bash-interceptor.ts +120 -0
- package/src/core/tools/bash.ts +250 -0
- package/src/core/tools/context.ts +32 -0
- package/src/core/tools/edit-diff.ts +475 -0
- package/src/core/tools/edit.ts +208 -0
- package/src/core/tools/exa/company.ts +59 -0
- package/src/core/tools/exa/index.ts +64 -0
- package/src/core/tools/exa/linkedin.ts +59 -0
- package/src/core/tools/exa/logger.ts +56 -0
- package/src/core/tools/exa/mcp-client.ts +368 -0
- package/src/core/tools/exa/render.ts +196 -0
- package/src/core/tools/exa/researcher.ts +90 -0
- package/src/core/tools/exa/search.ts +337 -0
- package/src/core/tools/exa/types.ts +168 -0
- package/src/core/tools/exa/websets.ts +248 -0
- package/src/core/tools/find.ts +261 -0
- package/src/core/tools/grep.ts +555 -0
- package/src/core/tools/index.ts +202 -0
- package/src/core/tools/ls.ts +140 -0
- package/src/core/tools/lsp/client.ts +605 -0
- package/src/core/tools/lsp/config.ts +147 -0
- package/src/core/tools/lsp/edits.ts +101 -0
- package/src/core/tools/lsp/index.ts +804 -0
- package/src/core/tools/lsp/render.ts +447 -0
- package/src/core/tools/lsp/rust-analyzer.ts +145 -0
- package/src/core/tools/lsp/types.ts +463 -0
- package/src/core/tools/lsp/utils.ts +486 -0
- package/src/core/tools/notebook.ts +229 -0
- package/src/core/tools/path-utils.ts +61 -0
- package/src/core/tools/read.ts +240 -0
- package/src/core/tools/renderers.ts +540 -0
- package/src/core/tools/task/agents.ts +153 -0
- package/src/core/tools/task/artifacts.ts +114 -0
- package/src/core/tools/task/bundled-agents/browser.md +71 -0
- package/src/core/tools/task/bundled-agents/explore.md +82 -0
- package/src/core/tools/task/bundled-agents/plan.md +54 -0
- package/src/core/tools/task/bundled-agents/reviewer.md +59 -0
- package/src/core/tools/task/bundled-agents/task.md +53 -0
- package/src/core/tools/task/bundled-commands/architect-plan.md +10 -0
- package/src/core/tools/task/bundled-commands/implement-with-critic.md +11 -0
- package/src/core/tools/task/bundled-commands/implement.md +11 -0
- package/src/core/tools/task/commands.ts +213 -0
- package/src/core/tools/task/discovery.ts +208 -0
- package/src/core/tools/task/executor.ts +367 -0
- package/src/core/tools/task/index.ts +388 -0
- package/src/core/tools/task/model-resolver.ts +115 -0
- package/src/core/tools/task/parallel.ts +38 -0
- package/src/core/tools/task/render.ts +232 -0
- package/src/core/tools/task/types.ts +99 -0
- package/src/core/tools/truncate.ts +265 -0
- package/src/core/tools/web-fetch.ts +2370 -0
- package/src/core/tools/web-search/auth.ts +193 -0
- package/src/core/tools/web-search/index.ts +537 -0
- package/src/core/tools/web-search/providers/anthropic.ts +198 -0
- package/src/core/tools/web-search/providers/exa.ts +302 -0
- package/src/core/tools/web-search/providers/perplexity.ts +195 -0
- package/src/core/tools/web-search/render.ts +182 -0
- package/src/core/tools/web-search/types.ts +180 -0
- package/src/core/tools/write.ts +99 -0
- package/src/index.ts +176 -0
- package/src/main.ts +464 -0
- package/src/migrations.ts +135 -0
- package/src/modes/index.ts +43 -0
- package/src/modes/interactive/components/armin.ts +382 -0
- package/src/modes/interactive/components/assistant-message.ts +86 -0
- package/src/modes/interactive/components/bash-execution.ts +196 -0
- package/src/modes/interactive/components/bordered-loader.ts +41 -0
- package/src/modes/interactive/components/branch-summary-message.ts +42 -0
- package/src/modes/interactive/components/compaction-summary-message.ts +45 -0
- package/src/modes/interactive/components/custom-editor.ts +122 -0
- package/src/modes/interactive/components/diff.ts +147 -0
- package/src/modes/interactive/components/dynamic-border.ts +25 -0
- package/src/modes/interactive/components/footer.ts +381 -0
- package/src/modes/interactive/components/hook-editor.ts +117 -0
- package/src/modes/interactive/components/hook-input.ts +64 -0
- package/src/modes/interactive/components/hook-message.ts +96 -0
- package/src/modes/interactive/components/hook-selector.ts +91 -0
- package/src/modes/interactive/components/model-selector.ts +247 -0
- package/src/modes/interactive/components/oauth-selector.ts +120 -0
- package/src/modes/interactive/components/plugin-settings.ts +479 -0
- package/src/modes/interactive/components/queue-mode-selector.ts +56 -0
- package/src/modes/interactive/components/session-selector.ts +204 -0
- package/src/modes/interactive/components/settings-selector.ts +453 -0
- package/src/modes/interactive/components/show-images-selector.ts +45 -0
- package/src/modes/interactive/components/theme-selector.ts +62 -0
- package/src/modes/interactive/components/thinking-selector.ts +64 -0
- package/src/modes/interactive/components/tool-execution.ts +675 -0
- package/src/modes/interactive/components/tree-selector.ts +866 -0
- package/src/modes/interactive/components/user-message-selector.ts +159 -0
- package/src/modes/interactive/components/user-message.ts +18 -0
- package/src/modes/interactive/components/visual-truncate.ts +50 -0
- package/src/modes/interactive/components/welcome.ts +183 -0
- package/src/modes/interactive/interactive-mode.ts +2516 -0
- package/src/modes/interactive/theme/dark.json +101 -0
- package/src/modes/interactive/theme/light.json +98 -0
- package/src/modes/interactive/theme/theme-schema.json +308 -0
- package/src/modes/interactive/theme/theme.ts +998 -0
- package/src/modes/print-mode.ts +128 -0
- package/src/modes/rpc/rpc-client.ts +527 -0
- package/src/modes/rpc/rpc-mode.ts +483 -0
- package/src/modes/rpc/rpc-types.ts +203 -0
- package/src/utils/changelog.ts +99 -0
- package/src/utils/clipboard.ts +265 -0
- package/src/utils/fuzzy.ts +108 -0
- package/src/utils/mime.ts +30 -0
- package/src/utils/shell.ts +276 -0
- package/src/utils/tools-manager.ts +274 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
export { type AskToolDetails, askTool, createAskTool } from "./ask.js";
|
|
2
|
+
export { type BashToolDetails, bashTool, createBashTool } from "./bash.js";
|
|
3
|
+
export { createEditTool, editTool } from "./edit.js";
|
|
4
|
+
// Exa MCP tools (22 tools)
|
|
5
|
+
export { exaTools } from "./exa/index.js";
|
|
6
|
+
export type { ExaRenderDetails, ExaSearchResponse, ExaSearchResult } from "./exa/types.js";
|
|
7
|
+
export { createFindTool, type FindToolDetails, findTool } from "./find.js";
|
|
8
|
+
export { createGrepTool, type GrepToolDetails, grepTool } from "./grep.js";
|
|
9
|
+
export { createLsTool, type LsToolDetails, lsTool } from "./ls.js";
|
|
10
|
+
export { createLspTool, type LspToolDetails, lspTool } from "./lsp/index.js";
|
|
11
|
+
export { createNotebookTool, type NotebookToolDetails, notebookTool } from "./notebook.js";
|
|
12
|
+
export { createReadTool, type ReadToolDetails, readTool } from "./read.js";
|
|
13
|
+
export { BUNDLED_AGENTS, createTaskTool, taskTool } from "./task/index.js";
|
|
14
|
+
export type { TruncationResult } from "./truncate.js";
|
|
15
|
+
export { createWebFetchTool, type WebFetchToolDetails, webFetchCustomTool, webFetchTool } from "./web-fetch.js";
|
|
16
|
+
export {
|
|
17
|
+
companyWebSearchTools,
|
|
18
|
+
createWebSearchTool,
|
|
19
|
+
exaWebSearchTools,
|
|
20
|
+
getWebSearchTools,
|
|
21
|
+
hasExaWebSearch,
|
|
22
|
+
linkedinWebSearchTools,
|
|
23
|
+
type WebSearchProvider,
|
|
24
|
+
type WebSearchResponse,
|
|
25
|
+
type WebSearchToolsOptions,
|
|
26
|
+
webSearchCodeContextTool,
|
|
27
|
+
webSearchCompanyTool,
|
|
28
|
+
webSearchCrawlTool,
|
|
29
|
+
webSearchCustomTool,
|
|
30
|
+
webSearchDeepTool,
|
|
31
|
+
webSearchLinkedinTool,
|
|
32
|
+
webSearchTool,
|
|
33
|
+
} from "./web-search/index.js";
|
|
34
|
+
export { createWriteTool, writeTool } from "./write.js";
|
|
35
|
+
|
|
36
|
+
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
37
|
+
import { askTool, createAskTool } from "./ask.js";
|
|
38
|
+
import { bashTool, createBashTool } from "./bash.js";
|
|
39
|
+
import { checkBashInterception, checkSimpleLsInterception } from "./bash-interceptor.js";
|
|
40
|
+
import { createEditTool, editTool } from "./edit.js";
|
|
41
|
+
import { createFindTool, findTool } from "./find.js";
|
|
42
|
+
import { createGrepTool, grepTool } from "./grep.js";
|
|
43
|
+
import { createLsTool, lsTool } from "./ls.js";
|
|
44
|
+
import { createLspTool, lspTool } from "./lsp/index.js";
|
|
45
|
+
import { createNotebookTool, notebookTool } from "./notebook.js";
|
|
46
|
+
import { createReadTool, readTool } from "./read.js";
|
|
47
|
+
import { createTaskTool, taskTool } from "./task/index.js";
|
|
48
|
+
import { createWebFetchTool, webFetchTool } from "./web-fetch.js";
|
|
49
|
+
import { createWebSearchTool, webSearchTool } from "./web-search/index.js";
|
|
50
|
+
import { createWriteTool, writeTool } from "./write.js";
|
|
51
|
+
|
|
52
|
+
/** Tool type (AgentTool from pi-ai) */
|
|
53
|
+
export type Tool = AgentTool<any, any, any>;
|
|
54
|
+
|
|
55
|
+
/** Context for tools that need session information */
|
|
56
|
+
export interface SessionContext {
|
|
57
|
+
getSessionFile: () => string | null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Factory function type
|
|
61
|
+
type ToolFactory = (cwd: string, sessionContext?: SessionContext) => Tool;
|
|
62
|
+
|
|
63
|
+
// Tool definitions: static tools and their factory functions
|
|
64
|
+
const toolDefs: Record<string, { tool: Tool; create: ToolFactory }> = {
|
|
65
|
+
ask: { tool: askTool, create: createAskTool },
|
|
66
|
+
read: { tool: readTool, create: createReadTool },
|
|
67
|
+
bash: { tool: bashTool, create: createBashTool },
|
|
68
|
+
edit: { tool: editTool, create: createEditTool },
|
|
69
|
+
write: { tool: writeTool, create: createWriteTool },
|
|
70
|
+
grep: { tool: grepTool, create: createGrepTool },
|
|
71
|
+
find: { tool: findTool, create: createFindTool },
|
|
72
|
+
ls: { tool: lsTool, create: createLsTool },
|
|
73
|
+
lsp: { tool: lspTool, create: createLspTool },
|
|
74
|
+
notebook: { tool: notebookTool, create: createNotebookTool },
|
|
75
|
+
task: { tool: taskTool, create: (cwd, ctx) => createTaskTool(cwd, ctx) },
|
|
76
|
+
web_fetch: { tool: webFetchTool, create: createWebFetchTool },
|
|
77
|
+
web_search: { tool: webSearchTool, create: createWebSearchTool },
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export type ToolName = keyof typeof toolDefs;
|
|
81
|
+
|
|
82
|
+
// Tools that require UI (excluded when hasUI is false)
|
|
83
|
+
const uiToolNames: ToolName[] = ["ask"];
|
|
84
|
+
|
|
85
|
+
// Tool sets defined by name (base sets, without UI-only tools)
|
|
86
|
+
const baseCodingToolNames: ToolName[] = [
|
|
87
|
+
"read",
|
|
88
|
+
"bash",
|
|
89
|
+
"edit",
|
|
90
|
+
"write",
|
|
91
|
+
"grep",
|
|
92
|
+
"find",
|
|
93
|
+
"ls",
|
|
94
|
+
"lsp",
|
|
95
|
+
"notebook",
|
|
96
|
+
"task",
|
|
97
|
+
"web_fetch",
|
|
98
|
+
"web_search",
|
|
99
|
+
];
|
|
100
|
+
const baseReadOnlyToolNames: ToolName[] = ["read", "grep", "find", "ls"];
|
|
101
|
+
|
|
102
|
+
// Default tools for full access mode (using process.cwd(), no UI)
|
|
103
|
+
export const codingTools: Tool[] = baseCodingToolNames.map((name) => toolDefs[name].tool);
|
|
104
|
+
|
|
105
|
+
// Read-only tools for exploration without modification (using process.cwd(), no UI)
|
|
106
|
+
export const readOnlyTools: Tool[] = baseReadOnlyToolNames.map((name) => toolDefs[name].tool);
|
|
107
|
+
|
|
108
|
+
// All available tools (using process.cwd(), no UI)
|
|
109
|
+
export const allTools = Object.fromEntries(Object.entries(toolDefs).map(([name, def]) => [name, def.tool])) as Record<
|
|
110
|
+
ToolName,
|
|
111
|
+
Tool
|
|
112
|
+
>;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Create coding tools configured for a specific working directory.
|
|
116
|
+
* @param cwd - Working directory for tools
|
|
117
|
+
* @param hasUI - Whether UI is available (includes ask tool if true)
|
|
118
|
+
* @param sessionContext - Optional session context for tools that need it
|
|
119
|
+
*/
|
|
120
|
+
export function createCodingTools(cwd: string, hasUI = false, sessionContext?: SessionContext): Tool[] {
|
|
121
|
+
const names = hasUI ? [...baseCodingToolNames, ...uiToolNames] : baseCodingToolNames;
|
|
122
|
+
return names.map((name) => toolDefs[name].create(cwd, sessionContext));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Create read-only tools configured for a specific working directory.
|
|
127
|
+
* @param cwd - Working directory for tools
|
|
128
|
+
* @param hasUI - Whether UI is available (includes ask tool if true)
|
|
129
|
+
* @param sessionContext - Optional session context for tools that need it
|
|
130
|
+
*/
|
|
131
|
+
export function createReadOnlyTools(cwd: string, hasUI = false, sessionContext?: SessionContext): Tool[] {
|
|
132
|
+
const names = hasUI ? [...baseReadOnlyToolNames, ...uiToolNames] : baseReadOnlyToolNames;
|
|
133
|
+
return names.map((name) => toolDefs[name].create(cwd, sessionContext));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Create all tools configured for a specific working directory.
|
|
138
|
+
* @param cwd - Working directory for tools
|
|
139
|
+
* @param sessionContext - Optional session context for tools that need it
|
|
140
|
+
*/
|
|
141
|
+
export function createAllTools(cwd: string, sessionContext?: SessionContext): Record<ToolName, Tool> {
|
|
142
|
+
return Object.fromEntries(
|
|
143
|
+
Object.entries(toolDefs).map(([name, def]) => [name, def.create(cwd, sessionContext)]),
|
|
144
|
+
) as Record<ToolName, Tool>;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Wrap a bash tool with interception that redirects common patterns to specialized tools.
|
|
149
|
+
* This helps prevent LLMs from falling back to shell commands when better tools exist.
|
|
150
|
+
*
|
|
151
|
+
* @param bashTool - The bash tool to wrap
|
|
152
|
+
* @param availableTools - Set of tool names that are available (for context-aware blocking)
|
|
153
|
+
* @returns Wrapped bash tool with interception
|
|
154
|
+
*/
|
|
155
|
+
export function wrapBashWithInterception(bashTool: Tool, availableTools: Set<string>): Tool {
|
|
156
|
+
const originalExecute = bashTool.execute;
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
...bashTool,
|
|
160
|
+
execute: async (toolCallId, params, signal, onUpdate, context) => {
|
|
161
|
+
const command = (params as { command: string }).command;
|
|
162
|
+
|
|
163
|
+
// Check for forbidden patterns
|
|
164
|
+
const interception = checkBashInterception(command, availableTools);
|
|
165
|
+
if (interception.block) {
|
|
166
|
+
throw new Error(interception.message);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Check for simple ls that should use ls tool
|
|
170
|
+
const lsInterception = checkSimpleLsInterception(command, availableTools);
|
|
171
|
+
if (lsInterception.block) {
|
|
172
|
+
throw new Error(lsInterception.message);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Pass through to original bash tool
|
|
176
|
+
return originalExecute(toolCallId, params, signal, onUpdate, context);
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Apply bash interception to a set of tools.
|
|
183
|
+
* Finds the bash tool and wraps it with interception based on other available tools.
|
|
184
|
+
*
|
|
185
|
+
* @param tools - Array of tools to process
|
|
186
|
+
* @returns Tools with bash interception applied
|
|
187
|
+
*/
|
|
188
|
+
export function applyBashInterception(tools: Tool[]): Tool[] {
|
|
189
|
+
const toolNames = new Set(tools.map((t) => t.name));
|
|
190
|
+
|
|
191
|
+
// If bash isn't in the tools, nothing to do
|
|
192
|
+
if (!toolNames.has("bash")) {
|
|
193
|
+
return tools;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return tools.map((tool) => {
|
|
197
|
+
if (tool.name === "bash") {
|
|
198
|
+
return wrapBashWithInterception(tool, toolNames);
|
|
199
|
+
}
|
|
200
|
+
return tool;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
2
|
+
import { Type } from "@sinclair/typebox";
|
|
3
|
+
import { existsSync, readdirSync, statSync } from "fs";
|
|
4
|
+
import nodePath from "path";
|
|
5
|
+
import { resolveToCwd } from "./path-utils.js";
|
|
6
|
+
import { DEFAULT_MAX_BYTES, formatSize, type TruncationResult, truncateHead } from "./truncate.js";
|
|
7
|
+
|
|
8
|
+
const lsSchema = Type.Object({
|
|
9
|
+
path: Type.Optional(Type.String({ description: "Directory to list (default: current directory)" })),
|
|
10
|
+
limit: Type.Optional(Type.Number({ description: "Maximum number of entries to return (default: 500)" })),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const DEFAULT_LIMIT = 500;
|
|
14
|
+
|
|
15
|
+
export interface LsToolDetails {
|
|
16
|
+
truncation?: TruncationResult;
|
|
17
|
+
entryLimitReached?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function createLsTool(cwd: string): AgentTool<typeof lsSchema> {
|
|
21
|
+
return {
|
|
22
|
+
name: "ls",
|
|
23
|
+
label: "Ls",
|
|
24
|
+
description: `List directory contents. Returns entries sorted alphabetically, with '/' suffix for directories. Includes dotfiles. Output is truncated to 500 entries or 50KB (whichever is hit first). List structure helps with directory navigation and finding target files.`,
|
|
25
|
+
parameters: lsSchema,
|
|
26
|
+
execute: async (
|
|
27
|
+
_toolCallId: string,
|
|
28
|
+
{ path, limit }: { path?: string; limit?: number },
|
|
29
|
+
signal?: AbortSignal,
|
|
30
|
+
) => {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
if (signal?.aborted) {
|
|
33
|
+
reject(new Error("Operation aborted"));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const onAbort = () => reject(new Error("Operation aborted"));
|
|
38
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const dirPath = resolveToCwd(path || ".", cwd);
|
|
42
|
+
const effectiveLimit = limit ?? DEFAULT_LIMIT;
|
|
43
|
+
|
|
44
|
+
// Check if path exists
|
|
45
|
+
if (!existsSync(dirPath)) {
|
|
46
|
+
reject(new Error(`Path not found: ${dirPath}`));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check if path is a directory
|
|
51
|
+
const stat = statSync(dirPath);
|
|
52
|
+
if (!stat.isDirectory()) {
|
|
53
|
+
reject(new Error(`Not a directory: ${dirPath}`));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Read directory entries
|
|
58
|
+
let entries: string[];
|
|
59
|
+
try {
|
|
60
|
+
entries = readdirSync(dirPath);
|
|
61
|
+
} catch (e: any) {
|
|
62
|
+
reject(new Error(`Cannot read directory: ${e.message}`));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Sort alphabetically (case-insensitive)
|
|
67
|
+
entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
|
68
|
+
|
|
69
|
+
// Format entries with directory indicators
|
|
70
|
+
const results: string[] = [];
|
|
71
|
+
let entryLimitReached = false;
|
|
72
|
+
|
|
73
|
+
for (const entry of entries) {
|
|
74
|
+
if (results.length >= effectiveLimit) {
|
|
75
|
+
entryLimitReached = true;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const fullPath = nodePath.join(dirPath, entry);
|
|
80
|
+
let suffix = "";
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const entryStat = statSync(fullPath);
|
|
84
|
+
if (entryStat.isDirectory()) {
|
|
85
|
+
suffix = "/";
|
|
86
|
+
}
|
|
87
|
+
} catch {
|
|
88
|
+
// Skip entries we can't stat
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
results.push(entry + suffix);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
signal?.removeEventListener("abort", onAbort);
|
|
96
|
+
|
|
97
|
+
if (results.length === 0) {
|
|
98
|
+
resolve({ content: [{ type: "text", text: "(empty directory)" }], details: undefined });
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Apply byte truncation (no line limit since we already have entry limit)
|
|
103
|
+
const rawOutput = results.join("\n");
|
|
104
|
+
const truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });
|
|
105
|
+
|
|
106
|
+
let output = truncation.content;
|
|
107
|
+
const details: LsToolDetails = {};
|
|
108
|
+
|
|
109
|
+
// Build notices
|
|
110
|
+
const notices: string[] = [];
|
|
111
|
+
|
|
112
|
+
if (entryLimitReached) {
|
|
113
|
+
notices.push(`${effectiveLimit} entries limit reached. Use limit=${effectiveLimit * 2} for more`);
|
|
114
|
+
details.entryLimitReached = effectiveLimit;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (truncation.truncated) {
|
|
118
|
+
notices.push(`${formatSize(DEFAULT_MAX_BYTES)} limit reached`);
|
|
119
|
+
details.truncation = truncation;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (notices.length > 0) {
|
|
123
|
+
output += `\n\n[${notices.join(". ")}]`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
resolve({
|
|
127
|
+
content: [{ type: "text", text: output }],
|
|
128
|
+
details: Object.keys(details).length > 0 ? details : undefined,
|
|
129
|
+
});
|
|
130
|
+
} catch (e: any) {
|
|
131
|
+
signal?.removeEventListener("abort", onAbort);
|
|
132
|
+
reject(e);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Default ls tool using process.cwd() - for backwards compatibility */
|
|
140
|
+
export const lsTool = createLsTool(process.cwd());
|