@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.
Files changed (120) hide show
  1. package/CHANGELOG.md +98 -1
  2. package/package.json +7 -7
  3. package/src/autoresearch/prompt.md +1 -1
  4. package/src/commit/agentic/prompts/analyze-file.md +1 -1
  5. package/src/config/model-registry.ts +67 -15
  6. package/src/config/prompt-templates.ts +5 -5
  7. package/src/config/settings-schema.ts +4 -4
  8. package/src/cursor.ts +3 -8
  9. package/src/discovery/helpers.ts +3 -3
  10. package/src/edit/diff.ts +50 -47
  11. package/src/edit/index.ts +86 -57
  12. package/src/edit/line-hash.ts +743 -24
  13. package/src/edit/modes/apply-patch.ts +0 -9
  14. package/src/edit/modes/atom.ts +893 -0
  15. package/src/edit/modes/chunk.ts +14 -24
  16. package/src/edit/modes/hashline.ts +193 -146
  17. package/src/edit/modes/patch.ts +5 -9
  18. package/src/edit/modes/replace.ts +6 -11
  19. package/src/edit/renderer.ts +14 -10
  20. package/src/edit/streaming.ts +50 -16
  21. package/src/exec/bash-executor.ts +2 -4
  22. package/src/export/html/template.generated.ts +1 -1
  23. package/src/export/html/template.js +4 -12
  24. package/src/extensibility/custom-tools/types.ts +2 -0
  25. package/src/extensibility/custom-tools/wrapper.ts +2 -1
  26. package/src/internal-urls/docs-index.generated.ts +2 -2
  27. package/src/lsp/defaults.json +142 -652
  28. package/src/lsp/index.ts +1 -1
  29. package/src/mcp/render.ts +1 -8
  30. package/src/modes/components/assistant-message.ts +4 -0
  31. package/src/modes/components/diff.ts +23 -14
  32. package/src/modes/components/footer.ts +21 -16
  33. package/src/modes/components/session-selector.ts +3 -3
  34. package/src/modes/components/settings-defs.ts +6 -1
  35. package/src/modes/components/todo-reminder.ts +1 -8
  36. package/src/modes/components/tool-execution.ts +1 -4
  37. package/src/modes/controllers/selector-controller.ts +1 -1
  38. package/src/modes/print-mode.ts +8 -0
  39. package/src/prompts/agents/librarian.md +1 -1
  40. package/src/prompts/agents/reviewer.md +4 -4
  41. package/src/prompts/ci-green-request.md +1 -1
  42. package/src/prompts/review-request.md +1 -1
  43. package/src/prompts/system/subagent-system-prompt.md +3 -3
  44. package/src/prompts/system/subagent-yield-reminder.md +11 -0
  45. package/src/prompts/system/system-prompt.md +3 -0
  46. package/src/prompts/tools/ask.md +3 -2
  47. package/src/prompts/tools/ast-edit.md +16 -20
  48. package/src/prompts/tools/ast-grep.md +19 -24
  49. package/src/prompts/tools/atom.md +87 -0
  50. package/src/prompts/tools/chunk-edit.md +37 -161
  51. package/src/prompts/tools/debug.md +4 -5
  52. package/src/prompts/tools/exit-plan-mode.md +4 -5
  53. package/src/prompts/tools/find.md +4 -8
  54. package/src/prompts/tools/github.md +18 -0
  55. package/src/prompts/tools/grep.md +4 -5
  56. package/src/prompts/tools/hashline.md +22 -89
  57. package/src/prompts/tools/{gemini-image.md → image-gen.md} +1 -1
  58. package/src/prompts/tools/inspect-image.md +6 -6
  59. package/src/prompts/tools/lsp.md +1 -1
  60. package/src/prompts/tools/patch.md +12 -19
  61. package/src/prompts/tools/python.md +3 -2
  62. package/src/prompts/tools/read-chunk.md +2 -3
  63. package/src/prompts/tools/read.md +2 -2
  64. package/src/prompts/tools/ssh.md +8 -17
  65. package/src/prompts/tools/todo-write.md +54 -41
  66. package/src/sdk.ts +14 -9
  67. package/src/session/agent-session.ts +25 -2
  68. package/src/session/session-manager.ts +4 -1
  69. package/src/task/executor.ts +43 -48
  70. package/src/task/render.ts +11 -13
  71. package/src/tools/ask.ts +7 -7
  72. package/src/tools/ast-edit.ts +45 -41
  73. package/src/tools/ast-grep.ts +77 -85
  74. package/src/tools/bash.ts +8 -9
  75. package/src/tools/browser.ts +32 -30
  76. package/src/tools/calculator.ts +4 -4
  77. package/src/tools/cancel-job.ts +1 -1
  78. package/src/tools/checkpoint.ts +2 -2
  79. package/src/tools/debug.ts +41 -37
  80. package/src/tools/exit-plan-mode.ts +1 -1
  81. package/src/tools/find.ts +4 -4
  82. package/src/tools/gh-renderer.ts +12 -4
  83. package/src/tools/gh.ts +509 -697
  84. package/src/tools/grep.ts +116 -131
  85. package/src/tools/{gemini-image.ts → image-gen.ts} +459 -60
  86. package/src/tools/index.ts +14 -32
  87. package/src/tools/inspect-image.ts +3 -3
  88. package/src/tools/json-tree.ts +114 -114
  89. package/src/tools/match-line-format.ts +8 -7
  90. package/src/tools/notebook.ts +8 -7
  91. package/src/tools/poll-tool.ts +2 -1
  92. package/src/tools/python.ts +9 -23
  93. package/src/tools/read.ts +32 -25
  94. package/src/tools/render-mermaid.ts +1 -1
  95. package/src/tools/render-utils.ts +18 -0
  96. package/src/tools/renderers.ts +2 -2
  97. package/src/tools/report-tool-issue.ts +3 -2
  98. package/src/tools/resolve.ts +1 -1
  99. package/src/tools/review.ts +12 -10
  100. package/src/tools/search-tool-bm25.ts +2 -4
  101. package/src/tools/ssh.ts +4 -4
  102. package/src/tools/todo-write.ts +172 -147
  103. package/src/tools/vim.ts +14 -15
  104. package/src/tools/write.ts +4 -4
  105. package/src/tools/{submit-result.ts → yield.ts} +11 -13
  106. package/src/utils/edit-mode.ts +2 -1
  107. package/src/utils/file-display-mode.ts +10 -5
  108. package/src/utils/git.ts +9 -5
  109. package/src/utils/shell-snapshot.ts +2 -3
  110. package/src/vim/render.ts +4 -4
  111. package/src/prompts/system/subagent-submit-reminder.md +0 -11
  112. package/src/prompts/tools/gh-issue-view.md +0 -11
  113. package/src/prompts/tools/gh-pr-checkout.md +0 -12
  114. package/src/prompts/tools/gh-pr-diff.md +0 -12
  115. package/src/prompts/tools/gh-pr-push.md +0 -12
  116. package/src/prompts/tools/gh-pr-view.md +0 -11
  117. package/src/prompts/tools/gh-repo-view.md +0 -11
  118. package/src/prompts/tools/gh-run-watch.md +0 -12
  119. package/src/prompts/tools/gh-search-issues.md +0 -11
  120. package/src/prompts/tools/gh-search-prs.md +0 -11
