@oh-my-pi/pi-coding-agent 15.0.0 → 15.0.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 (140) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/examples/extensions/plan-mode.ts +0 -1
  3. package/package.json +9 -9
  4. package/scripts/build-binary.ts +5 -0
  5. package/src/autoresearch/helpers.ts +17 -0
  6. package/src/autoresearch/tools/log-experiment.ts +9 -17
  7. package/src/autoresearch/tools/run-experiment.ts +2 -17
  8. package/src/capability/skill.ts +7 -0
  9. package/src/cli/list-models.ts +1 -1
  10. package/src/cli/shell-cli.ts +3 -13
  11. package/src/cli/update-cli.ts +1 -1
  12. package/src/cli.ts +10 -29
  13. package/src/commit/agentic/tools/propose-changelog.ts +8 -1
  14. package/src/commit/analysis/conventional.ts +8 -66
  15. package/src/commit/map-reduce/reduce-phase.ts +6 -65
  16. package/src/commit/pipeline.ts +2 -2
  17. package/src/commit/shared-llm.ts +89 -0
  18. package/src/config/config-file.ts +210 -0
  19. package/src/config/model-equivalence.ts +8 -11
  20. package/src/config/model-registry.ts +13 -2
  21. package/src/config/model-resolver.ts +1 -4
  22. package/src/config/settings-schema.ts +71 -1
  23. package/src/config/settings.ts +1 -1
  24. package/src/config.ts +3 -219
  25. package/src/edit/renderer.ts +7 -1
  26. package/src/eval/js/executor.ts +3 -0
  27. package/src/eval/js/shared/rewrite-imports.ts +2 -2
  28. package/src/eval/py/executor.ts +5 -0
  29. package/src/exa/factory.ts +2 -2
  30. package/src/exa/mcp-client.ts +74 -1
  31. package/src/exec/bash-executor.ts +5 -1
  32. package/src/export/html/template.generated.ts +1 -1
  33. package/src/export/html/template.js +0 -11
  34. package/src/extensibility/extensions/runner.ts +1 -1
  35. package/src/extensibility/extensions/types.ts +89 -223
  36. package/src/extensibility/hooks/types.ts +89 -314
  37. package/src/extensibility/shared-events.ts +343 -0
  38. package/src/extensibility/skills.ts +9 -0
  39. package/src/goals/index.ts +3 -0
  40. package/src/goals/runtime.ts +500 -0
  41. package/src/goals/state.ts +37 -0
  42. package/src/goals/tools/goal-tool.ts +237 -0
  43. package/src/hashline/anchors.ts +2 -2
  44. package/src/hindsight/mental-models.ts +1 -1
  45. package/src/internal-urls/agent-protocol.ts +1 -20
  46. package/src/internal-urls/artifact-protocol.ts +1 -19
  47. package/src/internal-urls/docs-index.generated.ts +5 -6
  48. package/src/internal-urls/registry-helpers.ts +25 -0
  49. package/src/main.ts +11 -2
  50. package/src/mcp/oauth-flow.ts +20 -0
  51. package/src/modes/acp/acp-agent.ts +79 -45
  52. package/src/modes/components/assistant-message.ts +14 -8
  53. package/src/modes/components/bash-execution.ts +24 -63
  54. package/src/modes/components/custom-message.ts +14 -40
  55. package/src/modes/components/eval-execution.ts +27 -57
  56. package/src/modes/components/execution-shared.ts +102 -0
  57. package/src/modes/components/hook-message.ts +17 -49
  58. package/src/modes/components/mcp-add-wizard.ts +26 -5
  59. package/src/modes/components/message-frame.ts +88 -0
  60. package/src/modes/components/model-selector.ts +1 -1
  61. package/src/modes/components/session-observer-overlay.ts +6 -2
  62. package/src/modes/components/session-selector.ts +1 -1
  63. package/src/modes/components/status-line/segments.ts +55 -4
  64. package/src/modes/components/status-line/types.ts +4 -0
  65. package/src/modes/components/status-line.ts +28 -10
  66. package/src/modes/components/tool-execution.ts +7 -8
  67. package/src/modes/controllers/command-controller-shared.ts +108 -0
  68. package/src/modes/controllers/command-controller.ts +13 -4
  69. package/src/modes/controllers/event-controller.ts +36 -7
  70. package/src/modes/controllers/input-controller.ts +13 -0
  71. package/src/modes/controllers/mcp-command-controller.ts +56 -61
  72. package/src/modes/controllers/ssh-command-controller.ts +18 -57
  73. package/src/modes/interactive-mode.ts +624 -52
  74. package/src/modes/print-mode.ts +16 -86
  75. package/src/modes/rpc/rpc-mode.ts +14 -87
  76. package/src/modes/runtime-init.ts +115 -0
  77. package/src/modes/theme/defaults/dark-poimandres.json +2 -0
  78. package/src/modes/theme/defaults/light-poimandres.json +2 -0
  79. package/src/modes/theme/theme.ts +18 -6
  80. package/src/modes/types.ts +14 -3
  81. package/src/modes/utils/context-usage.ts +13 -13
  82. package/src/modes/utils/ui-helpers.ts +10 -3
  83. package/src/plan-mode/approved-plan.ts +35 -1
  84. package/src/prompts/goals/goal-budget-limit.md +16 -0
  85. package/src/prompts/goals/goal-continuation.md +28 -0
  86. package/src/prompts/goals/goal-mode-active.md +23 -0
  87. package/src/prompts/system/plan-mode-active.md +5 -5
  88. package/src/prompts/system/plan-mode-tool-decision-reminder.md +1 -1
  89. package/src/prompts/tools/bash.md +6 -0
  90. package/src/prompts/tools/goal.md +13 -0
  91. package/src/prompts/tools/hashline.md +102 -114
  92. package/src/prompts/tools/read.md +1 -0
  93. package/src/prompts/tools/resolve.md +6 -5
  94. package/src/sdk.ts +12 -5
  95. package/src/session/agent-session.ts +428 -106
  96. package/src/session/blob-store.ts +36 -3
  97. package/src/session/messages.ts +67 -2
  98. package/src/session/session-manager.ts +131 -12
  99. package/src/session/session-storage.ts +33 -15
  100. package/src/session/streaming-output.ts +309 -13
  101. package/src/slash-commands/builtin-registry.ts +18 -0
  102. package/src/ssh/ssh-executor.ts +5 -0
  103. package/src/system-prompt.ts +4 -2
  104. package/src/task/executor.ts +17 -7
  105. package/src/task/index.ts +3 -0
  106. package/src/task/render.ts +21 -15
  107. package/src/task/types.ts +4 -0
  108. package/src/tools/ast-edit.ts +21 -120
  109. package/src/tools/ast-grep.ts +21 -119
  110. package/src/tools/bash-interactive.ts +9 -1
  111. package/src/tools/bash.ts +27 -4
  112. package/src/tools/browser/attach.ts +3 -3
  113. package/src/tools/browser/launch.ts +81 -18
  114. package/src/tools/browser/registry.ts +1 -5
  115. package/src/tools/browser/tab-supervisor.ts +51 -14
  116. package/src/tools/conflict-detect.ts +15 -4
  117. package/src/tools/eval.ts +3 -1
  118. package/src/tools/find.ts +20 -38
  119. package/src/tools/gh.ts +7 -6
  120. package/src/tools/index.ts +22 -11
  121. package/src/tools/inspect-image.ts +3 -10
  122. package/src/tools/output-meta.ts +176 -37
  123. package/src/tools/path-utils.ts +125 -2
  124. package/src/tools/read.ts +516 -233
  125. package/src/tools/render-utils.ts +92 -0
  126. package/src/tools/renderers.ts +2 -0
  127. package/src/tools/resolve.ts +72 -44
  128. package/src/tools/search.ts +120 -186
  129. package/src/tools/write.ts +44 -9
  130. package/src/utils/file-mentions.ts +1 -1
  131. package/src/utils/image-loading.ts +7 -3
  132. package/src/utils/image-resize.ts +32 -43
  133. package/src/vim/parser.ts +0 -17
  134. package/src/vim/render.ts +1 -1
  135. package/src/vim/types.ts +1 -1
  136. package/src/web/search/providers/gemini.ts +35 -95
  137. package/src/prompts/tools/exit-plan-mode.md +0 -6
  138. package/src/tools/exit-plan-mode.ts +0 -97
  139. package/src/utils/fuzzy.ts +0 -108
  140. package/src/utils/image-convert.ts +0 -27
