@oh-my-pi/pi-coding-agent 13.18.0 → 14.0.2

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 (235) hide show
  1. package/CHANGELOG.md +316 -1
  2. package/package.json +86 -24
  3. package/scripts/format-prompts.ts +2 -2
  4. package/src/autoresearch/apply-contract-to-state.ts +24 -0
  5. package/src/autoresearch/contract.ts +0 -44
  6. package/src/autoresearch/dashboard.ts +1 -2
  7. package/src/autoresearch/git.ts +116 -30
  8. package/src/autoresearch/helpers.ts +49 -0
  9. package/src/autoresearch/index.ts +28 -187
  10. package/src/autoresearch/prompt.md +26 -9
  11. package/src/autoresearch/state.ts +0 -6
  12. package/src/autoresearch/tools/init-experiment.ts +202 -117
  13. package/src/autoresearch/tools/log-experiment.ts +123 -178
  14. package/src/autoresearch/tools/run-experiment.ts +48 -10
  15. package/src/autoresearch/types.ts +2 -2
  16. package/src/capability/index.ts +4 -2
  17. package/src/cli/file-processor.ts +3 -3
  18. package/src/cli/grep-cli.ts +8 -8
  19. package/src/cli/grievances-cli.ts +78 -0
  20. package/src/cli/read-cli.ts +67 -0
  21. package/src/cli/setup-cli.ts +4 -4
  22. package/src/cli/update-cli.ts +3 -3
  23. package/src/cli.ts +2 -0
  24. package/src/commands/grep.ts +6 -1
  25. package/src/commands/grievances.ts +20 -0
  26. package/src/commands/read.ts +33 -0
  27. package/src/commit/agentic/agent.ts +5 -8
  28. package/src/commit/agentic/index.ts +22 -26
  29. package/src/commit/agentic/tools/analyze-file.ts +3 -3
  30. package/src/commit/agentic/tools/git-file-diff.ts +3 -6
  31. package/src/commit/agentic/tools/git-hunk.ts +3 -3
  32. package/src/commit/agentic/tools/git-overview.ts +6 -9
  33. package/src/commit/agentic/tools/index.ts +6 -8
  34. package/src/commit/agentic/tools/propose-commit.ts +4 -7
  35. package/src/commit/agentic/tools/recent-commits.ts +3 -3
  36. package/src/commit/agentic/tools/split-commit.ts +4 -4
  37. package/src/commit/agentic/validation.ts +1 -1
  38. package/src/commit/analysis/conventional.ts +4 -4
  39. package/src/commit/analysis/summary.ts +3 -3
  40. package/src/commit/changelog/generate.ts +4 -4
  41. package/src/commit/changelog/index.ts +5 -9
  42. package/src/commit/map-reduce/map-phase.ts +4 -4
  43. package/src/commit/map-reduce/reduce-phase.ts +4 -4
  44. package/src/commit/pipeline.ts +13 -16
  45. package/src/config/keybindings.ts +7 -6
  46. package/src/config/prompt-templates.ts +44 -226
  47. package/src/config/resolve-config-value.ts +4 -2
  48. package/src/config/settings-schema.ts +98 -2
  49. package/src/config/settings.ts +25 -26
  50. package/src/dap/client.ts +674 -0
  51. package/src/dap/config.ts +150 -0
  52. package/src/dap/defaults.json +211 -0
  53. package/src/dap/index.ts +4 -0
  54. package/src/dap/session.ts +1255 -0
  55. package/src/dap/types.ts +600 -0
  56. package/src/debug/log-viewer.ts +3 -2
  57. package/src/discovery/builtin.ts +1 -2
  58. package/src/discovery/codex.ts +2 -2
  59. package/src/discovery/github.ts +2 -1
  60. package/src/discovery/helpers.ts +2 -2
  61. package/src/discovery/opencode.ts +2 -2
  62. package/src/edit/diff.ts +818 -0
  63. package/src/edit/index.ts +309 -0
  64. package/src/edit/line-hash.ts +67 -0
  65. package/src/edit/modes/chunk.ts +454 -0
  66. package/src/{patch → edit/modes}/hashline.ts +741 -361
  67. package/src/{patch/applicator.ts → edit/modes/patch.ts} +420 -117
  68. package/src/{patch/fuzzy.ts → edit/modes/replace.ts} +519 -197
  69. package/src/{patch → edit}/normalize.ts +97 -76
  70. package/src/{patch/shared.ts → edit/renderer.ts} +181 -108
  71. package/src/exec/bash-executor.ts +4 -2
  72. package/src/exec/idle-timeout-watchdog.ts +126 -0
  73. package/src/exec/non-interactive-env.ts +5 -0
  74. package/src/extensibility/custom-commands/bundled/ci-green/index.ts +6 -18
  75. package/src/extensibility/custom-commands/bundled/review/index.ts +45 -43
  76. package/src/extensibility/custom-commands/loader.ts +1 -2
  77. package/src/extensibility/custom-tools/loader.ts +34 -11
  78. package/src/extensibility/custom-tools/types.ts +1 -1
  79. package/src/extensibility/extensions/loader.ts +9 -4
  80. package/src/extensibility/extensions/runner.ts +24 -1
  81. package/src/extensibility/extensions/types.ts +4 -2
  82. package/src/extensibility/hooks/loader.ts +5 -6
  83. package/src/extensibility/hooks/types.ts +2 -2
  84. package/src/extensibility/plugins/doctor.ts +2 -1
  85. package/src/extensibility/plugins/marketplace/fetcher.ts +2 -57
  86. package/src/extensibility/plugins/marketplace/source-resolver.ts +4 -4
  87. package/src/extensibility/slash-commands.ts +3 -7
  88. package/src/index.ts +3 -1
  89. package/src/internal-urls/docs-index.generated.ts +11 -11
  90. package/src/ipy/executor.ts +58 -17
  91. package/src/ipy/gateway-coordinator.ts +6 -4
  92. package/src/ipy/kernel.ts +45 -22
  93. package/src/ipy/runtime.ts +2 -2
  94. package/src/lsp/client.ts +7 -4
  95. package/src/lsp/clients/lsp-linter-client.ts +4 -4
  96. package/src/lsp/config.ts +2 -2
  97. package/src/lsp/defaults.json +688 -154
  98. package/src/lsp/index.ts +234 -45
  99. package/src/lsp/lspmux.ts +2 -2
  100. package/src/lsp/startup-events.ts +13 -0
  101. package/src/lsp/types.ts +12 -1
  102. package/src/lsp/utils.ts +8 -1
  103. package/src/main.ts +125 -47
  104. package/src/memories/index.ts +4 -5
  105. package/src/modes/acp/acp-agent.ts +563 -163
  106. package/src/modes/acp/acp-event-mapper.ts +9 -1
  107. package/src/modes/acp/acp-mode.ts +4 -2
  108. package/src/modes/components/agent-dashboard.ts +3 -4
  109. package/src/modes/components/diff.ts +6 -7
  110. package/src/modes/components/footer.ts +9 -29
  111. package/src/modes/components/hook-editor.ts +3 -3
  112. package/src/modes/components/hook-selector.ts +6 -1
  113. package/src/modes/components/read-tool-group.ts +6 -12
  114. package/src/modes/components/session-observer-overlay.ts +472 -0
  115. package/src/modes/components/settings-defs.ts +24 -0
  116. package/src/modes/components/status-line.ts +15 -61
  117. package/src/modes/components/tool-execution.ts +1 -1
  118. package/src/modes/components/welcome.ts +1 -1
  119. package/src/modes/controllers/btw-controller.ts +2 -2
  120. package/src/modes/controllers/command-controller.ts +4 -2
  121. package/src/modes/controllers/event-controller.ts +59 -2
  122. package/src/modes/controllers/extension-ui-controller.ts +1 -0
  123. package/src/modes/controllers/input-controller.ts +15 -8
  124. package/src/modes/controllers/selector-controller.ts +26 -0
  125. package/src/modes/index.ts +20 -2
  126. package/src/modes/interactive-mode.ts +278 -69
  127. package/src/modes/rpc/host-tools.ts +186 -0
  128. package/src/modes/rpc/rpc-client.ts +178 -13
  129. package/src/modes/rpc/rpc-mode.ts +73 -3
  130. package/src/modes/rpc/rpc-types.ts +53 -1
  131. package/src/modes/session-observer-registry.ts +146 -0
  132. package/src/modes/shared.ts +0 -42
  133. package/src/modes/theme/theme.ts +80 -8
  134. package/src/modes/types.ts +4 -2
  135. package/src/modes/utils/keybinding-matchers.ts +9 -0
  136. package/src/prompts/system/custom-system-prompt.md +5 -0
  137. package/src/prompts/system/system-prompt.md +8 -1
  138. package/src/prompts/tools/chunk-edit.md +219 -0
  139. package/src/prompts/tools/debug.md +43 -0
  140. package/src/prompts/tools/grep.md +3 -0
  141. package/src/prompts/tools/lsp.md +5 -5
  142. package/src/prompts/tools/read-chunk.md +17 -0
  143. package/src/prompts/tools/read.md +19 -5
  144. package/src/sdk.ts +216 -165
  145. package/src/secrets/index.ts +1 -1
  146. package/src/secrets/obfuscator.ts +25 -17
  147. package/src/session/agent-session.ts +381 -286
  148. package/src/session/agent-storage.ts +12 -12
  149. package/src/session/compaction/branch-summarization.ts +3 -3
  150. package/src/session/compaction/compaction.ts +5 -6
  151. package/src/session/compaction/utils.ts +3 -3
  152. package/src/session/history-storage.ts +62 -19
  153. package/src/session/messages.ts +3 -3
  154. package/src/session/session-dump-format.ts +203 -0
  155. package/src/session/session-manager.ts +15 -5
  156. package/src/session/session-storage.ts +4 -2
  157. package/src/session/streaming-output.ts +1 -1
  158. package/src/session/tool-choice-queue.ts +213 -0
  159. package/src/slash-commands/builtin-registry.ts +56 -8
  160. package/src/ssh/connection-manager.ts +2 -2
  161. package/src/ssh/sshfs-mount.ts +5 -5
  162. package/src/stt/downloader.ts +4 -4
  163. package/src/stt/recorder.ts +4 -4
  164. package/src/stt/transcriber.ts +2 -2
  165. package/src/system-prompt.ts +25 -13
  166. package/src/task/agents.ts +5 -6
  167. package/src/task/commands.ts +2 -5
  168. package/src/task/executor.ts +32 -4
  169. package/src/task/index.ts +91 -82
  170. package/src/task/template.ts +2 -2
  171. package/src/task/types.ts +25 -0
  172. package/src/task/worktree.ts +131 -149
  173. package/src/tools/ask.ts +2 -3
  174. package/src/tools/ast-edit.ts +7 -7
  175. package/src/tools/ast-grep.ts +7 -7
  176. package/src/tools/auto-generated-guard.ts +36 -41
  177. package/src/tools/await-tool.ts +2 -2
  178. package/src/tools/bash.ts +5 -23
  179. package/src/tools/browser.ts +4 -5
  180. package/src/tools/calculator.ts +2 -3
  181. package/src/tools/cancel-job.ts +2 -2
  182. package/src/tools/checkpoint.ts +3 -3
  183. package/src/tools/debug.ts +1007 -0
  184. package/src/tools/exit-plan-mode.ts +3 -3
  185. package/src/tools/fetch.ts +67 -3
  186. package/src/tools/find.ts +4 -5
  187. package/src/tools/fs-cache-invalidation.ts +5 -0
  188. package/src/tools/gemini-image.ts +13 -5
  189. package/src/tools/gh.ts +130 -308
  190. package/src/tools/grep.ts +57 -9
  191. package/src/tools/index.ts +44 -22
  192. package/src/tools/inspect-image.ts +4 -4
  193. package/src/tools/output-meta.ts +1 -1
  194. package/src/tools/python.ts +19 -6
  195. package/src/tools/read.ts +211 -146
  196. package/src/tools/render-mermaid.ts +2 -3
  197. package/src/tools/render-utils.ts +20 -6
  198. package/src/tools/renderers.ts +3 -1
  199. package/src/tools/report-tool-issue.ts +80 -0
  200. package/src/tools/resolve.ts +70 -39
  201. package/src/tools/search-tool-bm25.ts +2 -2
  202. package/src/tools/ssh.ts +2 -2
  203. package/src/tools/todo-write.ts +2 -2
  204. package/src/tools/tool-timeouts.ts +1 -0
  205. package/src/tools/write.ts +5 -6
  206. package/src/tui/tree-list.ts +3 -1
  207. package/src/utils/clipboard.ts +80 -0
  208. package/src/utils/commit-message-generator.ts +2 -3
  209. package/src/utils/edit-mode.ts +49 -0
  210. package/src/utils/external-editor.ts +11 -5
  211. package/src/utils/file-display-mode.ts +6 -5
  212. package/src/utils/file-mentions.ts +8 -7
  213. package/src/utils/git.ts +1400 -0
  214. package/src/utils/image-loading.ts +98 -0
  215. package/src/utils/title-generator.ts +2 -3
  216. package/src/utils/tools-manager.ts +6 -6
  217. package/src/web/scrapers/choosealicense.ts +1 -1
  218. package/src/web/search/index.ts +3 -3
  219. package/src/web/search/render.ts +6 -4
  220. package/src/autoresearch/command-initialize.md +0 -34
  221. package/src/commit/git/errors.ts +0 -9
  222. package/src/commit/git/index.ts +0 -210
  223. package/src/commit/git/operations.ts +0 -54
  224. package/src/patch/diff.ts +0 -433
  225. package/src/patch/index.ts +0 -888
  226. package/src/patch/parser.ts +0 -532
  227. package/src/patch/types.ts +0 -292
  228. package/src/prompts/agents/oracle.md +0 -77
  229. package/src/tools/gh-cli.ts +0 -125
  230. package/src/tools/pending-action.ts +0 -49
  231. package/src/utils/child-process.ts +0 -88
  232. package/src/utils/frontmatter.ts +0 -117
  233. package/src/utils/image-input.ts +0 -274
  234. package/src/utils/mime.ts +0 -53
  235. package/src/utils/prompt-format.ts +0 -170