package/src/edit/index.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  writethroughNoop,
10
10
  } from "../lsp";
11
11
  import applyPatchDescription from "../prompts/tools/apply-patch.md" with { type: "text" };
12
+ import atomDescription from "../prompts/tools/atom.md" with { type: "text" };
12
13
  import chunkEditDescription from "../prompts/tools/chunk-edit.md" with { type: "text" };
13
14
  import hashlineDescription from "../prompts/tools/hashline.md" with { type: "text" };
14
15
  import patchDescription from "../prompts/tools/patch.md" with { type: "text" };
@@ -17,44 +18,33 @@ import type { ToolSession } from "../tools";
17
18
  import { VimTool, vimSchema } from "../tools/vim";
18
19
  import { type EditMode, normalizeEditMode, resolveEditMode } from "../utils/edit-mode";
19
20
  import type { VimToolDetails } from "../vim/types";
20
- import {
21
- type ApplyPatchParams,
22
- applyPatchSchema,
23
- expandApplyPatchToEntries,
24
- isApplyPatchParams,
25
- } from "./modes/apply-patch";
21
+ import { type ApplyPatchParams, applyPatchSchema, expandApplyPatchToEntries } from "./modes/apply-patch";
26
22
  import applyPatchGrammar from "./modes/apply-patch.lark" with { type: "text" };
