@oh-my-pi/pi-coding-agent 8.0.16 → 8.1.0

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 (166) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/package.json +14 -11
  3. package/scripts/generate-wasm-b64.ts +24 -0
  4. package/src/capability/context-file.ts +1 -1
  5. package/src/capability/extension-module.ts +1 -1
  6. package/src/capability/extension.ts +1 -1
  7. package/src/capability/hook.ts +1 -1
  8. package/src/capability/instruction.ts +1 -1
  9. package/src/capability/mcp.ts +1 -1
  10. package/src/capability/prompt.ts +1 -1
  11. package/src/capability/rule.ts +1 -1
  12. package/src/capability/settings.ts +1 -1
  13. package/src/capability/skill.ts +1 -1
  14. package/src/capability/slash-command.ts +1 -1
  15. package/src/capability/ssh.ts +1 -1
  16. package/src/capability/system-prompt.ts +1 -1
  17. package/src/capability/tool.ts +1 -1
  18. package/src/cli/args.ts +1 -1
  19. package/src/cli/plugin-cli.ts +1 -5
  20. package/src/commit/agentic/agent.ts +309 -0
  21. package/src/commit/agentic/fallback.ts +96 -0
  22. package/src/commit/agentic/index.ts +359 -0
  23. package/src/commit/agentic/prompts/analyze-file.md +22 -0
  24. package/src/commit/agentic/prompts/session-user.md +26 -0
  25. package/src/commit/agentic/prompts/split-confirm.md +1 -0
  26. package/src/commit/agentic/prompts/system.md +40 -0
  27. package/src/commit/agentic/state.ts +74 -0
  28. package/src/commit/agentic/tools/analyze-file.ts +131 -0
  29. package/src/commit/agentic/tools/git-file-diff.ts +194 -0
  30. package/src/commit/agentic/tools/git-hunk.ts +50 -0
  31. package/src/commit/agentic/tools/git-overview.ts +84 -0
  32. package/src/commit/agentic/tools/index.ts +56 -0
  33. package/src/commit/agentic/tools/propose-changelog.ts +128 -0
  34. package/src/commit/agentic/tools/propose-commit.ts +154 -0
  35. package/src/commit/agentic/tools/recent-commits.ts +81 -0
  36. package/src/commit/agentic/tools/split-commit.ts +284 -0
  37. package/src/commit/agentic/topo-sort.ts +44 -0
  38. package/src/commit/agentic/trivial.ts +51 -0
  39. package/src/commit/agentic/validation.ts +200 -0
  40. package/src/commit/analysis/conventional.ts +169 -0
  41. package/src/commit/analysis/index.ts +4 -0
  42. package/src/commit/analysis/scope.ts +242 -0
  43. package/src/commit/analysis/summary.ts +114 -0
  44. package/src/commit/analysis/validation.ts +66 -0
  45. package/src/commit/changelog/detect.ts +36 -0
  46. package/src/commit/changelog/generate.ts +112 -0
  47. package/src/commit/changelog/index.ts +233 -0
  48. package/src/commit/changelog/parse.ts +44 -0
  49. package/src/commit/cli.ts +93 -0
  50. package/src/commit/git/diff.ts +148 -0
  51. package/src/commit/git/errors.ts +11 -0
  52. package/src/commit/git/index.ts +217 -0
  53. package/src/commit/git/operations.ts +53 -0
  54. package/src/commit/index.ts +5 -0
  55. package/src/commit/map-reduce/.map-phase.ts.kate-swp +0 -0
  56. package/src/commit/map-reduce/index.ts +63 -0
  57. package/src/commit/map-reduce/map-phase.ts +193 -0
  58. package/src/commit/map-reduce/reduce-phase.ts +147 -0
  59. package/src/commit/map-reduce/utils.ts +9 -0
  60. package/src/commit/message.ts +11 -0
  61. package/src/commit/model-selection.ts +84 -0
  62. package/src/commit/pipeline.ts +242 -0
  63. package/src/commit/prompts/analysis-system.md +155 -0
  64. package/src/commit/prompts/analysis-user.md +41 -0
  65. package/src/commit/prompts/changelog-system.md +56 -0
  66. package/src/commit/prompts/changelog-user.md +19 -0
  67. package/src/commit/prompts/file-observer-system.md +26 -0
  68. package/src/commit/prompts/file-observer-user.md +9 -0
  69. package/src/commit/prompts/reduce-system.md +60 -0
  70. package/src/commit/prompts/reduce-user.md +17 -0
  71. package/src/commit/prompts/summary-retry.md +4 -0
  72. package/src/commit/prompts/summary-system.md +52 -0
  73. package/src/commit/prompts/summary-user.md +13 -0
  74. package/src/commit/prompts/types-description.md +2 -0
  75. package/src/commit/types.ts +109 -0
  76. package/src/commit/utils/exclusions.ts +42 -0
  77. package/src/config/file-lock.ts +111 -0
  78. package/src/config/model-registry.ts +16 -7
  79. package/src/config/settings-manager.ts +115 -40
  80. package/src/config.ts +5 -5
  81. package/src/discovery/agents-md.ts +1 -1
  82. package/src/discovery/builtin.ts +1 -1
  83. package/src/discovery/claude.ts +1 -1
  84. package/src/discovery/cline.ts +1 -1
  85. package/src/discovery/codex.ts +1 -1
  86. package/src/discovery/cursor.ts +1 -1
  87. package/src/discovery/gemini.ts +1 -1
  88. package/src/discovery/github.ts +1 -1
  89. package/src/discovery/index.ts +11 -11
  90. package/src/discovery/mcp-json.ts +1 -1
  91. package/src/discovery/ssh.ts +1 -1
  92. package/src/discovery/vscode.ts +1 -1
  93. package/src/discovery/windsurf.ts +1 -1
  94. package/src/extensibility/custom-commands/loader.ts +1 -1
  95. package/src/extensibility/custom-commands/types.ts +1 -1
  96. package/src/extensibility/custom-tools/loader.ts +1 -1
  97. package/src/extensibility/custom-tools/types.ts +1 -1
  98. package/src/extensibility/extensions/loader.ts +1 -1
  99. package/src/extensibility/extensions/types.ts +1 -1
  100. package/src/extensibility/hooks/loader.ts +1 -1
  101. package/src/extensibility/hooks/types.ts +3 -3
  102. package/src/index.ts +10 -10
  103. package/src/ipy/executor.ts +97 -1
  104. package/src/lsp/index.ts +1 -1
  105. package/src/lsp/render.ts +90 -46
  106. package/src/main.ts +16 -3
  107. package/src/mcp/loader.ts +3 -3
  108. package/src/migrations.ts +3 -3
  109. package/src/modes/components/assistant-message.ts +29 -1
  110. package/src/modes/components/tool-execution.ts +5 -3
  111. package/src/modes/components/tree-selector.ts +1 -1
  112. package/src/modes/controllers/extension-ui-controller.ts +1 -1
  113. package/src/modes/controllers/selector-controller.ts +1 -1
  114. package/src/modes/interactive-mode.ts +5 -3
  115. package/src/modes/rpc/rpc-client.ts +1 -1
  116. package/src/modes/rpc/rpc-mode.ts +1 -4
  117. package/src/modes/rpc/rpc-types.ts +1 -1
  118. package/src/modes/theme/mermaid-cache.ts +89 -0
  119. package/src/modes/theme/theme.ts +2 -0
  120. package/src/modes/types.ts +2 -2
  121. package/src/patch/index.ts +3 -9
  122. package/src/patch/shared.ts +33 -5
  123. package/src/prompts/tools/task.md +2 -0
  124. package/src/sdk.ts +60 -22
  125. package/src/session/agent-session.ts +3 -3
  126. package/src/session/agent-storage.ts +32 -28
  127. package/src/session/artifacts.ts +24 -1
  128. package/src/session/auth-storage.ts +25 -10
  129. package/src/session/storage-migration.ts +12 -53
  130. package/src/system-prompt.ts +2 -2
  131. package/src/task/.executor.ts.kate-swp +0 -0
  132. package/src/task/executor.ts +1 -1
  133. package/src/task/index.ts +10 -1
  134. package/src/task/output-manager.ts +94 -0
  135. package/src/task/render.ts +7 -12
  136. package/src/task/worker.ts +1 -1
  137. package/src/tools/ask.ts +35 -13
  138. package/src/tools/bash.ts +80 -87
  139. package/src/tools/calculator.ts +42 -40
  140. package/src/tools/complete.ts +1 -1
  141. package/src/tools/fetch.ts +67 -104
  142. package/src/tools/find.ts +83 -86
  143. package/src/tools/grep.ts +80 -96
  144. package/src/tools/index.ts +10 -7
  145. package/src/tools/ls.ts +39 -65
  146. package/src/tools/notebook.ts +48 -64
  147. package/src/tools/output-utils.ts +1 -1
  148. package/src/tools/python.ts +71 -183
  149. package/src/tools/read.ts +74 -15
  150. package/src/tools/render-utils.ts +1 -15
  151. package/src/tools/ssh.ts +43 -24
  152. package/src/tools/todo-write.ts +27 -15
  153. package/src/tools/write.ts +93 -64
  154. package/src/tui/code-cell.ts +115 -0
  155. package/src/tui/file-list.ts +48 -0
  156. package/src/tui/index.ts +11 -0
  157. package/src/tui/output-block.ts +73 -0
  158. package/src/tui/status-line.ts +40 -0
  159. package/src/tui/tree-list.ts +56 -0
  160. package/src/tui/types.ts +17 -0
  161. package/src/tui/utils.ts +49 -0
  162. package/src/vendor/photon/photon_rs_bg.wasm.b64.js +1 -0
  163. package/src/web/search/auth.ts +1 -1
  164. package/src/web/search/index.ts +1 -1
  165. package/src/web/search/render.ts +119 -163
  166. package/tsconfig.json +0 -42
