@oh-my-pi/pi-coding-agent 13.19.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 (202) hide show
  1. package/CHANGELOG.md +266 -1
  2. package/package.json +86 -20
  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 +91 -0
  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 +83 -125
  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 -5
  28. package/src/commit/agentic/index.ts +3 -4
  29. package/src/commit/agentic/tools/analyze-file.ts +3 -3
  30. package/src/commit/agentic/validation.ts +1 -1
  31. package/src/commit/analysis/conventional.ts +4 -4
  32. package/src/commit/analysis/summary.ts +3 -3
  33. package/src/commit/changelog/generate.ts +4 -4
  34. package/src/commit/map-reduce/map-phase.ts +4 -4
  35. package/src/commit/map-reduce/reduce-phase.ts +4 -4
  36. package/src/commit/pipeline.ts +3 -4
  37. package/src/config/prompt-templates.ts +44 -226
  38. package/src/config/resolve-config-value.ts +4 -2
  39. package/src/config/settings-schema.ts +54 -2
  40. package/src/config/settings.ts +25 -26
  41. package/src/dap/client.ts +674 -0
  42. package/src/dap/config.ts +150 -0
  43. package/src/dap/defaults.json +211 -0
  44. package/src/dap/index.ts +4 -0
  45. package/src/dap/session.ts +1255 -0
  46. package/src/dap/types.ts +600 -0
  47. package/src/debug/log-viewer.ts +3 -2
  48. package/src/discovery/builtin.ts +1 -2
  49. package/src/discovery/codex.ts +2 -2
  50. package/src/discovery/github.ts +2 -1
  51. package/src/discovery/helpers.ts +2 -2
  52. package/src/discovery/opencode.ts +2 -2
  53. package/src/edit/diff.ts +818 -0
  54. package/src/edit/index.ts +309 -0
  55. package/src/edit/line-hash.ts +67 -0
  56. package/src/edit/modes/chunk.ts +454 -0
  57. package/src/{patch → edit/modes}/hashline.ts +741 -361
  58. package/src/{patch/applicator.ts → edit/modes/patch.ts} +420 -117
  59. package/src/{patch/fuzzy.ts → edit/modes/replace.ts} +519 -197
  60. package/src/{patch → edit}/normalize.ts +97 -76
  61. package/src/{patch/shared.ts → edit/renderer.ts} +181 -108
  62. package/src/exec/bash-executor.ts +4 -2
  63. package/src/exec/idle-timeout-watchdog.ts +126 -0
  64. package/src/exec/non-interactive-env.ts +5 -0
  65. package/src/extensibility/custom-commands/bundled/ci-green/index.ts +2 -2
  66. package/src/extensibility/custom-commands/bundled/review/index.ts +2 -2
  67. package/src/extensibility/custom-commands/loader.ts +1 -2
  68. package/src/extensibility/custom-tools/loader.ts +34 -11
  69. package/src/extensibility/extensions/loader.ts +9 -4
  70. package/src/extensibility/extensions/runner.ts +24 -1
  71. package/src/extensibility/extensions/types.ts +1 -1
  72. package/src/extensibility/hooks/loader.ts +5 -6
  73. package/src/extensibility/hooks/types.ts +1 -1
  74. package/src/extensibility/plugins/doctor.ts +2 -1
  75. package/src/extensibility/slash-commands.ts +3 -7
  76. package/src/index.ts +2 -1
  77. package/src/internal-urls/docs-index.generated.ts +11 -11
  78. package/src/ipy/executor.ts +58 -17
  79. package/src/ipy/gateway-coordinator.ts +6 -4
  80. package/src/ipy/kernel.ts +45 -22
  81. package/src/ipy/runtime.ts +2 -2
  82. package/src/lsp/client.ts +7 -4
  83. package/src/lsp/clients/lsp-linter-client.ts +4 -4
  84. package/src/lsp/config.ts +2 -2
  85. package/src/lsp/defaults.json +688 -154
  86. package/src/lsp/index.ts +234 -45
  87. package/src/lsp/lspmux.ts +2 -2
  88. package/src/lsp/startup-events.ts +13 -0
  89. package/src/lsp/types.ts +12 -1
  90. package/src/lsp/utils.ts +8 -1
  91. package/src/main.ts +102 -46
  92. package/src/memories/index.ts +4 -5
  93. package/src/modes/acp/acp-agent.ts +563 -163
  94. package/src/modes/acp/acp-event-mapper.ts +9 -1
  95. package/src/modes/acp/acp-mode.ts +4 -2
  96. package/src/modes/components/agent-dashboard.ts +3 -4
  97. package/src/modes/components/diff.ts +6 -7
  98. package/src/modes/components/read-tool-group.ts +6 -12
  99. package/src/modes/components/settings-defs.ts +5 -0
  100. package/src/modes/components/tool-execution.ts +1 -1
  101. package/src/modes/components/welcome.ts +1 -1
  102. package/src/modes/controllers/btw-controller.ts +2 -2
  103. package/src/modes/controllers/command-controller.ts +3 -2
  104. package/src/modes/controllers/input-controller.ts +12 -8
  105. package/src/modes/index.ts +20 -2
  106. package/src/modes/interactive-mode.ts +94 -37
  107. package/src/modes/rpc/host-tools.ts +186 -0
  108. package/src/modes/rpc/rpc-client.ts +178 -13
  109. package/src/modes/rpc/rpc-mode.ts +73 -3
  110. package/src/modes/rpc/rpc-types.ts +53 -1
  111. package/src/modes/theme/theme.ts +80 -8
  112. package/src/modes/types.ts +2 -2
  113. package/src/prompts/system/system-prompt.md +2 -1
  114. package/src/prompts/tools/chunk-edit.md +219 -0
  115. package/src/prompts/tools/debug.md +43 -0
  116. package/src/prompts/tools/grep.md +3 -0
  117. package/src/prompts/tools/lsp.md +5 -5
  118. package/src/prompts/tools/read-chunk.md +17 -0
  119. package/src/prompts/tools/read.md +19 -5
  120. package/src/sdk.ts +190 -154
  121. package/src/secrets/obfuscator.ts +1 -1
  122. package/src/session/agent-session.ts +306 -256
  123. package/src/session/agent-storage.ts +12 -12
  124. package/src/session/compaction/branch-summarization.ts +3 -3
  125. package/src/session/compaction/compaction.ts +5 -6
  126. package/src/session/compaction/utils.ts +3 -3
  127. package/src/session/history-storage.ts +62 -19
  128. package/src/session/messages.ts +3 -3
  129. package/src/session/session-dump-format.ts +203 -0
  130. package/src/session/session-storage.ts +4 -2
  131. package/src/session/streaming-output.ts +1 -1
  132. package/src/session/tool-choice-queue.ts +213 -0
  133. package/src/slash-commands/builtin-registry.ts +56 -8
  134. package/src/ssh/connection-manager.ts +2 -2
  135. package/src/ssh/sshfs-mount.ts +5 -5
  136. package/src/stt/downloader.ts +4 -4
  137. package/src/stt/recorder.ts +4 -4
  138. package/src/stt/transcriber.ts +2 -2
  139. package/src/system-prompt.ts +21 -13
  140. package/src/task/agents.ts +5 -6
  141. package/src/task/commands.ts +2 -5
  142. package/src/task/executor.ts +4 -4
  143. package/src/task/index.ts +3 -4
  144. package/src/task/template.ts +2 -2
  145. package/src/task/worktree.ts +4 -4
  146. package/src/tools/ask.ts +2 -3
  147. package/src/tools/ast-edit.ts +7 -7
  148. package/src/tools/ast-grep.ts +7 -7
  149. package/src/tools/auto-generated-guard.ts +36 -41
  150. package/src/tools/await-tool.ts +2 -2
  151. package/src/tools/bash.ts +5 -23
  152. package/src/tools/browser.ts +4 -5
  153. package/src/tools/calculator.ts +2 -3
  154. package/src/tools/cancel-job.ts +2 -2
  155. package/src/tools/checkpoint.ts +3 -3
  156. package/src/tools/debug.ts +1007 -0
  157. package/src/tools/exit-plan-mode.ts +2 -3
  158. package/src/tools/fetch.ts +67 -3
  159. package/src/tools/find.ts +4 -5
  160. package/src/tools/fs-cache-invalidation.ts +5 -0
  161. package/src/tools/gemini-image.ts +13 -5
  162. package/src/tools/gh.ts +10 -11
  163. package/src/tools/grep.ts +57 -9
  164. package/src/tools/index.ts +44 -22
  165. package/src/tools/inspect-image.ts +4 -4
  166. package/src/tools/output-meta.ts +1 -1
  167. package/src/tools/python.ts +19 -6
  168. package/src/tools/read.ts +198 -67
  169. package/src/tools/render-mermaid.ts +2 -3
  170. package/src/tools/render-utils.ts +20 -6
  171. package/src/tools/renderers.ts +3 -1
  172. package/src/tools/report-tool-issue.ts +80 -0
  173. package/src/tools/resolve.ts +70 -39
  174. package/src/tools/search-tool-bm25.ts +2 -2
  175. package/src/tools/ssh.ts +2 -2
  176. package/src/tools/todo-write.ts +2 -2
  177. package/src/tools/tool-timeouts.ts +1 -0
  178. package/src/tools/write.ts +5 -6
  179. package/src/tui/tree-list.ts +3 -1
  180. package/src/utils/clipboard.ts +80 -0
  181. package/src/utils/commit-message-generator.ts +2 -3
  182. package/src/utils/edit-mode.ts +49 -0
  183. package/src/utils/file-display-mode.ts +6 -5
  184. package/src/utils/file-mentions.ts +8 -7
  185. package/src/utils/git.ts +4 -4
  186. package/src/utils/image-loading.ts +98 -0
  187. package/src/utils/title-generator.ts +2 -3
  188. package/src/utils/tools-manager.ts +6 -6
  189. package/src/web/scrapers/choosealicense.ts +1 -1
  190. package/src/web/search/index.ts +3 -3
  191. package/src/autoresearch/command-initialize.md +0 -34
  192. package/src/patch/diff.ts +0 -433
  193. package/src/patch/index.ts +0 -888
  194. package/src/patch/parser.ts +0 -532
  195. package/src/patch/types.ts +0 -292
  196. package/src/prompts/agents/oracle.md +0 -77
  197. package/src/tools/pending-action.ts +0 -49
  198. package/src/utils/child-process.ts +0 -88
  199. package/src/utils/frontmatter.ts +0 -117
  200. package/src/utils/image-input.ts +0 -274
  201. package/src/utils/mime.ts +0 -53
  202. 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,6 +47,7 @@ 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";