23
+ import {
24
+ type AtomParams,
25
+ type AtomToolEdit,
26
+ atomEditParamsSchema,
27
+ executeAtomSingle,
28
+ resolveAtomEntryPaths,
29
+ } from "./modes/atom";
27
30
  import {
28
31
  type ChunkParams,
29
32
  type ChunkToolEdit,
30
33
  chunkEditParamsSchema,
31
34
  executeChunkSingle,
32
- isChunkParams,
33
35
  parseChunkEditPath,
34
36
  resolveAnchorStyle,
35
37
  resolveChunkAutoIndent,
36
38
  } from "./modes/chunk";
37
39
  import {
38
40
  executeHashlineSingle,
41
+ HashlineMismatchError,
39
42
  type HashlineParams,
40
43
  type HashlineToolEdit,
41
44
  hashlineEditParamsSchema,
42
- isHashlineParams,
43
45
  } from "./modes/hashline";
44
- import {
45
- executePatchSingle,
46
- isPatchParams,
47
- type PatchEditEntry,
48
- type PatchParams,
49
- patchEditSchema,
50
- } from "./modes/patch";
51
- import {
52
- executeReplaceSingle,
53
- isReplaceParams,
54
- type ReplaceEditEntry,
55
- type ReplaceParams,
56
- replaceEditSchema,
57
- } from "./modes/replace";
46
+ import { executePatchSingle, type PatchEditEntry, type PatchParams, patchEditSchema } from "./modes/patch";
47
+ import { executeReplaceSingle, type ReplaceEditEntry, type ReplaceParams, replaceEditSchema } from "./modes/replace";
58
48
  import { type EditToolDetails, type EditToolPerFileResult, getLspBatchRequest, type LspBatchRequest } from "./renderer";
59
49
 
60
50
  export { DEFAULT_EDIT_MODE, type EditMode, normalizeEditMode } from "../utils/edit-mode";
@@ -62,6 +52,7 @@ export * from "./apply-patch";
62
52
  export * from "./diff";
63
53
  export * from "./line-hash";
64
54
  export * from "./modes/apply-patch";
55
+ export * from "./modes/atom";
65
56
  export * from "./modes/chunk";
66
57
  export * from "./modes/hashline";
67
58
  export * from "./modes/patch";
@@ -74,19 +65,25 @@ type TInput =
74
65
  | typeof replaceEditSchema
75
66
  | typeof patchEditSchema
76
67
  | typeof hashlineEditParamsSchema
68
+ | typeof atomEditParamsSchema
77
69
  | typeof chunkEditParamsSchema
78
70
  | typeof vimSchema
79
71
  | typeof applyPatchSchema;
80
72
 
81
73
  type VimParams = Static<typeof vimSchema>;
82
- type EditParams = ReplaceParams | PatchParams | HashlineParams | ChunkParams | VimParams | ApplyPatchParams;
74
+ type EditParams =
75
+ | ReplaceParams
76
+ | PatchParams
77
+ | HashlineParams
78
+ | AtomParams
79
+ | ChunkParams
80
+ | VimParams
81
+ | ApplyPatchParams;
83
82
  type EditToolResultDetails = EditToolDetails | VimToolDetails;
