@oh-my-pi/pi-coding-agent 14.1.2 → 14.2.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 (130) hide show
  1. package/CHANGELOG.md +47 -2
  2. package/package.json +8 -8
  3. package/scripts/build-binary.ts +61 -0
  4. package/src/autoresearch/helpers.ts +10 -0
  5. package/src/autoresearch/index.ts +1 -11
  6. package/src/autoresearch/tools/init-experiment.ts +1 -10
  7. package/src/autoresearch/tools/log-experiment.ts +1 -11
  8. package/src/autoresearch/tools/run-experiment.ts +1 -10
  9. package/src/bun-imports.d.ts +6 -0
  10. package/src/cli/plugin-cli.ts +23 -45
  11. package/src/commit/agentic/tools/propose-commit.ts +1 -14
  12. package/src/commit/agentic/tools/split-commit.ts +1 -15
  13. package/src/commit/utils.ts +15 -1
  14. package/src/config/model-registry.ts +3 -3
  15. package/src/config/prompt-templates.ts +4 -12
  16. package/src/config/settings-schema.ts +27 -2
  17. package/src/config/settings.ts +1 -1
  18. package/src/dap/session.ts +8 -2
  19. package/src/discovery/claude-plugins.ts +61 -6
  20. package/src/discovery/codex.ts +2 -15
  21. package/src/discovery/gemini.ts +2 -15
  22. package/src/discovery/helpers.ts +40 -1
  23. package/src/discovery/opencode.ts +2 -15
  24. package/src/edit/apply-patch/index.ts +87 -0
  25. package/src/edit/apply-patch/parser.ts +174 -0
  26. package/src/edit/diff.ts +3 -14
  27. package/src/edit/index.ts +67 -3
  28. package/src/edit/modes/apply-patch.lark +19 -0
  29. package/src/edit/modes/apply-patch.ts +63 -0
  30. package/src/edit/modes/chunk.ts +6 -2
  31. package/src/edit/modes/hashline.ts +3 -3
  32. package/src/edit/modes/replace.ts +2 -13
  33. package/src/edit/read-file.ts +18 -0
  34. package/src/edit/renderer.ts +61 -33
  35. package/src/extensibility/extensions/compact-handler.ts +40 -0
  36. package/src/extensibility/extensions/runner.ts +11 -29
  37. package/src/extensibility/utils.ts +7 -1
  38. package/src/internal-urls/docs-index.generated.ts +9 -2
  39. package/src/lsp/client.ts +14 -5
  40. package/src/lsp/index.ts +53 -10
  41. package/src/lsp/render.ts +14 -2
  42. package/src/lsp/types.ts +2 -0
  43. package/src/main.ts +1 -0
  44. package/src/mcp/manager.ts +29 -48
  45. package/src/memories/index.ts +7 -1
  46. package/src/modes/acp/acp-agent.ts +3 -16
  47. package/src/modes/components/model-selector.ts +15 -24
  48. package/src/modes/components/plugin-settings.ts +16 -5
  49. package/src/modes/components/read-tool-group.ts +92 -9
  50. package/src/modes/components/settings-defs.ts +18 -0
  51. package/src/modes/components/settings-selector.ts +2 -6
  52. package/src/modes/components/tool-execution.ts +61 -28
  53. package/src/modes/controllers/event-controller.ts +3 -1
  54. package/src/modes/controllers/extension-ui-controller.ts +99 -150
  55. package/src/modes/controllers/selector-controller.ts +3 -12
  56. package/src/modes/interactive-mode.ts +4 -2
  57. package/src/modes/print-mode.ts +4 -22
  58. package/src/modes/rpc/rpc-mode.ts +18 -38
  59. package/src/modes/shared.ts +10 -1
  60. package/src/modes/utils/ui-helpers.ts +6 -2
  61. package/src/plan-mode/approved-plan.ts +5 -4
  62. package/src/prompts/system/subagent-system-prompt.md +4 -4
  63. package/src/prompts/system/subagent-user-prompt.md +2 -2
  64. package/src/prompts/system/system-prompt.md +208 -243
  65. package/src/prompts/tools/apply-patch.md +67 -0
  66. package/src/prompts/tools/ast-edit.md +18 -23
  67. package/src/prompts/tools/ast-grep.md +25 -32
  68. package/src/prompts/tools/bash.md +11 -23
  69. package/src/prompts/tools/debug.md +8 -22
  70. package/src/prompts/tools/find.md +0 -4
  71. package/src/prompts/tools/grep.md +3 -5
  72. package/src/prompts/tools/hashline.md +16 -10
  73. package/src/prompts/tools/python.md +10 -14
  74. package/src/prompts/tools/read.md +17 -24
  75. package/src/prompts/tools/task.md +57 -21
  76. package/src/prompts/tools/todo-write.md +45 -67
  77. package/src/session/agent-session.ts +4 -4
  78. package/src/session/session-manager.ts +15 -7
  79. package/src/session/streaming-output.ts +24 -0
  80. package/src/slash-commands/builtin-registry.ts +3 -14
  81. package/src/task/executor.ts +13 -34
  82. package/src/task/index.ts +82 -18
  83. package/src/task/simple-mode.ts +27 -0
  84. package/src/task/template.ts +17 -3
  85. package/src/task/types.ts +77 -30
  86. package/src/tools/ask.ts +2 -4
  87. package/src/tools/ast-edit.ts +41 -17
  88. package/src/tools/ast-grep.ts +8 -27
  89. package/src/tools/bash-skill-urls.ts +9 -7
  90. package/src/tools/bash.ts +66 -24
  91. package/src/tools/browser.ts +1 -1
  92. package/src/tools/fetch.ts +1 -14
  93. package/src/tools/file-recorder.ts +35 -0
  94. package/src/tools/find.ts +25 -29
  95. package/src/tools/gh-format.ts +12 -0
  96. package/src/tools/gh-renderer.ts +1 -8
  97. package/src/tools/gh.ts +6 -13
  98. package/src/tools/grep.ts +103 -59
  99. package/src/tools/jtd-to-json-schema.ts +16 -0
  100. package/src/tools/match-line-format.ts +20 -0
  101. package/src/tools/path-utils.ts +61 -5
  102. package/src/tools/plan-mode-guard.ts +6 -5
  103. package/src/tools/python.ts +1 -1
  104. package/src/tools/read.ts +1 -1
  105. package/src/tools/render-utils.ts +38 -6
  106. package/src/tools/renderers.ts +1 -0
  107. package/src/tools/resolve.ts +12 -3
  108. package/src/tools/ssh.ts +3 -11
  109. package/src/tools/submit-result.ts +1 -13
  110. package/src/tools/todo-write.ts +137 -103
  111. package/src/tools/vim.ts +1 -1
  112. package/src/tools/write.ts +2 -23
  113. package/src/tui/code-cell.ts +12 -7
  114. package/src/utils/edit-mode.ts +3 -2
  115. package/src/utils/git.ts +1 -1
  116. package/src/vim/engine.ts +41 -58
  117. package/src/web/scrapers/crates-io.ts +1 -14
  118. package/src/web/scrapers/types.ts +13 -0
  119. package/src/web/search/providers/base.ts +13 -0
  120. package/src/web/search/providers/brave.ts +2 -5
  121. package/src/web/search/providers/codex.ts +20 -24
  122. package/src/web/search/providers/gemini.ts +39 -1
  123. package/src/web/search/providers/jina.ts +2 -5
  124. package/src/web/search/providers/kagi.ts +3 -8
  125. package/src/web/search/providers/kimi.ts +3 -7
  126. package/src/web/search/providers/parallel.ts +3 -8
  127. package/src/web/search/providers/synthetic.ts +3 -7
  128. package/src/web/search/providers/tavily.ts +15 -11
  129. package/src/web/search/providers/utils.ts +36 -0
  130. package/src/web/search/providers/zai.ts +3 -7
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import type { AssistantMessage, ImageContent } from "@oh-my-pi/pi-ai";
9
9
  import { sanitizeText } from "@oh-my-pi/pi-natives";