@@ -379,12 +379,14 @@ export class ToolExecutionComponent extends Container {
379
379
  // Check for custom tool rendering
380
380
  if (this.tool && (this.tool.renderCall || this.tool.renderResult)) {
381
381
  const tool = this.tool;
382
+ const mergeCallAndResult = Boolean((tool as { mergeCallAndResult?: boolean }).mergeCallAndResult);
382
383
  // Custom tools use Box for flexible component rendering
383
384
  this.contentBox.setBgFn(bgFn);
384
385
  this.contentBox.clear();
385
386
 
386
387
  // Render call component
387
- if (tool.renderCall) {
388
+ const shouldRenderCall = !this.result || !mergeCallAndResult;
389
+ if (shouldRenderCall && tool.renderCall) {
388
390
  try {
389
391
  const callComponent = tool.renderCall(this.args, theme);
390
392
  if (callComponent) {
@@ -552,13 +554,13 @@ export class ToolExecutionComponent extends Container {
552
554
 
553
555
  if (this.toolName === "bash" && this.result) {
554
556
  // Pass raw output and expanded state - renderer handles width-aware truncation
555
- const output = this.getTextOutput().trim();
557
+ const output = this.getTextOutput().trimEnd();
556
558
  context.output = output;
557
559
  context.expanded = this.expanded;
558
560
  context.previewLines = BASH_DEFAULT_PREVIEW_LINES;
559
561
  context.timeout = typeof this.args?.timeout === "number" ? this.args.timeout : undefined;
560
562
  } else if (this.toolName === "python" && this.result) {
561
- const output = this.getTextOutput().trim();
563
+ const output = this.getTextOutput().trimEnd();
562
564
  context.output = output;
563
565
  context.expanded = this.expanded;
564
566
  context.previewLines = PYTHON_DEFAULT_PREVIEW_LINES;
@@ -821,7 +821,7 @@ export class TreeSelectorComponent extends Container {
821
821
  new TruncatedText(
822
822
  theme.fg(
823
823
  "muted",
824
- " Up/Down: move. Left/Right: page. Shift+L: label. Ctrl+O/Shift+Ctrl+O: filter. Alt+D/T/U/L/A: filter. Type to search",
824
+ "Up/Down: move. Left/Right: page. Shift+L: label. Ctrl+O/Shift+Ctrl+O: filter. Alt+D/T/U/L/A: filter. Type to search",
825
825
  ),
826
826
  0,
827
827
  0,
@@ -5,7 +5,7 @@ import type {
5
5
  ExtensionContextActions,
6
6
  ExtensionError,
7
7
  ExtensionUIContext,
8
- } from "@oh-my-pi/pi-coding-agent/extensibility/extensions/index";
8
+ } from "@oh-my-pi/pi-coding-agent/extensibility/extensions";
9
9
  import { HookEditorComponent } from "@oh-my-pi/pi-coding-agent/modes/components/hook-editor";
10
10
  import { HookInputComponent } from "@oh-my-pi/pi-coding-agent/modes/components/hook-input";
11
11
  import { HookSelectorComponent } from "@oh-my-pi/pi-coding-agent/modes/components/hook-selector";
@@ -21,7 +21,7 @@ import {
21
21
  } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
22
22
  import type { InteractiveModeContext } from "@oh-my-pi/pi-coding-agent/modes/types";
23
23
  import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
24
- import { setPreferredImageProvider, setPreferredWebSearchProvider } from "@oh-my-pi/pi-coding-agent/tools/index";
24
+ import { setPreferredImageProvider, setPreferredWebSearchProvider } from "@oh-my-pi/pi-coding-agent/tools";
25
25
  import type { Component } from "@oh-my-pi/pi-tui";
26
26
  import { Input, Loader, Spacer, Text } from "@oh-my-pi/pi-tui";
27
27
 
@@ -8,7 +8,7 @@ import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
8
8
  import type { AssistantMessage, ImageContent, Message, UsageReport } from "@oh-my-pi/pi-ai";
9
9
  import { KeybindingsManager } from "@oh-my-pi/pi-coding-agent/config/keybindings";
10
10
  import type { SettingsManager } from "@oh-my-pi/pi-coding-agent/config/settings-manager";
11
- import type { ExtensionUIContext } from "@oh-my-pi/pi-coding-agent/extensibility/extensions/index";
11
+ import type { ExtensionUIContext } from "@oh-my-pi/pi-coding-agent/extensibility/extensions";
12
12
  import type { CompactOptions } from "@oh-my-pi/pi-coding-agent/extensibility/extensions/types";
13
13
  import { loadSlashCommands } from "@oh-my-pi/pi-coding-agent/extensibility/slash-commands";
14
14
  import type { AgentSession, AgentSessionEvent } from "@oh-my-pi/pi-coding-agent/session/agent-session";
@@ -44,6 +44,7 @@ import { EventController } from "./controllers/event-controller";
44
44
  import { ExtensionUiController } from "./controllers/extension-ui-controller";
45
45
  import { InputController } from "./controllers/input-controller";
46
46
  import { SelectorController } from "./controllers/selector-controller";
47
+ import { setMermaidRenderCallback } from "./theme/mermaid-cache";
47
48
  import type { Theme } from "./theme/theme";
48
49
  import { getEditorTheme, getMarkdownTheme, onThemeChange, theme } from "./theme/theme";
49
50
  import type { CompactionQueuedMessage, InteractiveModeContext, TodoItem } from "./types";
@@ -124,7 +125,7 @@ export class InteractiveMode implements InteractiveModeContext {
124
125
  private readonly changelogMarkdown: string | undefined;
125
126
  public readonly lspServers: Array<{ name: string; status: "ready" | "error"; fileTypes: string[] }> | undefined =
126
127
  undefined;
127
- public mcpManager?: import("@oh-my-pi/pi-coding-agent/mcp/index").MCPManager;
128
+ public mcpManager?: import("@oh-my-pi/pi-coding-agent/mcp").MCPManager;
128
129
  private readonly toolUiContextSetter: (uiContext: ExtensionUIContext, hasUI: boolean) => void;
129
130
 
130
131
  private readonly commandController: CommandController;
@@ -140,7 +141,7 @@ export class InteractiveMode implements InteractiveModeContext {
140
141
  changelogMarkdown: string | undefined = undefined,
141
142
  setToolUIContext: (uiContext: ExtensionUIContext, hasUI: boolean) => void = () => {},
142
143
  lspServers: Array<{ name: string; status: "ready" | "error"; fileTypes: string[] }> | undefined = undefined,
143
- mcpManager?: import("@oh-my-pi/pi-coding-agent/mcp/index").MCPManager,
144
+ mcpManager?: import("@oh-my-pi/pi-coding-agent/mcp").MCPManager,
144
145
  ) {
145
146
  this.session = session;
146
147
  this.sessionManager = session.sessionManager;
@@ -154,6 +155,7 @@ export class InteractiveMode implements InteractiveModeContext {
154
155
  this.mcpManager = mcpManager;
155
156
 
156
157
  this.ui = new TUI(new ProcessTerminal(), this.settingsManager.getShowHardwareCursor());
158
+ setMermaidRenderCallback(() => this.ui.requestRender());
157
159
  this.chatContainer = new Container();
158
160
  this.pendingMessagesContainer = new Container();
159
161
  this.statusContainer = new Container();
@@ -8,7 +8,7 @@ import type { AgentEvent, AgentMessage, ThinkingLevel } from "@oh-my-pi/pi-agent
8
8
  import type { ImageContent } from "@oh-my-pi/pi-ai";
9
9
  import type { BashResult } from "@oh-my-pi/pi-coding-agent/exec/bash-executor";
10
10
  import type { SessionStats } from "@oh-my-pi/pi-coding-agent/session/agent-session";
11
- import type { CompactionResult } from "@oh-my-pi/pi-coding-agent/session/compaction/index";
11
+ import type { CompactionResult } from "@oh-my-pi/pi-coding-agent/session/compaction";
12
12
  import { createSanitizerStream, createSplitterStream, createTextDecoderStream, ptree } from "@oh-my-pi/pi-utils";
13
13
  import type { RpcCommand, RpcResponse, RpcSessionState } from "./rpc-types";
14
14
 
@@ -11,10 +11,7 @@
11
11
  * - Extension UI: Extension UI requests are emitted, client responds with extension_ui_response
12
12
  */
13
13
 
14
- import type {
15
- ExtensionUIContext,
16
- ExtensionUIDialogOptions,
17
- } from "@oh-my-pi/pi-coding-agent/extensibility/extensions/index";
14
+ import type { ExtensionUIContext, ExtensionUIDialogOptions } from "@oh-my-pi/pi-coding-agent/extensibility/extensions";
18
15
  import { type Theme, theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
19
16
  import type { AgentSession } from "@oh-my-pi/pi-coding-agent/session/agent-session";
20
17
  import { readLines } from "@oh-my-pi/pi-utils";
@@ -9,7 +9,7 @@ import type { AgentMessage, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
9
9
  import type { ImageContent, Model } from "@oh-my-pi/pi-ai";
10
10
  import type { BashResult } from "@oh-my-pi/pi-coding-agent/exec/bash-executor";
11
11
  import type { SessionStats } from "@oh-my-pi/pi-coding-agent/session/agent-session";
12
- import type { CompactionResult } from "@oh-my-pi/pi-coding-agent/session/compaction/index";
12
+ import type { CompactionResult } from "@oh-my-pi/pi-coding-agent/session/compaction";
13
13
 
14
14
  // ============================================================================
15
15
  // RPC Commands (stdin)
@@ -0,0 +1,89 @@
1
+ import {
2
+ extractMermaidBlocks,
3
+ type MermaidImage,
4
+ type MermaidRenderOptions,
5
+ renderMermaidToPng,
6
+ } from "@oh-my-pi/pi-tui";
7
+
8
+ const cache = new Map<string, MermaidImage>();
9
+ const pending = new Map<string, Promise<MermaidImage | null>>();
10
+
11
+ const defaultOptions: MermaidRenderOptions = {
12
+ theme: "dark",
13
+ backgroundColor: "transparent",
14
+ };
15
+
16
+ let onRenderNeeded: (() => void) | null = null;
17
+
18
+ /**
19
+ * Set callback to trigger TUI re-render when mermaid images become available.
20
+ */
21
+ export function setMermaidRenderCallback(callback: (() => void) | null): void {
22
+ onRenderNeeded = callback;
23
+ }
24
+
25
+ /**
26
+ * Get a pre-rendered mermaid image by hash.
27
+ * Returns null if not cached or rendering failed.
28
+ */
29
+ export function getMermaidImage(hash: string): MermaidImage | null {
30
+ return cache.get(hash) ?? null;
31
+ }
32
+
33
+ /**
34
+ * Pre-render all mermaid blocks in markdown text.
35
+ * Renders in parallel, deduplicates concurrent requests.
36
+ * Calls render callback when new images are cached.
37
+ */
38
+ export async function prerenderMermaid(
39
+ markdown: string,
40
+ options: MermaidRenderOptions = defaultOptions,
41
+ ): Promise<void> {
42
+ const blocks = extractMermaidBlocks(markdown);
43
+ if (blocks.length === 0) return;
44
+
45
+ const promises: Promise<boolean>[] = [];
46
+
47
+ for (const { source, hash } of blocks) {
48
+ if (cache.has(hash)) continue;
49
+
50
+ let promise = pending.get(hash);
51
+ if (!promise) {
52
+ promise = renderMermaidToPng(source, options);
53
+ pending.set(hash, promise);
54
+ }
55
+
56
+ promises.push(
57
+ promise.then((image) => {
58
+ pending.delete(hash);
59
+ if (image) {
60
+ cache.set(hash, image);
61
+ return true;
62
+ }
63
+ return false;
64
+ }),
65
+ );
66
+ }
67
+
68
+ const results = await Promise.all(promises);
69
+ const newImages = results.some((added) => added);
70
+
71
+ if (newImages && onRenderNeeded) {
72
+ onRenderNeeded();
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Check if markdown contains mermaid blocks that aren't cached yet.
78
+ */
79
+ export function hasPendingMermaid(markdown: string): boolean {
80
+ const blocks = extractMermaidBlocks(markdown);
81
+ return blocks.some(({ hash }) => !cache.has(hash));
82
+ }
83
+
84
+ /**
85
+ * Clear the mermaid cache.
86
+ */
87
+ export function clearMermaidCache(): void {
88
+ cache.clear();
89
+ }
@@ -11,6 +11,7 @@ import { highlight, supportsLanguage } from "cli-highlight";
11
11
  import darkThemeJson from "./dark.json" with { type: "json" };
12
12
  import { defaultThemes } from "./defaults";
13
13
  import lightThemeJson from "./light.json" with { type: "json" };
14
+ import { getMermaidImage } from "./mermaid-cache";
14
15
 
15
16
  // ============================================================================
16
17
  // Symbol Presets
@@ -2224,6 +2225,7 @@ export function getMarkdownTheme(): MarkdownTheme {
2224
2225
  underline: (text: string) => theme.underline(text),
2225
2226
  strikethrough: (text: string) => chalk.strikethrough(text),
2226
2227
  symbols: getSymbolTheme(),
2228
+ getMermaidImage,
2227
2229
  highlightCode: (code: string, lang?: string): string[] => {
2228
2230
  // Validate language before highlighting to avoid stderr spam from cli-highlight
2229
2231
  const validLang = lang && supportsLanguage(lang) ? lang : undefined;
@@ -2,9 +2,9 @@ import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
2
2
  import type { AssistantMessage, ImageContent, Message, UsageReport } from "@oh-my-pi/pi-ai";
3
3
  import type { KeybindingsManager } from "@oh-my-pi/pi-coding-agent/config/keybindings";
4
4
  import type { SettingsManager } from "@oh-my-pi/pi-coding-agent/config/settings-manager";
5
- import type { ExtensionUIContext } from "@oh-my-pi/pi-coding-agent/extensibility/extensions/index";
5
+ import type { ExtensionUIContext } from "@oh-my-pi/pi-coding-agent/extensibility/extensions";
6
6
  import type { CompactOptions } from "@oh-my-pi/pi-coding-agent/extensibility/extensions/types";
7
- import type { MCPManager } from "@oh-my-pi/pi-coding-agent/mcp/index";
7
+ import type { MCPManager } from "@oh-my-pi/pi-coding-agent/mcp";
8
8
  import type { AgentSession, AgentSessionEvent } from "@oh-my-pi/pi-coding-agent/session/agent-session";
9
9
  import type { HistoryStorage } from "@oh-my-pi/pi-coding-agent/session/history-storage";
10
10
  import type { SessionContext, SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
@@ -18,10 +18,10 @@ import {
18
18
  flushLspWritethroughBatch,
19
19
  type WritethroughCallback,
20
20
  writethroughNoop,
21
- } from "@oh-my-pi/pi-coding-agent/lsp/index";
21
+ } from "@oh-my-pi/pi-coding-agent/lsp";
22
22
  import patchDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/patch.md" with { type: "text" };
23
23
  import replaceDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/replace.md" with { type: "text" };
24
- import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools/index";
24
+ import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
25
25
  import { outputMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
26
26
  import { resolveToCwd } from "@oh-my-pi/pi-coding-agent/tools/path-utils";
27
27
  import { Type } from "@sinclair/typebox";
@@ -48,13 +48,7 @@ export { computeEditDiff, computePatchDiff, generateDiffString, generateUnifiedD
48
48
  export { DEFAULT_FUZZY_THRESHOLD, findContextLine, findMatch as findEditMatch, findMatch, seekSequence } from "./fuzzy";
49
49
 
50
50
  // Normalization
51
- export {
52
- adjustIndentation,
53
- detectLineEnding,
54
- normalizeToLF,
55
- restoreLineEndings,
56
- stripBom,
57
- } from "./normalize";
51
+ export { adjustIndentation, detectLineEnding, normalizeToLF, restoreLineEndings, stripBom } from "./normalize";
58
52
 
59
53
  // Parsing
60
54
  export { normalizeCreateContent, normalizeDiff, parseHunks as parseDiffHunks } from "./parser";
@@ -4,7 +4,7 @@
4
4
 
5
5
  import type { ToolCallContext } from "@oh-my-pi/pi-agent-core";
6
6
  import type { RenderResultOptions } from "@oh-my-pi/pi-coding-agent/extensibility/custom-tools/types";
7
- import type { FileDiagnosticsResult } from "@oh-my-pi/pi-coding-agent/lsp/index";
7
+ import type { FileDiagnosticsResult } from "@oh-my-pi/pi-coding-agent/lsp";
8
8
  import { renderDiff as renderDiffColored } from "@oh-my-pi/pi-coding-agent/modes/components/diff";
9
9
  import { getLanguageFromPath, type Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
10
10
  import type { OutputMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
@@ -17,6 +17,7 @@ import {
17
17
  truncateDiffByHunk,
18
18
  } from "@oh-my-pi/pi-coding-agent/tools/render-utils";
19
19
  import type { RenderCallOptions } from "@oh-my-pi/pi-coding-agent/tools/renderers";
20
+ import { renderStatusLine } from "@oh-my-pi/pi-coding-agent/tui";
20
21
  import type { Component } from "@oh-my-pi/pi-tui";
21
22
  import { Text } from "@oh-my-pi/pi-tui";
22
23
  import type { DiffError, DiffResult, Operation } from "./types";
@@ -178,9 +179,28 @@ export const editToolRenderer = {
178
179
  let text = `${ui.title(opTitle)} ${spinner ? `${spinner} ` : ""}${editIcon} ${pathDisplay}`;
179
180
 
180
181
  // Show streaming preview of diff/content
181
- const streamingContent = args.diff ?? args.newText ?? args.patch;
182
- if (streamingContent) {
183
- text += formatStreamingDiff(streamingContent, rawPath, uiTheme);
182
+ if (args.diff && args.op) {
183
+ text += formatStreamingDiff(args.diff, rawPath, uiTheme);
184
+ } else if (args.diff) {
185
+ const previewLines = args.diff.split("\n");
186
+ const maxLines = 6;
187
+ text += "\n\n";
188
+ for (const line of previewLines.slice(0, maxLines)) {
189
+ text += `${uiTheme.fg("toolOutput", ui.truncate(line, 80))}\n`;
190
+ }
191
+ if (previewLines.length > maxLines) {
192
+ text += uiTheme.fg("dim", `${uiTheme.format.ellipsis} ${previewLines.length - maxLines} more lines`);
193
+ }
194
+ } else if (args.newText || args.patch) {
195
+ const previewLines = (args.newText ?? args.patch ?? "").split("\n");
196
+ const maxLines = 6;
197
+ text += "\n\n";
198
+ for (const line of previewLines.slice(0, maxLines)) {
199
+ text += `${uiTheme.fg("toolOutput", ui.truncate(line, 80))}\n`;
200
+ }
201
+ if (previewLines.length > maxLines) {
202
+ text += uiTheme.fg("dim", `${uiTheme.format.ellipsis} ${previewLines.length - maxLines} more lines`);
203
+ }
184
204
  }
185
205
 
186
206
  return new Text(text, 0, 0);
@@ -221,7 +241,15 @@ export const editToolRenderer = {
221
241
 
222
242
  // Show operation type for patch mode
223
243
  const opTitle = op === "create" ? "Create" : op === "delete" ? "Delete" : "Edit";
224
- let text = `${uiTheme.fg("toolTitle", uiTheme.bold(opTitle))} ${editIcon} ${pathDisplay}`;
244
+ const header = renderStatusLine(
245
+ {
246
+ icon: result.isError ? "error" : "success",
247
+ title: opTitle,
248
+ description: `${editIcon} ${pathDisplay}`,
249
+ },
250
+ uiTheme,
251
+ );
252
+ let text = header;
225
253
 
226
254
  // Skip metadata line for delete operations
227
255
  if (op !== "delete") {
@@ -59,6 +59,8 @@ Results are keyed by task `id` (e.g., "AuthProvider", "AuthApi").
59
59
  If you discussed requirements, plans, schemas, or decisions with the user, you MUST include that information in `context`. Subagents cannot see prior messages—they start fresh with only what you explicitly pass them.
60
60
 
61
61
  **Never call Task multiple times in parallel.** Use a single Task call with multiple entries in the `tasks` array. Parallel Task calls waste resources and bypass coordination.
62
+
63
+ **For code changes, subagents write files directly.** Never ask an agent to "return the changes" for you to apply—they have Edit and Write tools. Their context window holds the work; asking them to report back wastes it.
62
64
  </critical>
63
65
 
64
66
  <example>
package/src/sdk.ts CHANGED
@@ -26,31 +26,34 @@
26
26
  * ```
27
27
  */
28
28
 
29
+ import { existsSync } from "node:fs";
30
+ import { rename } from "node:fs/promises";
29
31
  import { join } from "node:path";
30
32
  import { Agent, type AgentEvent, type AgentMessage, type AgentTool, type ThinkingLevel } from "@oh-my-pi/pi-agent-core";
31
33
  import { type Message, type Model, supportsXhigh } from "@oh-my-pi/pi-ai";
32
- import { loadCapability } from "@oh-my-pi/pi-coding-agent/capability/index";
34
+ // Import discovery to register all providers on startup
35
+ import { loadCapability } from "@oh-my-pi/pi-coding-agent/capability";
33
36
  import { type Rule, ruleCapability } from "@oh-my-pi/pi-coding-agent/capability/rule";
34
37
  import { getAgentDir, getConfigDirPaths } from "@oh-my-pi/pi-coding-agent/config";
35
- import type { Component } from "@oh-my-pi/pi-tui";
36
- // Import discovery to register all providers on startup
37
- import { logger, postmortem } from "@oh-my-pi/pi-utils";
38
- import chalk from "chalk";
39
- import "./discovery";
40
38
  import { CursorExecHandlers } from "@oh-my-pi/pi-coding-agent/cursor";
41
39
  import { initializeWithSettings } from "@oh-my-pi/pi-coding-agent/discovery";
42
40
  import { TtsrManager } from "@oh-my-pi/pi-coding-agent/export/ttsr";
43
41
  import { disposeAllKernelSessions } from "@oh-my-pi/pi-coding-agent/ipy/executor";
44
42
  import { closeAllConnections } from "@oh-my-pi/pi-coding-agent/ssh/connection-manager";
45
43
  import { unmountAll } from "@oh-my-pi/pi-coding-agent/ssh/sshfs-mount";
44
+ import type { Component } from "@oh-my-pi/pi-tui";
45
+ import { logger, postmortem } from "@oh-my-pi/pi-utils";
46
+ import { YAML } from "bun";
47
+ import chalk from "chalk";
46
48
  import { ModelRegistry } from "./config/model-registry";
47
49
  import { formatModelString, parseModelString } from "./config/model-resolver";
48
50
  import { loadPromptTemplates as loadPromptTemplatesInternal, type PromptTemplate } from "./config/prompt-templates";
49
51
  import { type Settings, SettingsManager, type SkillsSettings } from "./config/settings-manager";
52
+ import "./discovery";
50
53
  import {
51
54
  type CustomCommandsLoadResult,
52
55
  loadCustomCommands as loadCustomCommandsInternal,
53
- } from "./extensibility/custom-commands/index";
56
+ } from "./extensibility/custom-commands";
54
57
  import type { CustomTool, CustomToolContext, CustomToolSessionEvent } from "./extensibility/custom-tools/types";
55
58
  import {
56
59
  discoverAndLoadExtensions,
@@ -64,7 +67,7 @@ import {
64
67
  loadExtensions,
65
68
  type ToolDefinition,
66
69
  wrapRegisteredTools,
67
- } from "./extensibility/extensions/index";
70
+ } from "./extensibility/extensions";
68
71
  import { loadSkills as loadSkillsInternal, type Skill, type SkillWarning } from "./extensibility/skills";
69
72
  import { type FileSlashCommand, loadSlashCommands as loadSlashCommandsInternal } from "./extensibility/slash-commands";
70
73
  import {
@@ -74,7 +77,7 @@ import {
74
77
  RuleProtocolHandler,
75
78
  SkillProtocolHandler,
76
79
  } from "./internal-urls";
77
- import { discoverAndLoadMCPTools, type MCPManager, type MCPToolsLoadResult } from "./mcp/index";
80
+ import { discoverAndLoadMCPTools, type MCPManager, type MCPToolsLoadResult } from "./mcp";
78
81
  import { AgentSession } from "./session/agent-session";
79
82
  import { AuthStorage } from "./session/auth-storage";
80
83
  import { convertToLlm } from "./session/messages";
@@ -84,8 +87,7 @@ import {
84
87
  buildSystemPrompt as buildSystemPromptInternal,
85
88
  loadProjectContextFiles as loadContextFilesInternal,
86
89
  } from "./system-prompt";
87
- import { ToolContextStore } from "./tools/context";
88
- import { getGeminiImageTools } from "./tools/gemini-image";
90
+ import { AgentOutputManager } from "./task/output-manager";
89
91
  import {
90
92
  BashTool,
91
93
  BUILTIN_TOOLS,
@@ -104,7 +106,9 @@ import {
104
106
  type ToolSession,
105
107
  WriteTool,
106
108
  warmupLspServers,
107
- } from "./tools/index";
109
+ } from "./tools";
110
+ import { ToolContextStore } from "./tools/context";
111
+ import { getGeminiImageTools } from "./tools/gemini-image";
108
112
  import { wrapToolsWithMetaNotice } from "./tools/output-meta";
109
113
  import { EventBus } from "./utils/event-bus";
110
114
  import { time } from "./utils/timings";
@@ -214,11 +218,11 @@ export type {
214
218
  ExtensionContext,
215
219
  ExtensionFactory,
216
220
  ToolDefinition,
217
- } from "@oh-my-pi/pi-coding-agent/extensibility/extensions/index";
221
+ } from "@oh-my-pi/pi-coding-agent/extensibility/extensions";
218
222
  export type { Skill } from "@oh-my-pi/pi-coding-agent/extensibility/skills";
219
223
  export type { FileSlashCommand } from "@oh-my-pi/pi-coding-agent/extensibility/slash-commands";
220
- export type { MCPManager, MCPServerConfig, MCPServerConnection, MCPToolsLoadResult } from "./mcp/index";
221
- export type { Tool } from "./tools/index";
224
+ export type { MCPManager, MCPServerConfig, MCPServerConnection, MCPToolsLoadResult } from "./mcp";
225
+ export type { Tool } from "./tools";
222
226
 
223
227
  export {
224
228
  // Individual tool classes (for custom usage)
@@ -264,31 +268,64 @@ export async function discoverAuthStorage(agentDir: string = getDefaultAgentDir(
264
268
  authPaths: [primaryPath, ...fallbackPaths],
265
269
  });
266
270
 
267
- const storage = new AuthStorage(primaryPath, fallbackPaths);
271
+ const storage = await AuthStorage.create(primaryPath, fallbackPaths);
268
272
  await storage.reload();
269
273
  return storage;
270
274
  }
271
275
 
272
276
  /**
273
277
  * Create a ModelRegistry with fallback support.
274
- * Reads from primary path first, then falls back to legacy paths (.pi, .claude).
278
+ * Prefers models.yml over models.json. Reads from primary path first,
279
+ * then falls back to legacy paths (.pi, .claude).
275
280
  */
276
281
  export async function discoverModels(
277
282
  authStorage: AuthStorage,
278
283
  agentDir: string = getDefaultAgentDir(),
279
284
  ): Promise<ModelRegistry> {
280
- const primaryPath = join(agentDir, "models.json");
281
- // Get all models.json paths (user-level only), excluding the primary
282
- const allPaths = getConfigDirPaths("models.json", { project: false });
283
- const fallbackPaths = allPaths.filter((p) => p !== primaryPath);
285
+ const yamlPath = join(agentDir, "models.yml");
286
+ const jsonPath = join(agentDir, "models.json");
287
+
288
+ // Migrate models.json to models.yml if yaml doesn't exist but json does
289
+ if (!existsSync(yamlPath) && existsSync(jsonPath)) {
290
+ await migrateModelsJsonToYaml(jsonPath, yamlPath);
291
+ }
292
+
293
+ // Prefer models.yml, fall back to models.json
294
+ const primaryPath = existsSync(yamlPath) ? yamlPath : jsonPath;
295
+
296
+ // Get all models config paths (user-level only), excluding the primary
297
+ const yamlPaths = getConfigDirPaths("models.yml", { project: false });
298
+ const jsonPaths = getConfigDirPaths("models.json", { project: false });
299
+ const allPaths = [...yamlPaths, ...jsonPaths];
300
+ const fallbackPaths = allPaths.filter((p) => p !== primaryPath && existsSync(p));
284
301
 
285
302
  logger.debug("discoverModels", { primaryPath, fallbackPaths });
286
303
 
287
304
  const registry = new ModelRegistry(authStorage, primaryPath, fallbackPaths);
288
- await registry.refresh();
305
+ registry.refresh();
289
306
  return registry;
290
307
  }
291
308
 
309
+ /**
310
+ * Migrate models.json to models.yml.
311
+ * Creates models.yml from models.json and renames the json file to .bak.
312
+ */
313
+ async function migrateModelsJsonToYaml(jsonPath: string, yamlPath: string): Promise<void> {
314
+ try {
315
+ const content = await Bun.file(jsonPath).text();
316
+ const parsed = JSON.parse(content);
317
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
318
+ logger.warn("migrateModelsJsonToYaml: invalid models.json structure", { path: jsonPath });
319
+ return;
320
+ }
321
+ await Bun.write(yamlPath, YAML.stringify(parsed, null, 2));
322
+ await rename(jsonPath, `${jsonPath}.bak`);
323
+ logger.debug("migrateModelsJsonToYaml: migrated models.json to models.yml", { from: jsonPath, to: yamlPath });
324
+ } catch (error) {
325
+ logger.warn("migrateModelsJsonToYaml: migration failed", { error: String(error) });
326
+ }
327
+ }
328
+
292
329
  /**
293
330
  * Discover extensions from cwd.
294
331
  */
@@ -734,6 +771,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
734
771
  );
735
772
  toolSession.internalRouter = internalRouter;
736
773
  toolSession.getArtifactsDir = getArtifactsDir;
774
+ toolSession.agentOutputManager = new AgentOutputManager(getArtifactsDir);
737
775
 
738
776
  // Create and wrap tools with meta notice formatting
739
777
  const rawBuiltinTools = await createTools(toolSession, options.toolNames);
@@ -38,9 +38,9 @@ import {
38
38
  } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
39
39
  import type { SettingsManager, SkillsSettings } from "@oh-my-pi/pi-coding-agent/config/settings-manager";
40
40
  import { type BashResult, executeBash as executeBashCommand } from "@oh-my-pi/pi-coding-agent/exec/bash-executor";
41
- import { exportSessionToHtml } from "@oh-my-pi/pi-coding-agent/export/html/index";
41
+ import { exportSessionToHtml } from "@oh-my-pi/pi-coding-agent/export/html";
42
42
  import type { TtsrManager } from "@oh-my-pi/pi-coding-agent/export/ttsr";
43
- import type { LoadedCustomCommand } from "@oh-my-pi/pi-coding-agent/extensibility/custom-commands/index";
43
+ import type { LoadedCustomCommand } from "@oh-my-pi/pi-coding-agent/extensibility/custom-commands";
44
44
  import type {
45
45
  ExtensionCommandContext,
46
46
  ExtensionRunner,
@@ -78,7 +78,7 @@ import {
78
78
  generateBranchSummary,
79
79
  prepareCompaction,
80
80
  shouldCompact,
81
- } from "./compaction/index";
81
+ } from "./compaction";
82
82
  import {
83
83
  type BashExecutionMessage,
84
84
  type BranchSummaryMessage,