@oh-my-pi/pi-coding-agent 14.3.0 → 14.4.1
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 +98 -1
- package/package.json +7 -7
- package/src/autoresearch/prompt.md +1 -1
- package/src/commit/agentic/prompts/analyze-file.md +1 -1
- package/src/config/model-registry.ts +67 -15
- package/src/config/prompt-templates.ts +5 -5
- package/src/config/settings-schema.ts +4 -4
- package/src/cursor.ts +3 -8
- package/src/discovery/helpers.ts +3 -3
- package/src/edit/diff.ts +50 -47
- package/src/edit/index.ts +86 -57
- package/src/edit/line-hash.ts +743 -24
- package/src/edit/modes/apply-patch.ts +0 -9
- package/src/edit/modes/atom.ts +893 -0
- package/src/edit/modes/chunk.ts +14 -24
- package/src/edit/modes/hashline.ts +193 -146
- package/src/edit/modes/patch.ts +5 -9
- package/src/edit/modes/replace.ts +6 -11
- package/src/edit/renderer.ts +14 -10
- package/src/edit/streaming.ts +50 -16
- package/src/exec/bash-executor.ts +2 -4
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +4 -12
- package/src/extensibility/custom-tools/types.ts +2 -0
- package/src/extensibility/custom-tools/wrapper.ts +2 -1
- package/src/internal-urls/docs-index.generated.ts +2 -2
- package/src/lsp/defaults.json +142 -652
- package/src/lsp/index.ts +1 -1
- package/src/mcp/render.ts +1 -8
- package/src/modes/components/assistant-message.ts +4 -0
- package/src/modes/components/diff.ts +23 -14
- package/src/modes/components/footer.ts +21 -16
- package/src/modes/components/session-selector.ts +3 -3
- package/src/modes/components/settings-defs.ts +6 -1
- package/src/modes/components/todo-reminder.ts +1 -8
- package/src/modes/components/tool-execution.ts +1 -4
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/print-mode.ts +8 -0
- package/src/prompts/agents/librarian.md +1 -1
- package/src/prompts/agents/reviewer.md +4 -4
- package/src/prompts/ci-green-request.md +1 -1
- package/src/prompts/review-request.md +1 -1
- package/src/prompts/system/subagent-system-prompt.md +3 -3
- package/src/prompts/system/subagent-yield-reminder.md +11 -0
- package/src/prompts/system/system-prompt.md +3 -0
- package/src/prompts/tools/ask.md +3 -2
- package/src/prompts/tools/ast-edit.md +16 -20
- package/src/prompts/tools/ast-grep.md +19 -24
- package/src/prompts/tools/atom.md +87 -0
- package/src/prompts/tools/chunk-edit.md +37 -161
- package/src/prompts/tools/debug.md +4 -5
- package/src/prompts/tools/exit-plan-mode.md +4 -5
- package/src/prompts/tools/find.md +4 -8
- package/src/prompts/tools/github.md +18 -0
- package/src/prompts/tools/grep.md +4 -5
- package/src/prompts/tools/hashline.md +22 -89
- package/src/prompts/tools/{gemini-image.md → image-gen.md} +1 -1
- package/src/prompts/tools/inspect-image.md +6 -6
- package/src/prompts/tools/lsp.md +1 -1
- package/src/prompts/tools/patch.md +12 -19
- package/src/prompts/tools/python.md +3 -2
- package/src/prompts/tools/read-chunk.md +2 -3
- package/src/prompts/tools/read.md +2 -2
- package/src/prompts/tools/ssh.md +8 -17
- package/src/prompts/tools/todo-write.md +54 -41
- package/src/sdk.ts +14 -9
- package/src/session/agent-session.ts +25 -2
- package/src/session/session-manager.ts +4 -1
- package/src/task/executor.ts +43 -48
- package/src/task/render.ts +11 -13
- package/src/tools/ask.ts +7 -7
- package/src/tools/ast-edit.ts +45 -41
- package/src/tools/ast-grep.ts +77 -85
- package/src/tools/bash.ts +8 -9
- package/src/tools/browser.ts +32 -30
- package/src/tools/calculator.ts +4 -4
- package/src/tools/cancel-job.ts +1 -1
- package/src/tools/checkpoint.ts +2 -2
- package/src/tools/debug.ts +41 -37
- package/src/tools/exit-plan-mode.ts +1 -1
- package/src/tools/find.ts +4 -4
- package/src/tools/gh-renderer.ts +12 -4
- package/src/tools/gh.ts +509 -697
- package/src/tools/grep.ts +116 -131
- package/src/tools/{gemini-image.ts → image-gen.ts} +459 -60
- package/src/tools/index.ts +14 -32
- package/src/tools/inspect-image.ts +3 -3
- package/src/tools/json-tree.ts +114 -114
- package/src/tools/match-line-format.ts +8 -7
- package/src/tools/notebook.ts +8 -7
- package/src/tools/poll-tool.ts +2 -1
- package/src/tools/python.ts +9 -23
- package/src/tools/read.ts +32 -25
- package/src/tools/render-mermaid.ts +1 -1
- package/src/tools/render-utils.ts +18 -0
- package/src/tools/renderers.ts +2 -2
- package/src/tools/report-tool-issue.ts +3 -2
- package/src/tools/resolve.ts +1 -1
- package/src/tools/review.ts +12 -10
- package/src/tools/search-tool-bm25.ts +2 -4
- package/src/tools/ssh.ts +4 -4
- package/src/tools/todo-write.ts +172 -147
- package/src/tools/vim.ts +14 -15
- package/src/tools/write.ts +4 -4
- package/src/tools/{submit-result.ts → yield.ts} +11 -13
- package/src/utils/edit-mode.ts +2 -1
- package/src/utils/file-display-mode.ts +10 -5
- package/src/utils/git.ts +9 -5
- package/src/utils/shell-snapshot.ts +2 -3
- package/src/vim/render.ts +4 -4
- package/src/prompts/system/subagent-submit-reminder.md +0 -11
- package/src/prompts/tools/gh-issue-view.md +0 -11
- package/src/prompts/tools/gh-pr-checkout.md +0 -12
- package/src/prompts/tools/gh-pr-diff.md +0 -12
- package/src/prompts/tools/gh-pr-push.md +0 -12
- package/src/prompts/tools/gh-pr-view.md +0 -11
- package/src/prompts/tools/gh-repo-view.md +0 -11
- package/src/prompts/tools/gh-run-watch.md +0 -12
- package/src/prompts/tools/gh-search-issues.md +0 -11
- package/src/prompts/tools/gh-search-prs.md +0 -11
package/src/tools/index.ts
CHANGED
|
@@ -29,17 +29,7 @@ import { type CheckpointState, CheckpointTool, RewindTool } from "./checkpoint";
|
|
|
29
29
|
import { DebugTool } from "./debug";
|
|
30
30
|
import { ExitPlanModeTool } from "./exit-plan-mode";
|
|
31
31
|
import { FindTool } from "./find";
|
|
32
|
-
import {
|
|
33
|
-
GhIssueViewTool,
|
|
34
|
-
GhPrCheckoutTool,
|
|
35
|
-
GhPrDiffTool,
|
|
36
|
-
GhPrPushTool,
|
|
37
|
-
GhPrViewTool,
|
|
38
|
-
GhRepoViewTool,
|
|
39
|
-
GhRunWatchTool,
|
|
40
|
-
GhSearchIssuesTool,
|
|
41
|
-
GhSearchPrsTool,
|
|
42
|
-
} from "./gh";
|
|
32
|
+
import { GithubTool } from "./gh";
|
|
43
33
|
import { GrepTool } from "./grep";
|
|
44
34
|
import { InspectImageTool } from "./inspect-image";
|
|
45
35
|
import { NotebookTool } from "./notebook";
|
|
@@ -53,9 +43,9 @@ import { ResolveTool } from "./resolve";
|
|
|
53
43
|
import { reportFindingTool } from "./review";
|
|
54
44
|
import { SearchToolBm25Tool } from "./search-tool-bm25";
|
|
55
45
|
import { loadSshTool } from "./ssh";
|
|
56
|
-
import { SubmitResultTool } from "./submit-result";
|
|
57
46
|
import { type TodoPhase, TodoWriteTool } from "./todo-write";
|
|
58
47
|
import { WriteTool } from "./write";
|
|
48
|
+
import { YieldTool } from "./yield";
|
|
59
49
|
|
|
60
50
|
// Exa MCP tools (22 tools)
|
|
61
51
|
|
|
@@ -77,9 +67,9 @@ export * from "./checkpoint";
|
|
|
77
67
|
export * from "./debug";
|
|
78
68
|
export * from "./exit-plan-mode";
|
|
79
69
|
export * from "./find";
|
|
80
|
-
export * from "./gemini-image";
|
|
81
70
|
export * from "./gh";
|
|
82
71
|
export * from "./grep";
|
|
72
|
+
export * from "./image-gen";
|
|
83
73
|
export * from "./inspect-image";
|
|
84
74
|
export * from "./notebook";
|
|
85
75
|
export * from "./poll-tool";
|
|
@@ -91,10 +81,10 @@ export * from "./resolve";
|
|
|
91
81
|
export * from "./review";
|
|
92
82
|
export * from "./search-tool-bm25";
|
|
93
83
|
export * from "./ssh";
|
|
94
|
-
export * from "./submit-result";
|
|
95
84
|
export * from "./todo-write";
|
|
96
85
|
export * from "./vim";
|
|
97
86
|
export * from "./write";
|
|
87
|
+
export * from "./yield";
|
|
98
88
|
|
|
99
89
|
/** Tool type (AgentTool from pi-ai) */
|
|
100
90
|
export type Tool = AgentTool<any, any, any>;
|
|
@@ -131,8 +121,8 @@ export interface ToolSession {
|
|
|
131
121
|
eventBus?: EventBus;
|
|
132
122
|
/** Output schema for structured completion (subagents) */
|
|
133
123
|
outputSchema?: unknown;
|
|
134
|
-
/** Whether to include the
|
|
135
|
-
|
|
124
|
+
/** Whether to include the yield tool by default */
|
|
125
|
+
requireYieldTool?: boolean;
|
|
136
126
|
/** Task recursion depth (0 = top-level, 1 = first child, etc.) */
|
|
137
127
|
taskDepth?: number;
|
|
138
128
|
/** Get session file */
|
|
@@ -217,15 +207,7 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
|
|
|
217
207
|
calc: s => new CalculatorTool(s),
|
|
218
208
|
ssh: loadSshTool,
|
|
219
209
|
edit: s => new EditTool(s),
|
|
220
|
-
|
|
221
|
-
gh_issue_view: GhIssueViewTool.createIf,
|
|
222
|
-
gh_pr_view: GhPrViewTool.createIf,
|
|
223
|
-
gh_pr_diff: GhPrDiffTool.createIf,
|
|
224
|
-
gh_pr_checkout: GhPrCheckoutTool.createIf,
|
|
225
|
-
gh_pr_push: GhPrPushTool.createIf,
|
|
226
|
-
gh_run_watch: GhRunWatchTool.createIf,
|
|
227
|
-
gh_search_issues: GhSearchIssuesTool.createIf,
|
|
228
|
-
gh_search_prs: GhSearchPrsTool.createIf,
|
|
210
|
+
github: GithubTool.createIf,
|
|
229
211
|
find: s => new FindTool(s),
|
|
230
212
|
grep: s => new GrepTool(s),
|
|
231
213
|
lsp: LspTool.createIf,
|
|
@@ -245,7 +227,7 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
|
|
|
245
227
|
};
|
|
246
228
|
|
|
247
229
|
export const HIDDEN_TOOLS: Record<string, ToolFactory> = {
|
|
248
|
-
|
|
230
|
+
yield: s => new YieldTool(s),
|
|
249
231
|
report_finding: () => reportFindingTool,
|
|
250
232
|
report_tool_issue: s => createReportToolIssueTool(s),
|
|
251
233
|
exit_plan_mode: s => new ExitPlanModeTool(s),
|
|
@@ -288,7 +270,7 @@ function getPythonModeFromEnv(): PythonToolMode | null {
|
|
|
288
270
|
* Create tools from BUILTIN_TOOLS registry.
|
|
289
271
|
*/
|
|
290
272
|
export async function createTools(session: ToolSession, toolNames?: string[]): Promise<Tool[]> {
|
|
291
|
-
const
|
|
273
|
+
const includeYield = session.requireYieldTool === true;
|
|
292
274
|
const enableLsp = session.enableLsp ?? true;
|
|
293
275
|
const requestedTools =
|
|
294
276
|
toolNames && toolNames.length > 0 ? [...new Set(toolNames.map(name => name.toLowerCase()))] : undefined;
|
|
@@ -390,10 +372,10 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
390
372
|
if (name === "bash") return allowBash;
|
|
391
373
|
if (name === "python") return allowPython;
|
|
392
374
|
if (name === "debug") return session.settings.get("debug.enabled");
|
|
393
|
-
if (name === "todo_write") return !
|
|
375
|
+
if (name === "todo_write") return !includeYield && session.settings.get("todo.enabled");
|
|
394
376
|
if (name === "find") return session.settings.get("find.enabled");
|
|
395
377
|
if (name === "grep") return session.settings.get("grep.enabled");
|
|
396
|
-
if (name
|
|
378
|
+
if (name === "github") return session.settings.get("github.enabled");
|
|
397
379
|
if (name === "ast_grep") return session.settings.get("astGrep.enabled");
|
|
398
380
|
if (name === "ast_edit") return session.settings.get("astEdit.enabled");
|
|
399
381
|
if (name === "render_mermaid") return session.settings.get("renderMermaid.enabled");
|
|
@@ -411,8 +393,8 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
411
393
|
}
|
|
412
394
|
return true;
|
|
413
395
|
};
|
|
414
|
-
if (
|
|
415
|
-
requestedTools.push("
|
|
396
|
+
if (includeYield && requestedTools && !requestedTools.includes("yield")) {
|
|
397
|
+
requestedTools.push("yield");
|
|
416
398
|
}
|
|
417
399
|
|
|
418
400
|
const filteredRequestedTools = requestedTools?.filter(name => name in allTools && isToolAllowed(name));
|
|
@@ -421,7 +403,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
421
403
|
? filteredRequestedTools.filter(name => name !== "resolve").map(name => [name, allTools[name]] as const)
|
|
422
404
|
: [
|
|
423
405
|
...Object.entries(BUILTIN_TOOLS).filter(([name]) => isToolAllowed(name)),
|
|
424
|
-
...(
|
|
406
|
+
...(includeYield ? ([["yield", HIDDEN_TOOLS.yield]] as const) : []),
|
|
425
407
|
...([["exit_plan_mode", HIDDEN_TOOLS.exit_plan_mode]] as const),
|
|
426
408
|
];
|
|
427
409
|
|
|
@@ -16,8 +16,8 @@ import { ToolError } from "./tool-errors";
|
|
|
16
16
|
|
|
17
17
|
const inspectImageSchema = Type.Object(
|
|
18
18
|
{
|
|
19
|
-
path: Type.String({ description: "
|
|
20
|
-
question: Type.String({ description: "
|
|
19
|
+
path: Type.String({ description: "image path", examples: ["image.png"] }),
|
|
20
|
+
question: Type.String({ description: "question about image", examples: ["What is in this image?"] }),
|
|
21
21
|
},
|
|
22
22
|
{ additionalProperties: false },
|
|
23
23
|
);
|
|
@@ -43,7 +43,7 @@ export class InspectImageTool implements AgentTool<typeof inspectImageSchema, In
|
|
|
43
43
|
readonly label = "InspectImage";
|
|
44
44
|
readonly description: string;
|
|
45
45
|
readonly parameters = inspectImageSchema;
|
|
46
|
-
readonly strict =
|
|
46
|
+
readonly strict = false;
|
|
47
47
|
|
|
48
48
|
constructor(
|
|
49
49
|
private readonly session: ToolSession,
|
package/src/tools/json-tree.ts
CHANGED
|
@@ -13,17 +13,17 @@ export const JSON_TREE_MAX_LINES_EXPANDED = 200;
|
|
|
13
13
|
export const JSON_TREE_SCALAR_LEN_COLLAPSED = 60;
|
|
14
14
|
export const JSON_TREE_SCALAR_LEN_EXPANDED = 2000;
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return result;
|
|
16
|
+
const HIDDEN_ARG_KEYS = { [INTENT_FIELD]: 1, __partialJson: 1 };
|
|
17
|
+
|
|
18
|
+
const ARGS_INLINE_PAIR_SEP = ", ";
|
|
19
|
+
const ARGS_INLINE_PAIR_SEP_WIDTH = Bun.stringWidth(ARGS_INLINE_PAIR_SEP);
|
|
20
|
+
const ARGS_INLINE_MORE = "…";
|
|
21
|
+
const ARGS_INLINE_MORE_WIDTH = Bun.stringWidth(ARGS_INLINE_MORE);
|
|
22
|
+
|
|
23
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
24
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
26
25
|
}
|
|
26
|
+
|
|
27
27
|
/**
|
|
28
28
|
* Format a scalar value for inline display.
|
|
29
29
|
*/
|
|
@@ -49,40 +49,35 @@ export function formatScalar(value: unknown, maxLen: number): string {
|
|
|
49
49
|
* Format args inline for collapsed view.
|
|
50
50
|
*/
|
|
51
51
|
export function formatArgsInline(args: Record<string, unknown>, maxWidth: number): string {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
let totalLen = 0;
|
|
64
|
-
|
|
65
|
-
for (const [key, value] of entries) {
|
|
66
|
-
const valueStr = formatScalar(value, 24);
|
|
67
|
-
const pairStr = `${key}=${valueStr}`;
|
|
68
|
-
const addLen = pairs.length > 0 ? pairStr.length + 2 : pairStr.length;
|
|
69
|
-
|
|
70
|
-
if (totalLen + addLen > maxWidth && pairs.length > 0) {
|
|
71
|
-
pairs.push("…");
|
|
72
|
-
break;
|
|
52
|
+
let result = "";
|
|
53
|
+
let width = 0;
|
|
54
|
+
for (const key in args) {
|
|
55
|
+
if (key in HIDDEN_ARG_KEYS) continue;
|
|
56
|
+
const value = args[key];
|
|
57
|
+
const sep = width > 0 ? ARGS_INLINE_PAIR_SEP : "";
|
|
58
|
+
const sepW = width > 0 ? ARGS_INLINE_PAIR_SEP_WIDTH : 0;
|
|
59
|
+
const current = width + sepW;
|
|
60
|
+
const cap = maxWidth - current - ARGS_INLINE_MORE_WIDTH;
|
|
61
|
+
if (cap <= 0) {
|
|
62
|
+
return `${result}${ARGS_INLINE_MORE}`;
|
|
73
63
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
64
|
+
const valueMaxLen = Math.min(maxWidth - current, 24);
|
|
65
|
+
const valueStr = formatScalar(value, valueMaxLen);
|
|
66
|
+
const piece = `${key}=${valueStr}`;
|
|
67
|
+
const pieceW = Bun.stringWidth(piece);
|
|
68
|
+
if (pieceW > cap) {
|
|
69
|
+
return `${result}${sep}${truncateToWidth(piece, cap)}`;
|
|
70
|
+
}
|
|
71
|
+
result += sep + piece;
|
|
72
|
+
width = current + pieceW;
|
|
77
73
|
}
|
|
78
|
-
|
|
79
|
-
return pairs.join(", ");
|
|
74
|
+
return result;
|
|
80
75
|
}
|
|
81
76
|
|
|
82
77
|
/**
|
|
83
78
|
* Build tree prefix for nested rendering.
|
|
84
79
|
*/
|
|
85
|
-
function buildTreePrefix(ancestors: boolean[]
|
|
80
|
+
function buildTreePrefix(theme: Theme, ancestors: readonly boolean[]): string {
|
|
86
81
|
return ancestors.map(hasNext => (hasNext ? `${theme.tree.vertical} ` : " ")).join("");
|
|
87
82
|
}
|
|
88
83
|
|
|
@@ -119,109 +114,114 @@ export function renderJsonTreeLines(
|
|
|
119
114
|
}
|
|
120
115
|
|
|
121
116
|
const connector = isLast ? theme.tree.last : theme.tree.branch;
|
|
122
|
-
const prefix = `${buildTreePrefix(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
117
|
+
const prefix = `${buildTreePrefix(theme, ancestors)}${theme.fg("dim", connector)} `;
|
|
118
|
+
|
|
119
|
+
ancestors.push(!isLast);
|
|
120
|
+
try {
|
|
121
|
+
// Handle scalars
|
|
122
|
+
if (val === null || val === undefined || typeof val !== "object") {
|
|
123
|
+
const label = key ? theme.fg("muted", key) : theme.fg("muted", "value");
|
|
124
|
+
|
|
125
|
+
// Special handling for multiline strings
|
|
126
|
+
if (typeof val === "string" && val.includes("\n")) {
|
|
127
|
+
const strLines = val.split("\n");
|
|
128
|
+
const maxStrLines = Math.min(strLines.length, Math.max(1, maxLines - lines.length - 1));
|
|
129
|
+
const continuePrefix = buildTreePrefix(theme, ancestors);
|
|
130
|
+
|
|
131
|
+
// First line with label
|
|
132
|
+
const firstLine = truncateToWidth(strLines[0], maxScalarLen);
|
|
133
|
+
pushLine(`${prefix}${iconScalar} ${label}: ${theme.fg("dim", `"${firstLine}`)}`);
|
|
134
|
+
|
|
135
|
+
// Subsequent lines indented
|
|
136
|
+
for (let i = 1; i < maxStrLines; i++) {
|
|
137
|
+
if (lines.length >= maxLines) {
|
|
138
|
+
truncated = true;
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
const line = truncateToWidth(strLines[i], maxScalarLen);
|
|
142
|
+
pushLine(`${continuePrefix} ${theme.fg("dim", ` ${line}`)}`);
|
|
143
|
+
}
|
|
127
144
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
145
|
+
// Show truncation and closing quote
|
|
146
|
+
if (strLines.length > maxStrLines) {
|
|
147
|
+
truncated = true;
|
|
148
|
+
pushLine(
|
|
149
|
+
`${continuePrefix} ${theme.fg("dim", ` …(${strLines.length - maxStrLines} more lines)"`)}`,
|
|
150
|
+
);
|
|
151
|
+
} else {
|
|
152
|
+
// Add closing quote to last line - need to modify the last pushed line
|
|
153
|
+
const lastIdx = lines.length - 1;
|
|
154
|
+
lines[lastIdx] = `${lines[lastIdx]}${theme.fg("dim", '"')}`;
|
|
155
|
+
}
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
133
158
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
159
|
+
const scalar = formatScalar(val, maxScalarLen);
|
|
160
|
+
pushLine(`${prefix}${iconScalar} ${label}: ${theme.fg("dim", scalar)}`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
137
163
|
|
|
138
|
-
|
|
139
|
-
|
|
164
|
+
// Handle arrays
|
|
165
|
+
if (Array.isArray(val)) {
|
|
166
|
+
const header = key ? theme.fg("muted", key) : theme.fg("muted", "array");
|
|
167
|
+
pushLine(`${prefix}${iconArray} ${header}`);
|
|
168
|
+
if (val.length === 0) {
|
|
169
|
+
pushLine(
|
|
170
|
+
`${buildTreePrefix(theme, ancestors)}${theme.fg("dim", theme.tree.last)} ${theme.fg("dim", "[]")}`,
|
|
171
|
+
);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (depth >= maxDepth) {
|
|
175
|
+
pushLine(
|
|
176
|
+
`${buildTreePrefix(theme, ancestors)}${theme.fg("dim", theme.tree.last)} ${theme.fg("dim", "…")}`,
|
|
177
|
+
);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
for (let i = 0; i < val.length; i++) {
|
|
181
|
+
renderNode(val[i], `[${i}]`, ancestors, i === val.length - 1, depth + 1);
|
|
140
182
|
if (lines.length >= maxLines) {
|
|
141
183
|
truncated = true;
|
|
142
|
-
|
|
184
|
+
return;
|
|
143
185
|
}
|
|
144
|
-
const line = truncateToWidth(strLines[i], maxScalarLen);
|
|
145
|
-
pushLine(`${continuePrefix} ${theme.fg("dim", ` ${line}`)}`);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Show truncation and closing quote
|
|
149
|
-
if (strLines.length > maxStrLines) {
|
|
150
|
-
truncated = true;
|
|
151
|
-
pushLine(`${continuePrefix} ${theme.fg("dim", ` …(${strLines.length - maxStrLines} more lines)"`)}`);
|
|
152
|
-
} else {
|
|
153
|
-
// Add closing quote to last line - need to modify the last pushed line
|
|
154
|
-
const lastIdx = lines.length - 1;
|
|
155
|
-
lines[lastIdx] = `${lines[lastIdx]}${theme.fg("dim", '"')}`;
|
|
156
186
|
}
|
|
157
187
|
return;
|
|
158
188
|
}
|
|
159
189
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
190
|
+
// Handle objects
|
|
191
|
+
if (!isRecord(val)) return;
|
|
164
192
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (val.length === 0) {
|
|
170
|
-
pushLine(
|
|
171
|
-
`${buildTreePrefix([...ancestors, !isLast], theme)}${theme.fg("dim", theme.tree.last)} ${theme.fg("dim", "[]")}`,
|
|
172
|
-
);
|
|
193
|
+
const header = key ? theme.fg("muted", key) : theme.fg("muted", "object");
|
|
194
|
+
pushLine(`${prefix}${iconObject} ${header}`);
|
|
195
|
+
if (depth >= maxDepth) {
|
|
196
|
+
pushLine(`${buildTreePrefix(theme, ancestors)}${theme.fg("dim", theme.tree.last)} ${theme.fg("dim", "…")}`);
|
|
173
197
|
return;
|
|
174
198
|
}
|
|
175
|
-
|
|
199
|
+
const keys = Object.keys(val);
|
|
200
|
+
if (keys.length === 0) {
|
|
176
201
|
pushLine(
|
|
177
|
-
`${buildTreePrefix(
|
|
202
|
+
`${buildTreePrefix(theme, ancestors)}${theme.fg("dim", theme.tree.last)} ${theme.fg("dim", "{}")}`,
|
|
178
203
|
);
|
|
179
204
|
return;
|
|
180
205
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
206
|
+
for (let i = 0; i < keys.length; i++) {
|
|
207
|
+
const childKey = keys[i];
|
|
208
|
+
const child = val[childKey];
|
|
209
|
+
renderNode(child, childKey, ancestors, i === keys.length - 1, depth + 1);
|
|
184
210
|
if (lines.length >= maxLines) {
|
|
185
211
|
truncated = true;
|
|
186
212
|
return;
|
|
187
213
|
}
|
|
188
214
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
// Handle objects
|
|
193
|
-
const header = key ? theme.fg("muted", key) : theme.fg("muted", "object");
|
|
194
|
-
pushLine(`${prefix}${iconObject} ${header}`);
|
|
195
|
-
const entries = Object.entries(val as Record<string, unknown>);
|
|
196
|
-
if (entries.length === 0) {
|
|
197
|
-
pushLine(
|
|
198
|
-
`${buildTreePrefix([...ancestors, !isLast], theme)}${theme.fg("dim", theme.tree.last)} ${theme.fg("dim", "{}")}`,
|
|
199
|
-
);
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
if (depth >= maxDepth) {
|
|
203
|
-
pushLine(
|
|
204
|
-
`${buildTreePrefix([...ancestors, !isLast], theme)}${theme.fg("dim", theme.tree.last)} ${theme.fg("dim", "…")}`,
|
|
205
|
-
);
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
const nextAncestors = [...ancestors, !isLast];
|
|
209
|
-
for (let i = 0; i < entries.length; i++) {
|
|
210
|
-
const [childKey, child] = entries[i];
|
|
211
|
-
renderNode(child, childKey, nextAncestors, i === entries.length - 1, depth + 1);
|
|
212
|
-
if (lines.length >= maxLines) {
|
|
213
|
-
truncated = true;
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
215
|
+
} finally {
|
|
216
|
+
ancestors.pop();
|
|
216
217
|
}
|
|
217
218
|
};
|
|
218
219
|
|
|
219
220
|
// Render root level
|
|
220
|
-
if (
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
renderNode(child, childKey, [], i === entries.length - 1, 1);
|
|
221
|
+
if (isRecord(value)) {
|
|
222
|
+
for (const key in value) {
|
|
223
|
+
if (key in HIDDEN_ARG_KEYS) continue;
|
|
224
|
+
renderNode(value[key], key, [], true, 1);
|
|
225
225
|
if (lines.length >= maxLines) {
|
|
226
226
|
truncated = true;
|
|
227
227
|
break;
|
|
@@ -2,19 +2,20 @@ import { computeLineHash } from "../edit/line-hash";
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Format a single line of match output for grep/ast-grep style results.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
|
+
* Match lines use `>` as the anchor/content separator; context lines use `:`.
|
|
7
|
+
* In hashline mode the anchor is `LINE+ID` (no `#`); in plain mode it is
|
|
8
|
+
* just the line number. Line numbers are never padded.
|
|
6
9
|
*/
|
|
7
10
|
export function formatMatchLine(
|
|
8
11
|
lineNumber: number,
|
|
9
12
|
line: string,
|
|
10
13
|
isMatch: boolean,
|
|
11
|
-
options: { useHashLines: boolean
|
|
14
|
+
options: { useHashLines: boolean },
|
|
12
15
|
): string {
|
|
13
|
-
const separator = isMatch ? "
|
|
16
|
+
const separator = isMatch ? ">" : ":";
|
|
14
17
|
if (options.useHashLines) {
|
|
15
|
-
|
|
16
|
-
return `${ref}${separator}${line}`;
|
|
18
|
+
return `${lineNumber}${computeLineHash(lineNumber, line)}${separator}${line}`;
|
|
17
19
|
}
|
|
18
|
-
|
|
19
|
-
return `${padded}${separator}${line}`;
|
|
20
|
+
return `${lineNumber}${separator}${line}`;
|
|
20
21
|
}
|
package/src/tools/notebook.ts
CHANGED
|
@@ -13,14 +13,16 @@ import { formatCount, formatErrorMessage, PREVIEW_LIMITS } from "./render-utils"
|
|
|
13
13
|
|
|
14
14
|
const notebookSchema = Type.Object({
|
|
15
15
|
action: StringEnum(["edit", "insert", "delete"], {
|
|
16
|
-
description: "
|
|
16
|
+
description: "cell action",
|
|
17
|
+
examples: ["edit", "insert", "delete"],
|
|
17
18
|
}),
|
|
18
|
-
notebook_path: Type.String({ description: "
|
|
19
|
-
cell_index: Type.Number({ description: "
|
|
20
|
-
content: Type.Optional(Type.String({ description: "
|
|
19
|
+
notebook_path: Type.String({ description: "notebook path", examples: ["analysis.ipynb"] }),
|
|
20
|
+
cell_index: Type.Number({ description: "cell index", examples: [0, 1] }),
|
|
21
|
+
content: Type.Optional(Type.String({ description: "new cell content" })),
|
|
21
22
|
cell_type: Type.Optional(
|
|
22
23
|
StringEnum(["code", "markdown"], {
|
|
23
|
-
description: "
|
|
24
|
+
description: "cell type",
|
|
25
|
+
examples: ["code", "markdown"],
|
|
24
26
|
}),
|
|
25
27
|
),
|
|
26
28
|
});
|
|
@@ -62,8 +64,7 @@ type NotebookParams = Static<typeof notebookSchema>;
|
|
|
62
64
|
export class NotebookTool implements AgentTool<typeof notebookSchema, NotebookToolDetails> {
|
|
63
65
|
readonly name = "notebook";
|
|
64
66
|
readonly label = "Notebook";
|
|
65
|
-
readonly description =
|
|
66
|
-
"Edit, insert, or delete cells in Jupyter notebooks (.ipynb). cell_index is 0-based. Paths must be absolute.";
|
|
67
|
+
readonly description = "Edit, insert, or delete cells in Jupyter notebooks (.ipynb). cell_index is 0-based.";
|
|
67
68
|
readonly parameters = notebookSchema;
|
|
68
69
|
readonly strict = true;
|
|
69
70
|
readonly concurrency = "exclusive";
|
package/src/tools/poll-tool.ts
CHANGED
|
@@ -8,7 +8,8 @@ import type { ToolSession } from "./index";
|
|
|
8
8
|
const pollSchema = Type.Object({
|
|
9
9
|
jobs: Type.Optional(
|
|
10
10
|
Type.Array(Type.String(), {
|
|
11
|
-
description: "
|
|
11
|
+
description: "job ids to wait for",
|
|
12
|
+
examples: [["job-1234"]],
|
|
12
13
|
}),
|
|
13
14
|
),
|
|
14
15
|
});
|
package/src/tools/python.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type * as fs from "node:fs";
|
|
2
1
|
import * as path from "node:path";
|
|
3
2
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
4
3
|
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
@@ -16,7 +15,6 @@ import { DEFAULT_MAX_BYTES, OutputSink, type OutputSummary, TailBuffer } from ".
|
|
|
16
15
|
import { getTreeBranch, getTreeContinuePrefix, renderCodeCell } from "../tui";
|
|
17
16
|
import type { ToolSession } from ".";
|
|
18
17
|
import { formatStyledTruncationWarning, type OutputMeta } from "./output-meta";
|
|
19
|
-
import { resolveToCwd } from "./path-utils";
|
|
20
18
|
import { formatTitle, replaceTabs, shortenPath, truncateToWidth, wrapBrackets } from "./render-utils";
|
|
21
19
|
import { ToolAbortError, ToolError } from "./tool-errors";
|
|
22
20
|
import { toolResult } from "./tool-result";
|
|
@@ -47,14 +45,13 @@ function groupPreludeHelpers(helpers: PreludeHelper[]): PreludeCategory[] {
|
|
|
47
45
|
export const pythonSchema = Type.Object({
|
|
48
46
|
cells: Type.Array(
|
|
49
47
|
Type.Object({
|
|
50
|
-
code: Type.String({ description: "
|
|
51
|
-
title: Type.
|
|
48
|
+
code: Type.String({ description: "python code", examples: ["print('hello')", "import json"] }),
|
|
49
|
+
title: Type.String({ description: "cell label", examples: ["imports", "helper"] }),
|
|
52
50
|
}),
|
|
53
|
-
{ description: "
|
|
51
|
+
{ description: "cells to execute" },
|
|
54
52
|
),
|
|
55
|
-
timeout: Type.Optional(Type.Number({ description: "
|
|
56
|
-
|
|
57
|
-
reset: Type.Optional(Type.Boolean({ description: "Restart kernel before execution" })),
|
|
53
|
+
timeout: Type.Optional(Type.Number({ description: "timeout in seconds", default: 30 })),
|
|
54
|
+
reset: Type.Optional(Type.Boolean({ description: "restart kernel" })),
|
|
58
55
|
});
|
|
59
56
|
export type PythonToolParams = Static<typeof pythonSchema>;
|
|
60
57
|
|
|
@@ -177,7 +174,7 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
|
|
|
177
174
|
}
|
|
178
175
|
const session = this.session;
|
|
179
176
|
|
|
180
|
-
const { cells, timeout: rawTimeout = 30,
|
|
177
|
+
const { cells, timeout: rawTimeout = 30, reset } = params;
|
|
181
178
|
// Clamp to reasonable range: 1s - 600s (10 min)
|
|
182
179
|
const timeoutSec = clampTimeout("python", rawTimeout);
|
|
183
180
|
const timeoutMs = timeoutSec * 1000;
|
|
@@ -204,17 +201,6 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
|
|
|
204
201
|
}
|
|
205
202
|
session.assertPythonExecutionAllowed?.();
|
|
206
203
|
|
|
207
|
-
const commandCwd = cwd ? resolveToCwd(cwd, session.cwd) : session.cwd;
|
|
208
|
-
let cwdStat: fs.Stats;
|
|
209
|
-
try {
|
|
210
|
-
cwdStat = await Bun.file(commandCwd).stat();
|
|
211
|
-
} catch {
|
|
212
|
-
throw new ToolError(`Working directory does not exist: ${commandCwd}`);
|
|
213
|
-
}
|
|
214
|
-
if (!cwdStat.isDirectory()) {
|
|
215
|
-
throw new ToolError(`Working directory is not a directory: ${commandCwd}`);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
204
|
const tailBuffer = new TailBuffer(DEFAULT_MAX_BYTES * 2);
|
|
219
205
|
const jsonOutputs: unknown[] = [];
|
|
220
206
|
const images: ImageContent[] = [];
|
|
@@ -273,11 +259,11 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
|
|
|
273
259
|
pushUpdate();
|
|
274
260
|
},
|
|
275
261
|
});
|
|
276
|
-
const sessionId = sessionFile ? `session:${sessionFile}:cwd:${
|
|
262
|
+
const sessionId = sessionFile ? `session:${sessionFile}:cwd:${session.cwd}` : `cwd:${session.cwd}`;
|
|
277
263
|
|
|
278
264
|
if (getPreludeDocs().length === 0) {
|
|
279
265
|
const warmup = await warmPythonEnvironment(
|
|
280
|
-
|
|
266
|
+
session.cwd,
|
|
281
267
|
sessionId,
|
|
282
268
|
session.settings.get("python.sharedGateway"),
|
|
283
269
|
sessionFile ?? undefined,
|
|
@@ -292,7 +278,7 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
|
|
|
292
278
|
}
|
|
293
279
|
|
|
294
280
|
const baseExecutorOptions = {
|
|
295
|
-
cwd:
|
|
281
|
+
cwd: session.cwd,
|
|
296
282
|
deadlineMs,
|
|
297
283
|
signal: combinedSignal,
|
|
298
284
|
sessionId,
|