51
52
  import type { EventBus } from "./utils/event-bus";
52
53
 
@@ -71,6 +72,29 @@ async function checkForNewVersion(currentVersion: string): Promise<string | unde
71
72
  }
72
73
  }
73
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
+
74
98
  async function readPipedInput(): Promise<string | undefined> {
75
99
  if (process.stdin.isTTY !== false) return undefined;
76
100
  try {
@@ -118,7 +142,7 @@ async function runInteractiveMode(
118
142
  versionCheckPromise: Promise<string | undefined>,
119
143
  initialMessages: string[],
120
144
  setExtensionUIContext: (uiContext: ExtensionUIContext, hasUI: boolean) => void,
121
- lspServers: Array<{ name: string; status: "ready" | "error"; fileTypes: string[]; error?: string }> | undefined,
145
+ lspServers: LspStartupServerInfo[] | undefined,
122
146
  mcpManager: MCPManager | undefined,
123
147
  eventBus?: EventBus,
124
148
  initialMessage?: string,
@@ -542,19 +566,16 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
542
566
 
543
567
  // Initialize theme early with defaults (CLI commands need symbols)
544
568
  // Will be re-initialized with user preferences later
545
- await logger.timeAsync("initTheme:initial", () => initTheme());
569
+ await logger.time("initTheme:initial", initTheme);
546
570
 
547
571
  const parsedArgs = parsed;
548
- await logger.timeAsync("maybeAutoChdir", () => maybeAutoChdir(parsedArgs));
572
+ await logger.time("maybeAutoChdir", maybeAutoChdir, parsedArgs);
549
573
 
550
574
  const notifs: (InteractiveModeNotify | null)[] = [];
551
575
 
552
576
  // Create AuthStorage and ModelRegistry upfront
553
- const { authStorage, modelRegistry } = await logger.timeAsync("discoverModels", async () => {
554
- const authStorage = await discoverAuthStorage();
555
- const modelRegistry = new ModelRegistry(authStorage);
556
- return { authStorage, modelRegistry };
557
- });
577
+ const authStorage = await logger.time("discoverModels", discoverAuthStorage);
578
+ const modelRegistry = new ModelRegistry(authStorage);
558
579
 
559
580
  if (parsedArgs.version) {
560
581
  process.stdout.write(`${VERSION}\n`);
@@ -562,7 +583,7 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
562
583
  }
563
584
 
564
585
  if (parsedArgs.listModels !== undefined) {
565
- await logger.timeAsync("settings:init:list-models", () => Settings.init({ cwd: getProjectDir() }));
586
+ await logger.time("settings:init:list-models", Settings.init, { cwd: getProjectDir() });
566
587
  await modelRegistry.refresh("online");
567
588
  const searchPattern = typeof parsedArgs.listModels === "string" ? parsedArgs.listModels : undefined;
568
589
  await listModels(modelRegistry, searchPattern);
@@ -589,24 +610,25 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
589
610
  }
590
611
 
591
612
  const cwd = getProjectDir();
592
- 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
+ }
593
617
  if (parsedArgs.noPty) {
594
618
  Bun.env.PI_NO_PTY = "1";
595
619
  }
596
- 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 () => {
597
624
  const pipedInput = await readPipedInput();
598
625
  if (parsedArgs.fileArgs.length === 0) {
599
- return { pipedInput };
626
+ return { pipedInput, fileText: undefined, fileImages: undefined };
600
627
  }
601
-
602
- const { text, images } = await processFileArguments(parsedArgs.fileArgs, {
628
+ const processed = await processFileArguments(parsedArgs.fileArgs, {
603
629
  autoResizeImages: settings.get("images.autoResize"),
604
630
  });
605
- return {
606
- pipedInput,
607
- fileText: text,
608
- fileImages: images,
609
- };
631
+ return { pipedInput, fileText: processed.text, fileImages: processed.images };
610
632
  });
611
633
  const { initialMessage, initialImages } = buildInitialMessage({
612
634
  parsed: parsedArgs,
@@ -619,7 +641,8 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
619
641
  const mode = parsedArgs.mode || "text";
620
642
 
621
643
  // Initialize discovery system with settings for provider persistence
622
- logger.time("initializeWithSettings", () => initializeWithSettings(settings));
644
+ logger.time("initializeWithSettings");
645
+ initializeWithSettings(settings);
623
646
  modelRegistry.refreshInBackground();
624
647
 
625
648
  // Apply model role overrides from CLI args or env vars (ephemeral, not persisted)
@@ -634,14 +657,14 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
634
657
  });
635
658
  }
636
659
 
637
- await logger.timeAsync("initTheme:final", () =>
638
- initTheme(
639
- isInteractive,
640
- settings.get("symbolPreset"),
641
- settings.get("colorBlindMode"),
642
- settings.get("theme.dark"),
643
- settings.get("theme.light"),
644
- ),
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"),
645
668
  );
646
669
 
647
670
  let scopedModels: ScopedModel[] = [];
@@ -650,24 +673,26 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
650
673
  usageOrder: settings.getStorage()?.getModelUsageOrder(),
651
674
  };
652
675
  if (modelPatterns && modelPatterns.length > 0) {
653
- scopedModels = await logger.timeAsync("resolveModelScope", () =>
654
- resolveModelScope(modelPatterns, modelRegistry, modelMatchPreferences),
676
+ scopedModels = await logger.time(
677
+ "resolveModelScope",
678
+ resolveModelScope,
679
+ modelPatterns,
680
+ modelRegistry,
681
+ modelMatchPreferences,
655
682
  );
656
683
  }
657
684
 
658
685
  // Create session manager based on CLI flags
659
- let sessionManager = await logger.timeAsync("createSessionManager", () => createSessionManager(parsedArgs, cwd));
686
+ let sessionManager = await logger.time("createSessionManager", createSessionManager, parsedArgs, cwd);
660
687
 
661
688
  // Handle --resume (no value): show session picker
662
689
  if (parsedArgs.resume === true && !parsedArgs.fork) {
663
- const sessions = await logger.timeAsync("SessionManager.list", () =>
664
- SessionManager.list(cwd, parsedArgs.sessionDir),
665
- );
690
+ const sessions = await logger.time("SessionManager.list", SessionManager.list, cwd, parsedArgs.sessionDir);
666
691
  if (sessions.length === 0) {
667
692
  process.stdout.write(`${chalk.dim("No sessions found")}\n`);
668
693
  return;
669
694
  }
670
- const selectedPath = await logger.timeAsync("selectSession", () => selectSession(sessions));
695
+ const selectedPath = await logger.time("selectSession", selectSession, sessions);
671
696
  if (!selectedPath) {
672
697
  process.stdout.write(`${chalk.dim("No session selected")}\n`);
673
698
  return;
@@ -678,11 +703,9 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
678
703
  // Wire --plugin-dir and preload plugin roots for sync consumers (LSP config)
679
704
  const home = os.homedir();
680
705
  if (parsedArgs.pluginDirs && parsedArgs.pluginDirs.length > 0) {
681
- await logger.timeAsync("injectPluginDirRoots", () =>
682
- injectPluginDirRoots(home, parsedArgs.pluginDirs!, getProjectDir()),
683
- );
706
+ await logger.time("injectPluginDirRoots", injectPluginDirRoots, home, parsedArgs.pluginDirs!, getProjectDir());
684
707
  } else {
685
- await logger.timeAsync("preloadPluginRoots", () => preloadPluginRoots(home, getProjectDir()));
708
+ await logger.time("preloadPluginRoots", preloadPluginRoots, home, getProjectDir());
686
709
  }
687
710
 
688
711
  // Background marketplace auto-update — never blocks startup.
@@ -719,8 +742,13 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
719
742
  })();
720
743
  }
721
744
 
722
- const { options: sessionOptions } = await logger.timeAsync("buildSessionOptions", () =>
723
- buildSessionOptions(parsedArgs, scopedModels, sessionManager, modelRegistry),
745
+ const { options: sessionOptions } = await logger.time(
746
+ "buildSessionOptions",
747
+ buildSessionOptions,
748
+ parsedArgs,
749
+ scopedModels,
750
+ sessionManager,
751
+ modelRegistry,
724
752
  );
725
753
  sessionOptions.authStorage = authStorage;
726
754
  sessionOptions.modelRegistry = modelRegistry;
@@ -739,10 +767,12 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
739
767
  }
740
768
  }