84
83
 
85
84
  type EditModeDefinition = {
86
85
  description: (session: ToolSession) => string;
87
86
  parameters: TInput;
88
- invalidParamsMessage: string;
89
- validate: (params: EditParams) => boolean;
90
87
  execute: (
91
88
  tool: EditTool,
92
89
  params: EditParams,
@@ -109,10 +106,6 @@ function resolveConfiguredEditMode(rawEditMode: string): EditMode | undefined {
109
106
  return editMode;
110
107
  }
111
108
 
112
- function isVimParams(params: EditParams): params is VimParams {
113
- return typeof params === "object" && params !== null && "file" in params && typeof params.file === "string";
114
- }
115
-
116
109
  function resolveAllowFuzzy(session: ToolSession, rawValue: string): boolean {
117
110
  switch (rawValue) {
118
111
  case "true":
@@ -148,6 +141,25 @@ function createEditWritethrough(session: ToolSession): WritethroughCallback {
148
141
  return enableLsp ? createLspWritethrough(session.cwd, { enableFormat, enableDiagnostics }) : writethroughNoop;
149
142
  }
150
143
 
144
+ /**
145
+ * Resolve per-entry `path` against an optional top-level `path` default.
146
+ * If both are absent on an entry, throws a descriptive error.
147
+ */
148
+ function resolveEntryPaths<T extends { path?: string }>(
149
+ edits: readonly T[],
150
+ topLevelPath: string | undefined,
151
+ ): (T & { path: string })[] {
152
+ return edits.map((edit, i) => {
153
+ const path = (edit && typeof edit.path === "string" && edit.path) || topLevelPath;
154
+ if (!path) {
155
+ throw new Error(
156
+ `Edit ${i}: missing \`path\`. Provide \`path\` on this edit or supply a top-level \`path\` for the request.`,
157
+ );
158
+ }
159
+ return { ...edit, path };
160
+ });
161
+ }
162
+
151
163
  /** Group items by a key, preserving insertion order. */
152
164
  function groupBy<T, K>(items: T[], key: (item: T) => K): Map<K, T[]> {
153
165
  const map = new Map<K, T[]>();
@@ -203,7 +215,8 @@ async function executePerFile(
203
215
  if (text) contentTexts.push(text);
204
216
  } catch (err) {
205
217
  const errorText = err instanceof Error ? err.message : String(err);
206
- perFileResults.push({ path, diff: "", isError: true, errorText });
218
+ const displayErrorText = err instanceof HashlineMismatchError ? err.displayMessage : undefined;
219
+ perFileResults.push({ path, diff: "", isError: true, errorText, displayErrorText });
207
220
  contentTexts.push(`Error editing ${path}: ${errorText}`);
208
221
  }
209
222
 
@@ -307,10 +320,6 @@ export class EditTool implements AgentTool<TInput> {
307
320
  context?: AgentToolContext,
308
321
  ): Promise<AgentToolResult<EditToolResultDetails, TInput>> {
309
322
  const modeDefinition = this.#getModeDefinition();
310
- if (!modeDefinition.validate(params)) {
311
- throw new Error(modeDefinition.invalidParamsMessage);
312
- }
313
-
314
323
  return modeDefinition.execute(this, params, signal, getLspBatchRequest(context?.toolCall), onUpdate);
315
324
  }
316
325
 
@@ -323,9 +332,6 @@ export class EditTool implements AgentTool<TInput> {
323
332
  chunkAutoIndent: resolveChunkAutoIndent(),
324
333
  }),
325
334
  parameters: chunkEditParamsSchema,
326
- invalidParamsMessage:
327
- "Invalid edit parameters for chunk mode. Expected `{ edits: [{ path: 'file:selector', ...op }, ...] }` with at least one edit. Each edit needs a `path`; supply exactly one of `write: 'content'`, `insert: { loc, body }`, or `delete: true`.",
328
- validate: isChunkParams,
329
335
  execute: (
330
336
  tool: EditTool,
331
337
  params: EditParams,
@@ -333,8 +339,9 @@ export class EditTool implements AgentTool<TInput> {
333
339
  batchRequest: LspBatchRequest | undefined,
334
340
  onUpdate?: (partialResult: AgentToolResult<EditToolDetails, TInput>) => void,
335
341
  ) => {
336
- const { edits } = params as ChunkParams;
337
- const byFile = groupBy(edits, (e: ChunkToolEdit) => parseChunkEditPath(e.path).filePath);
342
+ const { edits, path: topPath } = params as ChunkParams & { path?: string };
343
+ const resolved = resolveEntryPaths(edits as ChunkToolEdit[], topPath);
344
+ const byFile = groupBy(resolved, (e: ChunkToolEdit) => parseChunkEditPath(e.path).filePath);
338
345
  const entries = [...byFile.entries()].map(([filePath, fileEdits]) => ({
339
346
  path: filePath,
340
347
  run: (br: LspBatchRequest | undefined) =>
@@ -354,8 +361,6 @@ export class EditTool implements AgentTool<TInput> {
354
361
  patch: {
355
362
  description: () => prompt.render(patchDescription),
356
363
  parameters: patchEditSchema,
357
- invalidParamsMessage: "Invalid edit parameters for patch mode.",
358
- validate: isPatchParams,
359
364
  execute: (
360
365
  tool: EditTool,
361
366
  params: EditParams,
@@ -363,8 +368,9 @@ export class EditTool implements AgentTool<TInput> {
363
368
  batchRequest: LspBatchRequest | undefined,
364
369
  onUpdate?: (partialResult: AgentToolResult<EditToolDetails, TInput>) => void,
365
370
  ) => {
366
- const { edits } = params as PatchParams;
367
- const entries = edits.map((entry: PatchEditEntry) => ({
371
+ const { edits, path: topPath } = params as PatchParams & { path?: string };
372
+ const resolved = resolveEntryPaths(edits as PatchEditEntry[], topPath);
373
+ const entries = resolved.map(entry => ({
368
374
  path: entry.path,
369
375
  run: (br: LspBatchRequest | undefined) =>
370
376
  executePatchSingle({
@@ -384,8 +390,6 @@ export class EditTool implements AgentTool<TInput> {
384
390
  apply_patch: {
385
391
  description: () => prompt.render(applyPatchDescription),
386
392
  parameters: applyPatchSchema,
387
- invalidParamsMessage: "Invalid edit parameters for apply_patch mode.",
388
- validate: isApplyPatchParams,
389
393
  execute: (
390
394
  tool: EditTool,
391
395
  params: EditParams,
@@ -394,8 +398,8 @@ export class EditTool implements AgentTool<TInput> {
394
398
  onUpdate?: (partialResult: AgentToolResult<EditToolDetails, TInput>) => void,
395
399
  ) => {
396
400
  const entries = expandApplyPatchToEntries(params as ApplyPatchParams);
397
- const perFile = entries.map((entry: PatchEditEntry) => ({
398
- path: entry.path,
401
+ const perFile = entries.map(entry => ({
402
+ path: entry.path!,
399
403
  run: (br: LspBatchRequest | undefined) =>
400
404
  executePatchSingle({
401
405
  session: tool.session,
@@ -414,8 +418,6 @@ export class EditTool implements AgentTool<TInput> {
414
418
  hashline: {
415
419
  description: () => prompt.render(hashlineDescription),
416
420
  parameters: hashlineEditParamsSchema,
417
- invalidParamsMessage: "Invalid edit parameters for hashline mode.",
418
- validate: isHashlineParams,
419
421
  execute: (
420
422
  tool: EditTool,
421
423
  params: EditParams,
@@ -423,8 +425,9 @@ export class EditTool implements AgentTool<TInput> {
423
425
  batchRequest: LspBatchRequest | undefined,
424
426
  onUpdate?: (partialResult: AgentToolResult<EditToolDetails, TInput>) => void,
425
427
  ) => {
426
- const { edits } = params as HashlineParams;
427
- const byFile = groupBy(edits, (e: HashlineToolEdit) => e.path);
428
+ const { edits, path: topPath } = params as HashlineParams & { path?: string };
429
+ const resolved = resolveEntryPaths(edits as HashlineToolEdit[], topPath);
430
+ const byFile = groupBy(resolved, e => e.path);
428
431
  const entries = [...byFile.entries()].map(([path, fileEdits]) => ({
429
432
  path,
430
433
  run: (br: LspBatchRequest | undefined) =>
@@ -441,11 +444,38 @@ export class EditTool implements AgentTool<TInput> {
441
444
  return executePerFile(entries, batchRequest, onUpdate);
442
445
  },
443
446
  },
447
+ atom: {
448
+ description: () => prompt.render(atomDescription),
449
+ parameters: atomEditParamsSchema,
450
+ execute: (
451
+ tool: EditTool,
452
+ params: EditParams,
453
+ signal: AbortSignal | undefined,
454
+ batchRequest: LspBatchRequest | undefined,
455
+ onUpdate?: (partialResult: AgentToolResult<EditToolDetails, TInput>) => void,
456
+ ) => {
457
+ const { edits, path: topPath } = params as AtomParams & { path?: string };
458
+ const resolved = resolveAtomEntryPaths(edits as AtomToolEdit[], topPath);
459
+ const byFile = groupBy(resolved, e => e.path);
460
+ const entries = [...byFile.entries()].map(([path, fileEdits]) => ({
461
+ path,
462
+ run: (br: LspBatchRequest | undefined) =>
463
+ executeAtomSingle({
464
+ session: tool.session,
465
+ path,
466
+ edits: fileEdits,
467
+ signal,
468
+ batchRequest: br,
469
+ writethrough: tool.#writethrough,
470
+ beginDeferredDiagnosticsForPath: p => tool.#beginDeferredDiagnosticsForPath(p),
471
+ }),
472
+ }));
473
+ return executePerFile(entries, batchRequest, onUpdate);
474
+ },
475
+ },
444
476
  replace: {
445
477
  description: () => prompt.render(replaceDescription),
446
478
  parameters: replaceEditSchema,
447
- invalidParamsMessage: "Invalid edit parameters for replace mode.",
448
- validate: isReplaceParams,
449
479
  execute: (
450
480
  tool: EditTool,
451
481
  params: EditParams,
@@ -453,8 +483,9 @@ export class EditTool implements AgentTool<TInput> {
453
483
  batchRequest: LspBatchRequest | undefined,
454
484
  onUpdate?: (partialResult: AgentToolResult<EditToolDetails, TInput>) => void,
455
485
  ) => {
456
- const { edits } = params as ReplaceParams;
457
- const entries = edits.map((entry: ReplaceEditEntry) => ({
486
+ const { edits, path: topPath } = params as ReplaceParams & { path?: string };
487
+ const resolved = resolveEntryPaths(edits as ReplaceEditEntry[], topPath);
488
+ const entries = resolved.map(entry => ({
458
489
  path: entry.path,
459
490
  run: (br: LspBatchRequest | undefined) =>
460
491
  executeReplaceSingle({
@@ -474,8 +505,6 @@ export class EditTool implements AgentTool<TInput> {
474
505
  vim: {
475
506
  description: () => this.#vimTool.description,
476
507
  parameters: vimSchema,
477
- invalidParamsMessage: "Invalid edit parameters for vim mode.",
478
- validate: isVimParams,
479
508
  execute: async (
480
509
  tool: EditTool,
481
510
  params: EditParams,