@oh-my-pi/pi-coding-agent 14.7.2 → 14.7.4

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/package.json +7 -7
  3. package/src/cli/read-cli.ts +1 -2
  4. package/src/commands/read.ts +2 -7
  5. package/src/config/settings-schema.ts +0 -5
  6. package/src/edit/modes/hashline.ts +40 -19
  7. package/src/edit/modes/patch.ts +7 -5
  8. package/src/edit/modes/replace.ts +6 -2
  9. package/src/edit/notebook.ts +222 -0
  10. package/src/edit/read-file.ts +7 -0
  11. package/src/edit/renderer.ts +4 -3
  12. package/src/edit/streaming.ts +49 -7
  13. package/src/modes/components/diff.ts +54 -7
  14. package/src/modes/components/tool-execution.ts +3 -29
  15. package/src/prompts/agents/designer.md +1 -2
  16. package/src/prompts/agents/explore.md +2 -5
  17. package/src/prompts/agents/init.md +1 -4
  18. package/src/prompts/agents/librarian.md +1 -3
  19. package/src/prompts/agents/plan.md +7 -8
  20. package/src/prompts/agents/reviewer.md +1 -2
  21. package/src/prompts/ci-green-request.md +10 -10
  22. package/src/prompts/commands/orchestrate.md +48 -0
  23. package/src/prompts/memories/consolidation.md +10 -10
  24. package/src/prompts/memories/read-path.md +6 -6
  25. package/src/prompts/system/agent-creation-architect.md +54 -44
  26. package/src/prompts/system/custom-system-prompt.md +3 -5
  27. package/src/prompts/system/eager-todo.md +4 -4
  28. package/src/prompts/system/handoff-document.md +7 -4
  29. package/src/prompts/system/plan-mode-active.md +7 -3
  30. package/src/prompts/system/plan-mode-approved.md +5 -5
  31. package/src/prompts/system/summarization-system.md +2 -2
  32. package/src/prompts/system/system-prompt.md +53 -65
  33. package/src/prompts/system/title-system.md +2 -2
  34. package/src/prompts/system/web-search.md +16 -19
  35. package/src/prompts/tools/bash.md +8 -8
  36. package/src/prompts/tools/browser.md +4 -4
  37. package/src/prompts/tools/debug.md +3 -1
  38. package/src/prompts/tools/eval.md +13 -9
  39. package/src/prompts/tools/hashline.md +4 -2
  40. package/src/prompts/tools/image-gen.md +1 -1
  41. package/src/prompts/tools/read.md +1 -2
  42. package/src/prompts/tools/reflect.md +3 -3
  43. package/src/prompts/tools/render-mermaid.md +2 -2
  44. package/src/prompts/tools/resolve.md +2 -2
  45. package/src/prompts/tools/retain.md +3 -2
  46. package/src/prompts/tools/rewind.md +2 -2
  47. package/src/prompts/tools/search-tool-bm25.md +3 -4
  48. package/src/prompts/tools/task.md +1 -1
  49. package/src/prompts/tools/todo-write.md +2 -2
  50. package/src/task/commands.ts +5 -1
  51. package/src/tools/fetch.ts +6 -7
  52. package/src/tools/index.ts +0 -4
  53. package/src/tools/read.ts +18 -7
  54. package/src/tools/renderers.ts +0 -2
  55. package/src/tools/write.ts +41 -26
  56. package/src/tools/notebook.ts +0 -286