@@ -180,7 +180,7 @@ export function rewriteImports(code: string): string {
180
180
  * Nested declarations (inside functions, blocks, classes) are left alone \u2014 they're
181
181
  * scoped to their enclosing function/block regardless of `var` vs `let`/`const`.
182
182
  */
183
- export function demoteTopLevelLexicals(code: string): string {
183
+ function demoteTopLevelLexicals(code: string): string {
184
184
  if (!/\b(?:const|let|class)\b/.test(code)) return code;
185
185
 
186
186
  const ast = parseProgram(code);
@@ -248,7 +248,7 @@ function returnFinalExpression(code: string): { source: string; returned: boolea
248
248
  * common case avoids an extra transpile pass. We detect "looks like TS" with a cheap regex
249
249
  * before invoking the transpiler.
250
250
  */
251
- export function stripTypeScript(code: string): string {
251
+ function stripTypeScript(code: string): string {
252
252
  if (!LOOKS_LIKE_TS.test(code)) return code;
253
253
  try {
254
254
  return new Bun.Transpiler({ loader: "ts" }).transformSync(code);
@@ -1,6 +1,8 @@
1
1
  import { getProjectDir, logger } from "@oh-my-pi/pi-utils";
2
+ import { Settings } from "../../config/settings";
2
3
  import { OutputSink } from "../../session/streaming-output";
3
4
  import type { ToolSession } from "../../tools";
5
+ import { resolveOutputMaxColumns, resolveOutputSinkHeadBytes } from "../../tools/output-meta";
4
6
  import type { JsStatusEvent } from "../js/shared/types";
5
7
  import type { KernelDisplayOutput } from "./display";
6
8
  import {
@@ -815,10 +817,13 @@ async function executeWithKernel(
815
817
  code: string,
816
818
  options: PythonExecutorOptions | undefined,
817
819
  ): Promise<PythonResult> {
820
+ const settings = await Settings.init();
818
821
  const sink = new OutputSink({
819
822
  onChunk: options?.onChunk,
820
823
  artifactPath: options?.artifactPath,
821
824
  artifactId: options?.artifactId,
825
+ headBytes: resolveOutputSinkHeadBytes(settings),
826
+ maxColumns: resolveOutputMaxColumns(settings),
822
827
  });
823
828
  const displayOutputs: KernelDisplayOutput[] = [];
824
829
  const deadlineMs = getExecutionDeadlineMs(options);
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import type { TObject, TProperties } from "@sinclair/typebox";
5
5
  import type { CustomTool } from "../extensibility/custom-tools/types";
6
- import { callExaTool, findApiKey, formatSearchResults, isSearchResponse } from "./mcp-client";
6
+ import { callExaTool, findApiKey, formatGenericResponse, formatSearchResults, isSearchResponse } from "./mcp-client";
7
7
  import type { ExaRenderDetails } from "./types";
8
8
 
9
9
  /** Creates an Exa tool with standardized API key handling, error wrapping, and optional search response formatting. */
@@ -44,7 +44,7 @@ export function createExaTool(
44
44
  }
45
45
 
46
46
  return {
47
- content: [{ type: "text" as const, text: JSON.stringify(response, null, 2) }],
47
+ content: [{ type: "text" as const, text: formatGenericResponse(response) }],
48
48
  details: { raw: response, toolName: name },
49
49
  };
50
50
  } catch (error) {
@@ -174,6 +174,79 @@ export function formatSearchResults(data: ExaSearchResponse): string {
174
174
 
175
175
  return output.trim();
176
176
  }
177
+ /**
178
+ * Format a non-search MCP response as human-readable text.
179
+ * Handles objects, arrays, primitives, and common MCP response shapes.
180
+ */
181
+ export function formatGenericResponse(data: unknown): string {
182
+ if (data === null || data === undefined) return "No result.";
183
+ if (typeof data === "string") return data;
184
+ if (typeof data === "number" || typeof data === "boolean") return String(data);
185
+
186
+ if (Array.isArray(data)) {
187
+ if (data.length === 0) return "(empty)";
188
+ const parts: string[] = [];
189
+ for (let i = 0; i < data.length; i++) {
190
+ const item = data[i];
191
+ if (typeof item === "object" && item !== null) {
192
+ const record = item as Record<string, unknown>;
193
+ const title = (record.title ?? record.name ?? record.id ?? `Item ${i + 1}`) as string;
194
+ parts.push(`\n### ${title}`);
195
+ for (const [k, v] of Object.entries(record)) {
196
+ if (["title", "name", "id"].includes(k)) continue;
197
+ parts.push(`- **${k}:** ${formatValue(v)}`);
198
+ }
199
+ } else {
200
+ parts.push(`- ${formatValue(item)}`);
201
+ }
202
+ }
203
+ return parts.join("\n");
204
+ }
205
+
206
+ if (typeof data === "object") {
207
+ const record = data as Record<string, unknown>;
208
+ if (record.content && Array.isArray(record.content)) {
209
+ // MCP-style content array — extract text blocks
210
+ const texts = record.content
211
+ .filter(
212
+ (c: unknown): c is { type: string; text?: string } =>
213
+ typeof c === "object" && c !== null && (c as Record<string, unknown>)?.type === "text",
214
+ )
215
+ .map(c => c.text ?? "")
216
+ .filter(Boolean);
217
+ if (texts.length > 0) return texts.join("\n");
218
+ }
219
+
220
+ const lines: string[] = [];
221
+ for (const [k, v] of Object.entries(record)) {
222
+ if (k === "content") continue; // handled above
223
+ if (v === null || v === undefined) continue;
224
+ if (typeof v === "object") {
225
+ const formatted = formatGenericResponse(v);
226
+ if (formatted) lines.push(`- **${k}:**\n${indent(formatted, 2)}`);
227
+ } else {
228
+ lines.push(`- **${k}:** ${formatValue(v)}`);
229
+ }
230
+ }
231
+ return lines.join("\n") || "(empty)";
232
+ }
233
+
234
+ return String(data);
235
+ }
236
+
237
+ function formatValue(v: unknown): string {
238
+ if (v === null || v === undefined) return "—";
239
+ if (typeof v === "object") return JSON.stringify(v);
240
+ return String(v);
241
+ }
242
+
243
+ function indent(text: string, spaces: number): string {
244
+ const pad = " ".repeat(spaces);
245
+ return text
246
+ .split("\n")
247
+ .map(line => pad + line)
248
+ .join("\n");
249
+ }
177
250
 
178
251
  /** Check if result is a search response */
179
252
  export function isSearchResponse(data: unknown): data is ExaSearchResponse {
@@ -260,7 +333,7 @@ export class MCPWrappedTool implements CustomTool<TSchema, ExaRenderDetails> {
260
333
  }
261
334
 
262
335
  return {
263
- content: [{ type: "text" as const, text: JSON.stringify(response, null, 2) }],
336
+ content: [{ type: "text" as const, text: formatGenericResponse(response) }],
264
337
  details: { raw: response, toolName: this.config.name },
265
338
  };
266
339
  } catch (error) {
@@ -7,6 +7,7 @@ import * as fs from "node:fs/promises";
7
7
  import { executeShell, type MinimizerOptions, Shell } from "@oh-my-pi/pi-natives";
8
8
  import { Settings, type ShellMinimizerSettings } from "../config/settings";
9
9
  import { OutputSink } from "../session/streaming-output";
10
+ import { resolveOutputMaxColumns, resolveOutputSinkHeadBytes } from "../tools/output-meta";
10
11
  import { getOrCreateSnapshot } from "../utils/shell-snapshot";
11
12
  import { NON_INTERACTIVE_ENV } from "./non-interactive-env";
12
13
 
@@ -64,7 +65,8 @@ async function resolveShellCwd(cwd: string | undefined): Promise<string | undefi
64
65
  }
65
66
  }
66
67
 
67
- function buildMinimizerOptions(group: ShellMinimizerSettings): MinimizerOptions | undefined {
68
+ /** Translate `ShellMinimizerSettings` into native `MinimizerOptions`, or `undefined` when disabled. */
69
+ export function buildMinimizerOptions(group: ShellMinimizerSettings): MinimizerOptions | undefined {
68
70
  if (!group.enabled) return undefined;
69
71
  return {
70
72
  enabled: true,
@@ -94,6 +96,8 @@ export async function executeBash(command: string, options?: BashExecutorOptions
94
96
  onChunk: options?.onChunk,
95
97
  artifactPath: options?.artifactPath,
96
98
  artifactId: options?.artifactId,
99
+ headBytes: resolveOutputSinkHeadBytes(settings),
100
+ maxColumns: resolveOutputMaxColumns(settings),
97
101
  // Throttle the streaming preview callback to avoid saturating the
98
102
  // event loop when commands produce massive output (e.g. seq 1 50M).
99
103
  chunkThrottleMs: options?.onChunk ? 50 : 0,