10
+ import { runExtensionCompact, runExtensionSetModel } from "../extensibility/extensions/compact-handler";
10
11
  import type { AgentSession } from "../session/agent-session";
11
12
 
12
13
  /**
@@ -65,12 +66,7 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
65
66
  getAllTools: () => session.getAllToolNames(),
66
67
  setActiveTools: (toolNames: string[]) => session.setActiveToolsByName(toolNames),
67
68
  getCommands: () => [],
68
- setModel: async model => {
69
- const key = await session.modelRegistry.getApiKey(model);
70
- if (!key) return false;
71
- await session.setModel(model);
72
- return true;
73
- },
69
+ setModel: model => runExtensionSetModel(session, model),
74
70
  getThinkingLevel: () => session.thinkingLevel,
75
71
  setThinkingLevel: level => session.setThinkingLevel(level),
76
72
  getSessionName: () => session.sessionManager.getSessionName(),
@@ -87,14 +83,7 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
87
83
  shutdown: () => {},
88
84
  getContextUsage: () => session.getContextUsage(),
89
85
  getSystemPrompt: () => session.systemPrompt,
90
- compact: async instructionsOrOptions => {
91
- const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined;
92
- const options =
93
- instructionsOrOptions && typeof instructionsOrOptions === "object"
94
- ? instructionsOrOptions
95
- : undefined;
96
- await session.compact(instructions, options);
97
- },
86
+ compact: instructionsOrOptions => runExtensionCompact(session, instructionsOrOptions),
98
87
  },
99
88
  // ExtensionCommandContextActions - commands invokable via prompt("/command")
100
89
  {
@@ -122,14 +111,7 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
122
111
  reload: async () => {
123
112
  await session.reload();
124
113
  },
125
- compact: async instructionsOrOptions => {
126
- const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined;
127
- const options =
128
- instructionsOrOptions && typeof instructionsOrOptions === "object"
129
- ? instructionsOrOptions
130
- : undefined;
131
- await session.compact(instructions, options);
132
- },
114
+ compact: instructionsOrOptions => runExtensionCompact(session, instructionsOrOptions),
133
115
  },
134
116
  // No UI context
135
117
  );
@@ -16,6 +16,7 @@ import type {
16
16
  ExtensionUIDialogOptions,
17
17
  ExtensionWidgetOptions,
18
18
  } from "../../extensibility/extensions";
19
+ import { runExtensionCompact, runExtensionSetModel } from "../../extensibility/extensions/compact-handler";
19
20
  import { type Theme, theme } from "../../modes/theme/theme";
20
21
  import type { AgentSession } from "../../session/agent-session";
21
22
  import { isRpcHostToolResult, isRpcHostToolUpdate, RpcHostToolBridge } from "./host-tools";
@@ -66,6 +67,18 @@ function normalizeHostToolDefinitions(tools: RpcHostToolDefinition[]): RpcHostTo
66
67
  });
67
68
  }
68
69
 
70
+ function parseValueDialogResponse(
71
+ response: RpcExtensionUIResponse,
72
+ dialogOptions: ExtensionUIDialogOptions | undefined,
73
+ ): string | undefined {
74
+ if ("cancelled" in response && response.cancelled) {
75
+ if (response.timedOut) dialogOptions?.onTimeout?.();
76
+ return undefined;
77
+ }
78
+ if ("value" in response) return response.value;
79
+ return undefined;
80
+ }
81
+
69
82
  function shouldEmitRpcTitles(): boolean {
70
83
  const raw = $env.PI_RPC_EMIT_TITLE;
71
84
  if (!raw) return false;
@@ -228,14 +241,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
228
241
  dialogOptions,
229
242
  undefined,
230
243
  { method: "select", title, options, timeout: dialogOptions?.timeout },
231
- response => {
232
- if ("cancelled" in response && response.cancelled) {
233
- if (response.timedOut) dialogOptions?.onTimeout?.();
234
- return undefined;
235
- }
236
- if ("value" in response) return response.value;
237
- return undefined;
238
- },
244
+ response => parseValueDialogResponse(response, dialogOptions),
239
245
  );
240
246
  }
241
247
 
@@ -264,14 +270,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
264
270
  dialogOptions,
265
271
  undefined,
266
272
  { method: "input", title, placeholder, timeout: dialogOptions?.timeout },
267
- response => {
268
- if ("cancelled" in response && response.cancelled) {
269
- if (response.timedOut) dialogOptions?.onTimeout?.();
270
- return undefined;
271
- }
272
- if ("value" in response) return response.value;
273
- return undefined;
274
- },
273
+ response => parseValueDialogResponse(response, dialogOptions),
275
274
  );
276
275
  }
277
276
 
@@ -432,12 +431,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
432
431
  getAllTools: () => session.getAllToolNames(),
433
432
  setActiveTools: (toolNames: string[]) => session.setActiveToolsByName(toolNames),
434
433
  getCommands: () => [],
435
- setModel: async model => {
436
- const key = await session.modelRegistry.getApiKey(model);
437
- if (!key) return false;
438
- await session.setModel(model);
439
- return true;
440
- },
434
+ setModel: model => runExtensionSetModel(session, model),
441
435
  getThinkingLevel: () => session.thinkingLevel,
442
436
  setThinkingLevel: level => session.setThinkingLevel(level),
443
437
  getSessionName: () => session.sessionManager.getSessionName(),
@@ -456,14 +450,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
456
450
  },
457
451
  getContextUsage: () => session.getContextUsage(),
458
452
  getSystemPrompt: () => session.systemPrompt,
459
- compact: async instructionsOrOptions => {
460
- const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined;
461
- const options =
462
- instructionsOrOptions && typeof instructionsOrOptions === "object"
463
- ? instructionsOrOptions
464
- : undefined;
465
- await session.compact(instructions, options);
466
- },
453
+ compact: instructionsOrOptions => runExtensionCompact(session, instructionsOrOptions),
467
454
  },
468
455
  // ExtensionCommandContextActions - commands invokable via prompt("/command")
469
456
  {
@@ -492,14 +479,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
492
479
  reload: async () => {
493
480
  await session.reload();
494
481
  },
495
- compact: async instructionsOrOptions => {
496
- const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined;
497
- const options =
498
- instructionsOrOptions && typeof instructionsOrOptions === "object"
499
- ? instructionsOrOptions
500
- : undefined;
501
- await session.compact(instructions, options);
502
- },
482
+ compact: instructionsOrOptions => runExtensionCompact(session, instructionsOrOptions),
503
483
  },
504
484
  new RpcExtensionUIContext(pendingExtensionRequests, output),
505
485
  );
@@ -5,9 +5,18 @@ import { theme } from "./theme/theme";
5
5
  // Text Sanitization
6
6
  // ═══════════════════════════════════════════════════════════════════════════
7
7
 
8
- /** Sanitize text for display in a single-line status. Strips C0/C1 control characters (including ANSI ESC), collapses whitespace, trims. */
8
+ const ANSI_OSC_RE = /\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x9d[^\x07\x9c]*(?:\x07|\x9c)/g;
9
+ const ANSI_STRING_RE = /\x1b(?:P|_|\^)[\s\S]*?\x1b\\|[\x90\x9e\x9f][\s\S]*?\x9c/g;
10
+ const ANSI_CSI_RE = /\x1b\[[0-?]*[ -/]*[@-~]|\x9b[0-?]*[ -/]*[@-~]/g;
11
+ const ANSI_SINGLE_RE = /\x1b[@-Z\\-_]/g;
12
+
13
+ /** Sanitize text for display in a single-line status. Strips ANSI escape sequences, C0/C1 control characters, collapses whitespace, trims. */
9
14
  export function sanitizeStatusText(text: string): string {
10
15
  return text
16
+ .replace(ANSI_OSC_RE, "")
17
+ .replace(ANSI_STRING_RE, "")
18
+ .replace(ANSI_CSI_RE, "")
19
+ .replace(ANSI_SINGLE_RE, "")
11
20
  .replace(/[\u0000-\u001f\u007f-\u009f]/g, " ")
12
21
  .replace(/ +/g, " ")
13
22
  .trim();
@@ -257,7 +257,9 @@ export class UiHelpers {
257
257
  if (content.name === "read") {
258
258
  if (hasErrorStop && errorMessage) {
259
259
  if (!readGroup) {
260
- readGroup = new ReadToolGroupComponent();
260
+ readGroup = new ReadToolGroupComponent({
261
+ showContentPreview: this.ctx.settings.get("read.toolResultPreview"),
262
+ });
261
263
  readGroup.setExpanded(this.ctx.toolOutputExpanded);
262
264
  this.ctx.chatContainer.addChild(readGroup);
263
265
  }
@@ -330,7 +332,9 @@ export class UiHelpers {
330
332
  let component = this.ctx.pendingTools.get(message.toolCallId);
331
333
  if (!component) {
332
334
  if (!readGroup) {
333
- readGroup = new ReadToolGroupComponent();
335
+ readGroup = new ReadToolGroupComponent({
336
+ showContentPreview: this.ctx.settings.get("read.toolResultPreview"),
337
+ });
334
338
  readGroup.setExpanded(this.ctx.toolOutputExpanded);
335
339
  this.ctx.chatContainer.addChild(readGroup);
336
340
  }
@@ -1,6 +1,7 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import { isEnoent } from "@oh-my-pi/pi-utils";
3
3
  import { resolveLocalUrlToPath } from "../internal-urls";
4
+ import { normalizeLocalScheme } from "../tools/path-utils";
4
5
 
5
6
  interface RenameApprovedPlanFileOptions {
6
7
  planFilePath: string;
@@ -10,8 +11,8 @@ interface RenameApprovedPlanFileOptions {
10
11
  }
11
12
 
12
13
  function assertLocalUrl(path: string, label: "source" | "destination"): void {
13
- if (!path.startsWith("local://")) {
14
- throw new Error(`Approved plan ${label} path must use local:// (received ${path}).`);
14
+ if (!path.startsWith("local:/") && !path.startsWith("local://")) {
15
+ throw new Error(`Approved plan ${label} path must use local: scheme with / or // (received ${path}).`);
15
16
  }
16
17
  }
17
18
 
@@ -24,8 +25,8 @@ export async function renameApprovedPlanFile(options: RenameApprovedPlanFileOpti
24
25
  getArtifactsDir: () => getArtifactsDir(),
25
26
  getSessionId: () => getSessionId(),
26
27
  };
27
- const resolvedSource = resolveLocalUrlToPath(planFilePath, resolveOptions);
28
- const resolvedDestination = resolveLocalUrlToPath(finalPlanFilePath, resolveOptions);
28
+ const resolvedSource = resolveLocalUrlToPath(normalizeLocalScheme(planFilePath), resolveOptions);
29
+ const resolvedDestination = resolveLocalUrlToPath(normalizeLocalScheme(finalPlanFilePath), resolveOptions);
29
30
 
30
31
  if (resolvedSource === resolvedDestination) {
31
32
  return;
@@ -1,9 +1,9 @@
1
1
  {{base}}
2
2
 
3
- {{SECTION_SEPERATOR "Acting as"}}
3
+ {{SECTION_SEPARATOR "Acting as"}}
4
4
  {{agent}}
5
5
 
6
- {{SECTION_SEPERATOR "Job"}}
6
+ {{SECTION_SEPARATOR "Job"}}
7
7
  You are operating on a delegated sub-task.
8
8
  {{#if worktree}}
9
9
  You are working in an isolated working tree at `{{worktree}}` for this sub-task.
@@ -14,7 +14,7 @@ You **MUST NOT** modify files outside this tree or in the original repository.
14
14
  If you need additional information, you can find your conversation with the user in {{contextFile}} (`tail` or `grep` relevant terms).
15
15
  {{/if}}
16
16
 
17
- {{SECTION_SEPERATOR "Closure"}}
17
+ {{SECTION_SEPARATOR "Closure"}}
18
18
  No TODO tracking, no progress updates. Execute, call `submit_result`, done.
19
19
 
20
20
  When finished, you **MUST** call `submit_result` exactly once. This is like writing to a ticket, provide what is required, and close it.
@@ -28,7 +28,7 @@ Your result **MUST** match this TypeScript interface:
28
28
  ```
29
29
  {{/if}}
30
30
 
31
- {{SECTION_SEPERATOR "Giving Up"}}
31
+ {{SECTION_SEPARATOR "Giving Up"}}
32
32
  Giving up is a last resort. If truly blocked, you **MUST** call `submit_result` exactly once with `result.error` describing what you tried and the exact blocker.
33
33
  You **MUST NOT** give up due to uncertainty, missing information obtainable via tools or repo context, or needing a design decision you can derive yourself.
34
34
 
@@ -1,11 +1,11 @@
1
1
  {{#if context}}
2
- {{SECTION_SEPERATOR "Background"}}
2
+ {{SECTION_SEPARATOR "Background"}}
3
3
  <context>
4
4
  {{context}}
5
5
  </context>
6
6
  {{/if}}
7
7
 
8
- {{SECTION_SEPERATOR "Task"}}
8
+ {{SECTION_SEPARATOR "Task"}}
9
9
  Your assignment is below. Your work begins now.
10
10
  <goal>
11
11
  {{assignment}}