@@ -1,286 +0,0 @@
1
- import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
2
- import { StringEnum } from "@oh-my-pi/pi-ai";
3
- import type { Component } from "@oh-my-pi/pi-tui";
4
- import { Text } from "@oh-my-pi/pi-tui";
5
- import { isEnoent, untilAborted } from "@oh-my-pi/pi-utils";
6
- import { type Static, Type } from "@sinclair/typebox";
7
- import type { RenderResultOptions } from "../extensibility/custom-tools/types";
8
- import type { Theme } from "../modes/theme/theme";
9
- import type { ToolSession } from "../sdk";
10
- import { Hasher, type RenderCache, renderCodeCell, renderStatusLine } from "../tui";
11
- import { resolveToCwd } from "./path-utils";
12
- import { formatCount, formatErrorMessage, PREVIEW_LIMITS } from "./render-utils";
13
-
14
- const notebookSchema = Type.Object({
15
- action: StringEnum(["edit", "insert", "delete"], {
16
- description: "cell action",
17
- examples: ["edit", "insert", "delete"],
18
- }),
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" })),
22
- cell_type: Type.Optional(
23
- StringEnum(["code", "markdown"], {
24
- description: "cell type",
25
- examples: ["code", "markdown"],
26
- }),
27
- ),
28
- });
29
-
30
- export interface NotebookToolDetails {
31
- /** Action performed */
32
- action: "edit" | "insert" | "delete";
33
- /** Cell index operated on */
34
- cellIndex: number;
35
- /** Cell type */
36
- cellType?: string;
37
- /** Total cell count after operation */
38
- totalCells: number;
39
- /** Cell content lines after operation (or removed content for delete) */
40
- cellSource?: string[];
41
- }
42
-
43
- interface NotebookCell {
44
- cell_type: "code" | "markdown" | "raw";
45
- source: string[];
46
- metadata: Record<string, unknown>;
47
- execution_count?: number | null;
48
- outputs?: unknown[];
49
- }
50
-
51
- interface Notebook {
52
- cells: NotebookCell[];
53
- metadata: Record<string, unknown>;
54
- nbformat: number;
55
- nbformat_minor: number;
56
- }
57
-
58
- function splitIntoLines(content: string): string[] {
59
- return content.split("\n").map((line, i, arr) => (i < arr.length - 1 ? `${line}\n` : line));
60
- }
61
-
62
- type NotebookParams = Static<typeof notebookSchema>;
63
-
64
- export class NotebookTool implements AgentTool<typeof notebookSchema, NotebookToolDetails> {
65
- readonly name = "notebook";
66
- readonly label = "Notebook";
67
- readonly loadMode = "discoverable";
68
- readonly summary = "Read and execute Jupyter notebooks";
69
- readonly description = "Edit, insert, or delete cells in Jupyter notebooks (.ipynb). cell_index is 0-based.";
70
- readonly parameters = notebookSchema;
71
- readonly strict = true;
72
- readonly concurrency = "exclusive";
73
-
74
- constructor(private readonly session: ToolSession) {}
75
-
76
- async execute(
77
- _toolCallId: string,
78
- params: NotebookParams,
79
- signal?: AbortSignal,
80
- _onUpdate?: AgentToolUpdateCallback<NotebookToolDetails>,
81
- _context?: AgentToolContext,
82
- ): Promise<AgentToolResult<NotebookToolDetails>> {
83
- const { action, notebook_path, cell_index, content, cell_type } = params;
84
- const absolutePath = resolveToCwd(notebook_path, this.session.cwd);
85
-
86
- return untilAborted(signal, async () => {
87
- // Read and parse notebook
88
- let notebook: Notebook;
89
- try {
90
- notebook = await Bun.file(absolutePath).json();
91
- } catch (err) {
92
- if (isEnoent(err)) throw new Error(`Notebook not found: ${notebook_path}`);
93
- throw new Error(`Invalid JSON in notebook: ${notebook_path}`);
94
- }
95
-
96
- // Validate notebook structure
97
- if (!notebook.cells || !Array.isArray(notebook.cells)) {
98
- throw new Error(`Invalid notebook structure (missing cells array): ${notebook_path}`);
99
- }
100
-
101
- const cellCount = notebook.cells.length;
102
-
103
- // Validate cell_index based on action
104
- if (action === "insert") {
105
- if (cell_index < 0 || cell_index > cellCount) {
106
- throw new Error(`Cell index ${cell_index} out of range for insert (0-${cellCount}) in ${notebook_path}`);
107
- }
108
- } else {
109
- if (cell_index < 0 || cell_index >= cellCount) {
110
- throw new Error(`Cell index ${cell_index} out of range (0-${cellCount - 1}) in ${notebook_path}`);
111
- }
112
- }
113
-
114
- // Validate content for edit/insert
115
- if ((action === "edit" || action === "insert") && content === undefined) {
116
- throw new Error(`Content is required for ${action} action`);
117
- }
118
-
119
- // Perform the action
120
- let resultMessage: string;
121
- let finalCellType: string | undefined;
122
- let cellSource: string[] | undefined;
123
-
124
- switch (action) {
125
- case "edit": {
126
- const sourceLines = splitIntoLines(content!);
127
- notebook.cells[cell_index].source = sourceLines;
128
- finalCellType = notebook.cells[cell_index].cell_type;
129
- cellSource = sourceLines;
130
- resultMessage = `Replaced cell ${cell_index} (${finalCellType})`;
131
- break;
132
- }
133
- case "insert": {
134
- const sourceLines = splitIntoLines(content!);
135
- const newCellType = (cell_type as "code" | "markdown") || "code";
136
- const newCell: NotebookCell = {
137
- cell_type: newCellType,
138
- source: sourceLines,
139
- metadata: {},
140
- };
141
- if (newCellType === "code") {
142
- newCell.execution_count = null;
143
- newCell.outputs = [];
144
- }
145
- notebook.cells.splice(cell_index, 0, newCell);
146
- finalCellType = newCellType;
147
- cellSource = sourceLines;
148
- resultMessage = `Inserted ${newCellType} cell at position ${cell_index}`;
149
- break;
150
- }
151
- case "delete": {
152
- const removedCell = notebook.cells[cell_index];
153
- finalCellType = removedCell.cell_type;
154
- cellSource = removedCell.source;
155
- notebook.cells.splice(cell_index, 1);
156
- resultMessage = `Deleted cell ${cell_index} (${finalCellType})`;
157
- break;
158
- }
159
- default: {
160
- throw new Error(`Invalid action: ${action}`);
161
- }
162
- }
163
-
164
- // Write back with single-space indentation
165
- await Bun.write(absolutePath, JSON.stringify(notebook, null, 1));
166
-
167
- const newCellCount = notebook.cells.length;
168
- return {
169
- content: [
170
- {
171
- type: "text",
172
- text: `${resultMessage}. Notebook now has ${newCellCount} cells.`,
173
- },
174
- ],
175
- details: {
176
- action: action as "edit" | "insert" | "delete",
177
- cellIndex: cell_index,
178
- cellType: finalCellType,
179
- totalCells: newCellCount,
180
- cellSource,
181
- },
182
- };
183
- });
184
- }
185
- }
186
-
187
- // =============================================================================
188
- // TUI Renderer
189
- // =============================================================================
190
-
191
- interface NotebookRenderArgs {
192
- action: string;
193
- notebookPath?: string;
194
- notebook_path?: string;
195
- cellNumber?: number;
196
- cell_index?: number;
197
- cellType?: string;
198
- cell_type?: string;
199
- content?: string;
200
- }
201
-
202
- const COLLAPSED_TEXT_LIMIT = PREVIEW_LIMITS.COLLAPSED_LINES * 2;
203
-
204
- export const notebookToolRenderer = {
205
- renderCall(args: NotebookRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
206
- const meta: string[] = [];
207
- const notebookPath = args.notebookPath ?? args.notebook_path;
208
- const cellNumber = args.cellNumber ?? args.cell_index;
209
- const cellType = args.cellType ?? args.cell_type;
210
- meta.push(`in ${notebookPath || "?"}`);
211
- if (cellNumber !== undefined) meta.push(`cell:${cellNumber}`);
212
- if (cellType) meta.push(`type:${cellType}`);
213
-
214
- const text = renderStatusLine(
215
- { icon: "pending", title: "Notebook", description: args.action || "?", meta },
216
- uiTheme,
217
- );
218
- return new Text(text, 0, 0);
219
- },
220
-
221
- renderResult(
222
- result: { content: Array<{ type: string; text?: string }>; details?: NotebookToolDetails },
223
- options: RenderResultOptions,
224
- uiTheme: Theme,
225
- args?: NotebookRenderArgs,
226
- ): Component {
227
- const content = result.content?.[0];
228
- if (content?.type === "text" && content.text?.startsWith("Error:")) {
229
- const notebookPath = args?.notebookPath ?? args?.notebook_path ?? "?";
230
- const header = renderStatusLine({ icon: "error", title: "Notebook", description: notebookPath }, uiTheme);
231
- return new Text([header, formatErrorMessage(content.text, uiTheme)].join("\n"), 0, 0);
232
- }
233
-
234
- const details = result.details;
235
- const action = details?.action ?? "edit";
236
- const cellIndex = details?.cellIndex;
237
- const cellType = details?.cellType;
238
- const totalCells = details?.totalCells;
239
- const cellSource = details?.cellSource ?? [];
240
- const lineCount = cellSource.length;
241
-
242
- const actionLabel = action === "insert" ? "Inserted" : action === "delete" ? "Deleted" : "Edited";
243
- const cellLabel = cellType || "cell";
244
- const summaryParts = [`${actionLabel} ${cellLabel} ${cellIndex ?? "?"}`];
245
- if (lineCount > 0) summaryParts.push(formatCount("line", lineCount));
246
- if (totalCells !== undefined) summaryParts.push(`${totalCells} total`);
247
-
248
- const outputLines = summaryParts.map(part => uiTheme.fg("dim", part));
249
- const codeText = cellSource.join("");
250
- const language = cellType === "markdown" ? "markdown" : undefined;
251
-
252
- const notebookPath = args?.notebookPath ?? args?.notebook_path;
253
- const notebookLabel = notebookPath ? `${actionLabel} ${notebookPath}` : "Notebook";
254
- let cached: RenderCache | undefined;
255
-
256
- return {
257
- render: (width: number): string[] => {
258
- // REACTIVE: read mutable options at render time
259
- const { expanded } = options;
260
- const key = new Hasher().bool(expanded).u32(width).digest();
261
- if (cached?.key === key) return cached.lines;
262
-
263
- const lines = renderCodeCell(
264
- {
265
- code: codeText,
266
- language,
267
- title: notebookLabel,
268
- status: "complete",
269
- output: outputLines.join("\n"),
270
- codeMaxLines: expanded ? Number.POSITIVE_INFINITY : COLLAPSED_TEXT_LIMIT,
271
- expanded,
272
- width,
273
- },
274
- uiTheme,
275
- );
276
-
277
- cached = { key, lines };
278
- return lines;
279
- },
280
- invalidate: () => {
281
- cached = undefined;
282
- },
283
- };
284
- },
285
- mergeCallAndResult: true,
286
- };