741
769
 
742
- const { session, setToolUIContext, modelFallbackMessage, lspServers, mcpManager, eventBus } = await logger.timeAsync(
770
+ const { session, setToolUIContext, modelFallbackMessage, lspServers, mcpManager, eventBus } = await logger.time(
743
771
  "createAgentSession",
744
- () => createAgentSession(sessionOptions),
772
+ createAgentSession,
773
+ sessionOptions,
745
774
  );
775
+ logger.time("main:afterCreateSession");
746
776
  if (parsedArgs.apiKey && !sessionOptions.model && session.model) {
747
777
  authStorage.setRuntimeApiKey(session.model.provider, parsedArgs.apiKey);
748
778
  }
@@ -793,12 +823,35 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
793
823
  process.exit(1);
794
824
  }
795
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
+
796
848
  if (mode === "rpc") {
797
849
  await runRpcMode(session);
798
850
  } else if (mode === "acp") {
799
- await runAcpMode(session);
851
+ await runAcpMode(session, createAcpSession);
800
852
  } else if (isInteractive) {
801
853
  const versionCheckPromise = checkForNewVersion(VERSION).catch(() => undefined);
854
+ logger.time("main:getChangelogForDisplay");
802
855
  const changelogMarkdown = await getChangelogForDisplay(parsedArgs);
803
856
 
804
857
  const scopedModelsForDisplay = sessionOptions.scopedModels ?? scopedModels;
@@ -812,8 +865,11 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
812
865
  process.stdout.write(`${chalk.dim(`Model scope: ${modelList} ${chalk.gray("(Ctrl+P to cycle)")}`)}\n`);
813
866
  }
814
867
 
815
- if ($env.PI_TIMING === "1") {
868
+ if ($env.PI_TIMING) {
816
869
  logger.printTimings();
870
+ if ($env.PI_TIMING === "x") {
871
+ process.exit(0);
872
+ }
817
873
  }
818
874
 
819
875
  logger.endTiming();
@@ -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
  });