package/src/main.ts CHANGED
@@ -22,7 +22,7 @@ import { selectSession } from "./cli/session-picker";
22
22
  import { findConfigFile } from "./config";
23
23
  import { ModelRegistry, ModelsConfigFile } from "./config/model-registry";
24
24
  import { resolveCliModel, resolveModelRoleValue, resolveModelScope, type ScopedModel } from "./config/model-resolver";
25
- import { Settings, settings } from "./config/settings";
25
+ import { getDefault, type SettingPath, Settings, settings } from "./config/settings";
26
26
  import { initializeWithSettings } from "./discovery";
27
27
  import {
28
28
  clearClaudePluginRootsCache,
@@ -47,7 +47,9 @@ import { type CreateAgentSessionOptions, createAgentSession, discoverAuthStorage
47
47
  import type { AgentSession } from "./session/agent-session";
48
48
  import { resolveResumableSession, type SessionInfo, SessionManager } from "./session/session-manager";
49
49
  import { resolvePromptInput } from "./system-prompt";
50
+ import type { LspStartupServerInfo } from "./tools";
50
51
  import { getChangelogPath, getNewEntries, parseChangelog } from "./utils/changelog";
52
+ import type { EventBus } from "./utils/event-bus";
51
53
 
52
54
  async function checkForNewVersion(currentVersion: string): Promise<string | undefined> {
53
55
  if (!settings.get("startup.checkUpdate")) {
@@ -70,6 +72,29 @@ async function checkForNewVersion(currentVersion: string): Promise<string | unde
70
72
  }
71
73
  }
72
74
 
75
+ const RPC_DEFAULTED_SETTING_PATHS: SettingPath[] = [
76
+ "todo.enabled",
77
+ "todo.reminders",
78
+ "todo.reminders.max",
79
+ "todo.eager",
80
+ "async.enabled",
81
+ "async.maxJobs",
82
+ "task.isolation.mode",
83
+ "task.isolation.merge",
84
+ "task.isolation.commits",
85
+ "task.eager",
86
+ "task.maxConcurrency",
87
+ "task.maxRecursionDepth",
88
+ "task.disabledAgents",
89
+ "task.agentModelOverrides",
90
+ ];
91
+
92
+ function applyRpcDefaultSettingOverrides(): void {
93
+ for (const settingPath of RPC_DEFAULTED_SETTING_PATHS) {
94
+ settings.override(settingPath, getDefault(settingPath));
95
+ }
96
+ }
97
+
73
98
  async function readPipedInput(): Promise<string | undefined> {
74
99
  if (process.stdin.isTTY !== false) return undefined;
75
100
  try {
@@ -117,12 +142,21 @@ async function runInteractiveMode(
117
142
  versionCheckPromise: Promise<string | undefined>,
118
143
  initialMessages: string[],
119
144
  setExtensionUIContext: (uiContext: ExtensionUIContext, hasUI: boolean) => void,
120
- lspServers: Array<{ name: string; status: "ready" | "error"; fileTypes: string[]; error?: string }> | undefined,
145
+ lspServers: LspStartupServerInfo[] | undefined,
121
146
  mcpManager: MCPManager | undefined,
147
+ eventBus?: EventBus,
122
148
  initialMessage?: string,
123
149
  initialImages?: ImageContent[],
124
150
  ): Promise<void> {
125
- const mode = new InteractiveMode(session, version, changelogMarkdown, setExtensionUIContext, lspServers, mcpManager);
151
+ const mode = new InteractiveMode(
152
+ session,
153
+ version,
154
+ changelogMarkdown,
155
+ setExtensionUIContext,
156
+ lspServers,
157
+ mcpManager,
158
+ eventBus,
159
+ );
126
160
 
127
161
  await mode.init();
128
162
 
@@ -273,6 +307,17 @@ async function createSessionManager(parsed: Args, cwd: string): Promise<SessionM
273
307
  if (parsed.sessionDir) {
274
308
  return SessionManager.create(cwd, parsed.sessionDir);
275
309
  }
310
+ // Auto-resume: behave like --continue if the setting is enabled and a prior
311
+ // session exists. When a prior session is resumed, mark parsed.continue so
312
+ // buildSessionOptions restores the session's model/thinking instead of
313
+ // overriding them with CLI defaults.
314
+ if (settings.get("autoResume")) {
315
+ const manager = await SessionManager.continueRecent(cwd, parsed.sessionDir);
316
+ if (manager.getEntries().length > 0) {
317
+ parsed.continue = true;
318
+ }
319
+ return manager;
320
+ }
276
321
  // Default case (new session) returns undefined, SDK will create one
277
322
  return undefined;
278
323
  }
@@ -521,19 +566,16 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
521
566
 
522
567
  // Initialize theme early with defaults (CLI commands need symbols)
523
568
  // Will be re-initialized with user preferences later
524
- await logger.timeAsync("initTheme:initial", () => initTheme());
569
+ await logger.time("initTheme:initial", initTheme);
525
570
 
526
571
  const parsedArgs = parsed;
527
- await logger.timeAsync("maybeAutoChdir", () => maybeAutoChdir(parsedArgs));
572
+ await logger.time("maybeAutoChdir", maybeAutoChdir, parsedArgs);
528
573
 
529
574
  const notifs: (InteractiveModeNotify | null)[] = [];
530
575
 
531
576
  // Create AuthStorage and ModelRegistry upfront
532
- const { authStorage, modelRegistry } = await logger.timeAsync("discoverModels", async () => {
533
- const authStorage = await discoverAuthStorage();
534
- const modelRegistry = new ModelRegistry(authStorage);
535
- return { authStorage, modelRegistry };
536
- });
577
+ const authStorage = await logger.time("discoverModels", discoverAuthStorage);
578
+ const modelRegistry = new ModelRegistry(authStorage);
537
579
 
538
580
  if (parsedArgs.version) {
539
581
  process.stdout.write(`${VERSION}\n`);
@@ -541,7 +583,7 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
541
583
  }
542
584
 
543
585
  if (parsedArgs.listModels !== undefined) {
544
- await logger.timeAsync("settings:init:list-models", () => Settings.init({ cwd: getProjectDir() }));
586
+ await logger.time("settings:init:list-models", Settings.init, { cwd: getProjectDir() });
545
587
  await modelRegistry.refresh("online");
546
588
  const searchPattern = typeof parsedArgs.listModels === "string" ? parsedArgs.listModels : undefined;
547
589
  await listModels(modelRegistry, searchPattern);
@@ -568,24 +610,25 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
568
610
  }
569
611
 
570
612
  const cwd = getProjectDir();
571
- await logger.timeAsync("settings:init", () => Settings.init({ cwd }));
613
+ await logger.time("settings:init", Settings.init, { cwd });
614
+ if (parsedArgs.mode === "rpc") {
615
+ applyRpcDefaultSettingOverrides();
616
+ }
572
617
  if (parsedArgs.noPty) {
573
618
  Bun.env.PI_NO_PTY = "1";
574
619
  }
575
- const { pipedInput, fileText, fileImages } = await logger.timeAsync("prepareInitialMessage", async () => {
620
+ if (parsedArgs.noTitle || parsedArgs.mode === "rpc") {
621
+ Bun.env.PI_NO_TITLE = "1";
622
+ }
623
+ const { pipedInput, fileText, fileImages } = await logger.time("prepareInitialMessage", async () => {
576
624
  const pipedInput = await readPipedInput();
577
625
  if (parsedArgs.fileArgs.length === 0) {
578
- return { pipedInput };
626
+ return { pipedInput, fileText: undefined, fileImages: undefined };
579
627
  }
580
-
581
- const { text, images } = await processFileArguments(parsedArgs.fileArgs, {
628
+ const processed = await processFileArguments(parsedArgs.fileArgs, {
582
629
  autoResizeImages: settings.get("images.autoResize"),
583
630
  });
584
- return {
585
- pipedInput,
586
- fileText: text,
587
- fileImages: images,
588
- };
631
+ return { pipedInput, fileText: processed.text, fileImages: processed.images };
589
632
  });
590
633
  const { initialMessage, initialImages } = buildInitialMessage({
591
634
  parsed: parsedArgs,
@@ -598,7 +641,8 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
598
641
  const mode = parsedArgs.mode || "text";
599
642
 
600
643
  // Initialize discovery system with settings for provider persistence
601
- logger.time("initializeWithSettings", () => initializeWithSettings(settings));
644
+ logger.time("initializeWithSettings");
645
+ initializeWithSettings(settings);
602
646
  modelRegistry.refreshInBackground();
603
647
 
604
648
  // Apply model role overrides from CLI args or env vars (ephemeral, not persisted)
@@ -613,14 +657,14 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
613
657
  });
614
658
  }
615
659
 
616
- await logger.timeAsync("initTheme:final", () =>
617
- initTheme(
618
- isInteractive,
619
- settings.get("symbolPreset"),
620
- settings.get("colorBlindMode"),
621
- settings.get("theme.dark"),
622
- settings.get("theme.light"),
623
- ),
660
+ await logger.time(
661
+ "initTheme:final",
662
+ initTheme,
663
+ isInteractive,
664
+ settings.get("symbolPreset"),
665
+ settings.get("colorBlindMode"),
666
+ settings.get("theme.dark"),
667
+ settings.get("theme.light"),
624
668
  );
625
669
 
626
670
  let scopedModels: ScopedModel[] = [];
@@ -629,24 +673,26 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
629
673
  usageOrder: settings.getStorage()?.getModelUsageOrder(),
630
674
  };
631
675
  if (modelPatterns && modelPatterns.length > 0) {
632
- scopedModels = await logger.timeAsync("resolveModelScope", () =>
633
- resolveModelScope(modelPatterns, modelRegistry, modelMatchPreferences),
676
+ scopedModels = await logger.time(
677
+ "resolveModelScope",
678
+ resolveModelScope,
679
+ modelPatterns,
680
+ modelRegistry,
681
+ modelMatchPreferences,
634
682
  );
635
683
  }
636
684
 
637
685
  // Create session manager based on CLI flags
638
- let sessionManager = await logger.timeAsync("createSessionManager", () => createSessionManager(parsedArgs, cwd));
686
+ let sessionManager = await logger.time("createSessionManager", createSessionManager, parsedArgs, cwd);
639
687
 
640
688
  // Handle --resume (no value): show session picker
641
689
  if (parsedArgs.resume === true && !parsedArgs.fork) {
642
- const sessions = await logger.timeAsync("SessionManager.list", () =>
643
- SessionManager.list(cwd, parsedArgs.sessionDir),
644
- );
690
+ const sessions = await logger.time("SessionManager.list", SessionManager.list, cwd, parsedArgs.sessionDir);
645
691
  if (sessions.length === 0) {
646
692
  process.stdout.write(`${chalk.dim("No sessions found")}\n`);
647
693
  return;
648
694
  }
649
- const selectedPath = await logger.timeAsync("selectSession", () => selectSession(sessions));
695
+ const selectedPath = await logger.time("selectSession", selectSession, sessions);
650
696
  if (!selectedPath) {
651
697
  process.stdout.write(`${chalk.dim("No session selected")}\n`);
652
698
  return;
@@ -657,11 +703,9 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
657
703
  // Wire --plugin-dir and preload plugin roots for sync consumers (LSP config)
658
704
  const home = os.homedir();
659
705
  if (parsedArgs.pluginDirs && parsedArgs.pluginDirs.length > 0) {
660
- await logger.timeAsync("injectPluginDirRoots", () =>
661
- injectPluginDirRoots(home, parsedArgs.pluginDirs!, getProjectDir()),
662
- );
706
+ await logger.time("injectPluginDirRoots", injectPluginDirRoots, home, parsedArgs.pluginDirs!, getProjectDir());
663
707
  } else {
664
- await logger.timeAsync("preloadPluginRoots", () => preloadPluginRoots(home, getProjectDir()));
708
+ await logger.time("preloadPluginRoots", preloadPluginRoots, home, getProjectDir());
665
709
  }
666
710
 
667
711
  // Background marketplace auto-update — never blocks startup.
@@ -698,8 +742,13 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
698
742
  })();
699
743
  }
700
744
 
701
- const { options: sessionOptions } = await logger.timeAsync("buildSessionOptions", () =>
702
- buildSessionOptions(parsedArgs, scopedModels, sessionManager, modelRegistry),
745
+ const { options: sessionOptions } = await logger.time(
746
+ "buildSessionOptions",
747
+ buildSessionOptions,
748
+ parsedArgs,
749
+ scopedModels,
750
+ sessionManager,
751
+ modelRegistry,
703
752
  );
704
753
  sessionOptions.authStorage = authStorage;
705
754
  sessionOptions.modelRegistry = modelRegistry;
@@ -718,10 +767,12 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
718
767
  }
719
768
  }
720
769
 
721
- const { session, setToolUIContext, modelFallbackMessage, lspServers, mcpManager } = await logger.timeAsync(
770
+ const { session, setToolUIContext, modelFallbackMessage, lspServers, mcpManager, eventBus } = await logger.time(
722
771
  "createAgentSession",
723
- () => createAgentSession(sessionOptions),
772
+ createAgentSession,
773
+ sessionOptions,
724
774
  );
775
+ logger.time("main:afterCreateSession");
725
776
  if (parsedArgs.apiKey && !sessionOptions.model && session.model) {
726
777
  authStorage.setRuntimeApiKey(session.model.provider, parsedArgs.apiKey);
727
778
  }
@@ -772,12 +823,35 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
772
823
  process.exit(1);
773
824
  }
774
825
 
826
+ const extensionFlagValues = session.extensionRunner?.getFlagValues() ?? new Map<string, boolean | string>();
827
+ const createAcpSession = async (cwd: string) => {
828
+ const nextSettings = await session.settings.cloneForCwd(cwd);
829
+ const nextSessionManager = SessionManager.create(cwd, parsedArgs.sessionDir);
830
+ const { session: nextSession } = await createAgentSession({
831
+ ...sessionOptions,
832
+ cwd,
833
+ sessionManager: nextSessionManager,
834
+ settings: nextSettings,
835
+ authStorage,
836
+ modelRegistry,
837
+ searchDb: session.searchDb,
838
+ hasUI: false,
839
+ });
840
+ if (nextSession.extensionRunner) {
841
+ for (const [flagName, value] of extensionFlagValues) {
842
+ nextSession.extensionRunner.setFlagValue(flagName, value);
843
+ }
844
+ }
845
+ return nextSession;
846
+ };
847
+
775
848
  if (mode === "rpc") {
776
849
  await runRpcMode(session);
777
850
  } else if (mode === "acp") {
778
- await runAcpMode(session);
851
+ await runAcpMode(session, createAcpSession);
779
852
  } else if (isInteractive) {
780
853
  const versionCheckPromise = checkForNewVersion(VERSION).catch(() => undefined);
854
+ logger.time("main:getChangelogForDisplay");
781
855
  const changelogMarkdown = await getChangelogForDisplay(parsedArgs);
782
856
 
783
857
  const scopedModelsForDisplay = sessionOptions.scopedModels ?? scopedModels;
@@ -791,8 +865,11 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
791
865
  process.stdout.write(`${chalk.dim(`Model scope: ${modelList} ${chalk.gray("(Ctrl+P to cycle)")}`)}\n`);
792
866
  }
793
867
 
794
- if ($env.PI_TIMING === "1") {
868
+ if ($env.PI_TIMING) {
795
869
  logger.printTimings();
870
+ if ($env.PI_TIMING === "x") {
871
+ process.exit(0);
872
+ }
796
873
  }
797
874
 
798
875
  logger.endTiming();
@@ -806,6 +883,7 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
806
883
  setToolUIContext,
807
884
  lspServers,
808
885
  mcpManager,
886
+ eventBus,
809
887
  initialMessage,
810
888
  initialImages,
811
889
  );
@@ -4,10 +4,9 @@ import * as fs from "node:fs/promises";
4
4
  import * as path from "node:path";
5
5
  import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
6
6
  import { completeSimple, Effort, type Model } from "@oh-my-pi/pi-ai";
7
- import { getAgentDbPath, getMemoriesDir, logger, parseJsonlLenient } from "@oh-my-pi/pi-utils";
7
+ import { getAgentDbPath, getMemoriesDir, logger, parseJsonlLenient, prompt } from "@oh-my-pi/pi-utils";
8
8
  import type { ModelRegistry } from "../config/model-registry";
9
9
  import { parseModelString } from "../config/model-resolver";
10
- import { renderPromptTemplate } from "../config/prompt-templates";
11
10
  import type { Settings } from "../config/settings";
12
11
  import consolidationTemplate from "../prompts/memories/consolidation.md" with { type: "text" };
13
12
  import readPathTemplate from "../prompts/memories/read-path.md" with { type: "text" };
@@ -166,7 +165,7 @@ export async function buildMemoryToolDeveloperInstructions(
166
165
  const truncated = truncateByApproxTokens(summary, cfg.summaryInjectionTokenLimit);
167
166
  if (!truncated.trim()) return undefined;
168
167
 
169
- return renderPromptTemplate(readPathTemplate, {
168
+ return prompt.render(readPathTemplate, {
170
169
  memory_summary: truncated,
171
170
  });
172
171
  }
@@ -585,7 +584,7 @@ async function runStage1Job(options: {
585
584
  const serializedItems = JSON.stringify(persisted);
586
585
  const budgetTokens = Math.floor(modelMaxTokens * config.rolloutPayloadPercent);
587
586
  const truncatedItems = truncateByApproxTokens(serializedItems, budgetTokens);
588
- const inputPrompt = renderPromptTemplate(stageOneInputTemplate, {
587
+ const inputPrompt = prompt.render(stageOneInputTemplate, {
589
588
  thread_id: claim.threadId,
590
589
  response_items_json: truncatedItems,
591
590
  });
@@ -716,7 +715,7 @@ async function runConsolidationModel(options: { memoryRoot: string; model: Model
716
715
  const { memoryRoot, model, apiKey } = options;
717
716
  const rawMemories = await Bun.file(path.join(memoryRoot, "raw_memories.md")).text();
718
717
  const rolloutSummaries = await readRolloutSummaries(memoryRoot);
719
- const input = renderPromptTemplate(consolidationTemplate, {
718
+ const input = prompt.render(consolidationTemplate, {
720
719
  raw_memories: truncateByApproxTokens(rawMemories, 20_000),
721
720
  rollout_summaries: truncateByApproxTokens(rolloutSummaries, 12_000),
722
721
  });