@oh-my-pi/pi-coding-agent 17.2.9 → 17.2.11

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 (281) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/dist/{CHANGELOG-8yy8h3bc.md → CHANGELOG-d9xenpn9.md} +83 -0
  3. package/dist/cli.js +10208 -12596
  4. package/dist/types/capability/mcp.d.ts +11 -0
  5. package/dist/types/capability/skill.d.ts +6 -0
  6. package/dist/types/cli/args.d.ts +1 -0
  7. package/dist/types/cli/command-help.d.ts +3 -0
  8. package/dist/types/commands/share.d.ts +25 -0
  9. package/dist/types/commit/agentic/index.d.ts +3 -1
  10. package/dist/types/commit/execute.d.ts +32 -0
  11. package/dist/types/commit/index.d.ts +2 -1
  12. package/dist/types/commit/pipeline.d.ts +7 -1
  13. package/dist/types/config/model-registry.d.ts +4 -0
  14. package/dist/types/config/model-resolver.d.ts +13 -0
  15. package/dist/types/config/settings-schema.d.ts +1 -34
  16. package/dist/types/discovery/agent-plugin-format.d.ts +143 -0
  17. package/dist/types/discovery/agent-plugins.d.ts +1 -0
  18. package/dist/types/discovery/contained-path.d.ts +34 -0
  19. package/dist/types/discovery/index.d.ts +1 -0
  20. package/dist/types/extensibility/custom-commands/types.d.ts +5 -5
  21. package/dist/types/extensibility/custom-tools/types.d.ts +3 -3
  22. package/dist/types/extensibility/extensions/loader.d.ts +9 -2
  23. package/dist/types/extensibility/extensions/types.d.ts +45 -8
  24. package/dist/types/extensibility/hooks/types.d.ts +5 -5
  25. package/dist/types/extensibility/shared-events.d.ts +3 -2
  26. package/dist/types/extensibility/skills.d.ts +5 -0
  27. package/dist/types/index.d.ts +2 -2
  28. package/dist/types/main.d.ts +1 -1
  29. package/dist/types/mcp/transports/header-policy.d.ts +45 -0
  30. package/dist/types/mcp/types.d.ts +15 -0
  31. package/dist/types/modes/acp/acp-agent.d.ts +1 -1
  32. package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -1
  33. package/dist/types/modes/acp/acp-event-mapper.d.ts +1 -1
  34. package/dist/types/modes/acp/acp-mode.d.ts +1 -1
  35. package/dist/types/modes/components/agent-hub-projection.d.ts +38 -0
  36. package/dist/types/modes/components/agent-hub-renderer.d.ts +41 -0
  37. package/dist/types/modes/components/agent-hub.d.ts +14 -5
  38. package/dist/types/modes/components/agent-transcript-viewer.d.ts +2 -0
  39. package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
  40. package/dist/types/modes/components/custom-editor.d.ts +1 -2
  41. package/dist/types/modes/components/model-browser.d.ts +9 -1
  42. package/dist/types/modes/components/status-line/types.d.ts +2 -0
  43. package/dist/types/modes/components/tool-execution.d.ts +2 -0
  44. package/dist/types/modes/interactive-mode.d.ts +8 -1
  45. package/dist/types/modes/utils/transcript-render-helpers.d.ts +3 -2
  46. package/dist/types/registry/agent-registry.d.ts +47 -0
  47. package/dist/types/registry/persisted-agents.d.ts +3 -1
  48. package/dist/types/secrets/index.d.ts +23 -1
  49. package/dist/types/session/agent-session-events.d.ts +2 -2
  50. package/dist/types/session/agent-session.d.ts +4 -2
  51. package/dist/types/session/messages.d.ts +1 -0
  52. package/dist/types/session/prewalk.d.ts +3 -1
  53. package/dist/types/session/session-entries.d.ts +10 -0
  54. package/dist/types/session/session-loader.d.ts +11 -1
  55. package/dist/types/session/session-manager.d.ts +9 -1
  56. package/dist/types/session/session-tools.d.ts +18 -1
  57. package/dist/types/session/turn-recovery.d.ts +7 -2
  58. package/dist/types/slash-commands/acp-builtins.d.ts +1 -1
  59. package/dist/types/slash-commands/available-commands.d.ts +1 -1
  60. package/dist/types/task/executor.d.ts +6 -0
  61. package/dist/types/task/index.d.ts +4 -3
  62. package/dist/types/task/read-only-policy.d.ts +3 -0
  63. package/dist/types/task/structured-subagent.d.ts +2 -0
  64. package/dist/types/task/types.d.ts +4 -0
  65. package/dist/types/tools/bash.d.ts +3 -3
  66. package/dist/types/tools/browser/launch.d.ts +2 -1
  67. package/dist/types/tools/browser/tab-worker.d.ts +2 -2
  68. package/dist/types/tools/path-utils.d.ts +1 -0
  69. package/dist/types/tools/read.d.ts +1 -5
  70. package/dist/types/tools/run-scope.d.ts +27 -1
  71. package/dist/types/tools/shell-tokenize.d.ts +16 -0
  72. package/dist/types/tools/terminal-output.d.ts +1 -1
  73. package/dist/types/utils/turndown.d.ts +1 -1
  74. package/dist/types/vibe/runtime.d.ts +2 -3
  75. package/dist/types/web/scrapers/readthedocs.d.ts +0 -3
  76. package/dist/types/web/search/providers/browser-headers.d.ts +2 -3
  77. package/examples/custom-tools/README.md +1 -1
  78. package/examples/extensions/README.md +1 -1
  79. package/examples/extensions/api-demo.ts +5 -6
  80. package/examples/extensions/chalk-logger.ts +1 -1
  81. package/examples/extensions/hello.ts +2 -2
  82. package/examples/extensions/reload-runtime.ts +1 -1
  83. package/examples/extensions/tools.ts +2 -2
  84. package/examples/extensions/with-deps/index.ts +1 -1
  85. package/examples/sdk/06-extensions.ts +1 -1
  86. package/package.json +15 -31
  87. package/scripts/bundle-dist.ts +1 -11
  88. package/scripts/legacy-pi-virtual-module.ts +4 -1
  89. package/src/advisor/runtime.ts +54 -0
  90. package/src/capability/mcp.ts +11 -0
  91. package/src/capability/skill.ts +6 -0
  92. package/src/cli/agents-cli.ts +1 -1
  93. package/src/cli/args.ts +24 -2
  94. package/src/cli/auth-broker-cli.ts +1 -1
  95. package/src/cli/auth-gateway-cli.ts +1 -1
  96. package/src/cli/bench-cli.ts +1 -1
  97. package/src/cli/claude-trace-cli.ts +1 -1
  98. package/src/cli/command-help.ts +4 -0
  99. package/src/cli/config-cli.ts +1 -1
  100. package/src/cli/dry-balance-cli.ts +1 -1
  101. package/src/cli/file-processor.ts +1 -1
  102. package/src/cli/flag-tables.ts +4 -0
  103. package/src/cli/gallery-cli.ts +1 -1
  104. package/src/cli/grep-cli.ts +1 -1
  105. package/src/cli/grievances-cli.ts +1 -1
  106. package/src/cli/help-extra.ts +1 -1
  107. package/src/cli/models-cli.ts +1 -1
  108. package/src/cli/plugin-cli.ts +1 -1
  109. package/src/cli/read-cli.ts +1 -1
  110. package/src/cli/setup-cli.ts +1 -1
  111. package/src/cli/shell-cli.ts +1 -1
  112. package/src/cli/ssh-cli.ts +1 -1
  113. package/src/cli/stats-cli.ts +1 -1
  114. package/src/cli/tiny-models-cli.ts +1 -1
  115. package/src/cli/ttsr-cli.ts +1 -1
  116. package/src/cli/update-cli.ts +1 -1
  117. package/src/cli/usage-cli.ts +1 -1
  118. package/src/cli/web-search-cli.ts +1 -1
  119. package/src/cli/worktree-cli.ts +1 -1
  120. package/src/cli-commands.ts +5 -0
  121. package/src/commands/commit.ts +16 -3
  122. package/src/commands/say.ts +1 -1
  123. package/src/commands/share.ts +71 -0
  124. package/src/commands/token.ts +1 -1
  125. package/src/commit/agentic/agent.ts +1 -1
  126. package/src/commit/agentic/index.ts +39 -21
  127. package/src/commit/cli.ts +1 -1
  128. package/src/commit/execute.ts +56 -0
  129. package/src/commit/index.ts +2 -1
  130. package/src/commit/pipeline.ts +20 -7
  131. package/src/config/model-discovery.ts +1 -1
  132. package/src/config/model-registry.ts +21 -1
  133. package/src/config/model-resolver.ts +54 -7
  134. package/src/config/settings-schema.ts +2 -33
  135. package/src/config/settings.ts +46 -0
  136. package/src/discovery/agent-plugin-format.ts +551 -0
  137. package/src/discovery/agent-plugins.ts +341 -0
  138. package/src/discovery/agents.ts +4 -2
  139. package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +5 -4
  140. package/src/discovery/builtin-rules/ts-no-tiny-functions.md +1 -1
  141. package/src/discovery/claude-plugins.ts +21 -5
  142. package/src/discovery/contained-path.ts +78 -0
  143. package/src/discovery/helpers.ts +23 -9
  144. package/src/discovery/index.ts +1 -0
  145. package/src/discovery/omp-plugins.ts +20 -7
  146. package/src/eval/py/runner.py +38 -1
  147. package/src/exec/non-interactive-env.ts +1 -0
  148. package/src/extensibility/custom-commands/loader.ts +4 -4
  149. package/src/extensibility/custom-commands/types.ts +5 -5
  150. package/src/extensibility/custom-tools/loader.ts +4 -4
  151. package/src/extensibility/custom-tools/types.ts +3 -3
  152. package/src/extensibility/extensions/loader.ts +85 -20
  153. package/src/extensibility/extensions/runner.ts +6 -0
  154. package/src/extensibility/extensions/types.ts +51 -8
  155. package/src/extensibility/hooks/loader.ts +4 -5
  156. package/src/extensibility/hooks/types.ts +5 -5
  157. package/src/extensibility/plugins/legacy-pi-compat.ts +53 -0
  158. package/src/extensibility/plugins/marketplace/manager.ts +2 -1
  159. package/src/extensibility/shared-events.ts +3 -2
  160. package/src/extensibility/skills.ts +9 -0
  161. package/src/index.ts +2 -2
  162. package/src/internal-urls/memory-protocol.ts +5 -1
  163. package/src/internal-urls/skill-protocol.ts +14 -0
  164. package/src/internal-urls/vault-protocol.ts +2 -2
  165. package/src/launch/client.ts +11 -1
  166. package/src/launch/protocol.ts +7 -2
  167. package/src/launch/terminal-output.ts +1 -1
  168. package/src/main.ts +88 -23
  169. package/src/markit/converters/docx.ts +1 -1
  170. package/src/markit/converters/epub.ts +1 -1
  171. package/src/markit/converters/pptx.ts +1 -1
  172. package/src/markit/converters/xlsx.ts +1 -1
  173. package/src/mcp/config.ts +3 -0
  174. package/src/mcp/manager.ts +12 -9
  175. package/src/mcp/transports/header-policy.ts +95 -0
  176. package/src/mcp/transports/http.ts +35 -52
  177. package/src/mcp/transports/sse.ts +20 -27
  178. package/src/mcp/types.ts +15 -0
  179. package/src/modes/acp/acp-agent.ts +20 -9
  180. package/src/modes/acp/acp-client-bridge.ts +1 -1
  181. package/src/modes/acp/acp-event-mapper.ts +1 -1
  182. package/src/modes/acp/acp-mode.ts +1 -1
  183. package/src/modes/components/agent-dashboard.ts +2 -0
  184. package/src/modes/components/agent-hub-projection.ts +248 -0
  185. package/src/modes/components/agent-hub-renderer.ts +194 -0
  186. package/src/modes/components/agent-hub.ts +674 -207
  187. package/src/modes/components/agent-transcript-viewer.ts +3 -0
  188. package/src/modes/components/assistant-message.ts +1 -1
  189. package/src/modes/components/chat-transcript-builder.ts +3 -0
  190. package/src/modes/components/custom-editor.ts +0 -9
  191. package/src/modes/components/extensions/inspector-panel.ts +11 -6
  192. package/src/modes/components/footer.ts +1 -3
  193. package/src/modes/components/model-browser.ts +11 -3
  194. package/src/modes/components/model-hub.ts +4 -1
  195. package/src/modes/components/status-line/component.ts +1 -0
  196. package/src/modes/components/status-line/segments.ts +10 -7
  197. package/src/modes/components/status-line/types.ts +2 -0
  198. package/src/modes/components/tool-execution.ts +13 -16
  199. package/src/modes/components/tree-selector.ts +62 -3
  200. package/src/modes/controllers/command-controller.ts +4 -1
  201. package/src/modes/controllers/event-controller.ts +44 -20
  202. package/src/modes/controllers/extension-ui-controller.ts +2 -2
  203. package/src/modes/controllers/input-controller.ts +20 -2
  204. package/src/modes/controllers/mcp-command-controller.ts +81 -20
  205. package/src/modes/controllers/selector-controller.ts +41 -49
  206. package/src/modes/controllers/streaming-reveal.ts +1 -1
  207. package/src/modes/interactive-mode.ts +13 -2
  208. package/src/modes/rpc/rpc-mode.ts +2 -2
  209. package/src/modes/runtime-init.ts +1 -1
  210. package/src/modes/theme/theme.ts +2 -2
  211. package/src/modes/utils/transcript-render-helpers.ts +4 -2
  212. package/src/modes/utils/ui-helpers.ts +1 -0
  213. package/src/prompts/tools/computer.md +1 -1
  214. package/src/registry/agent-lifecycle.ts +14 -7
  215. package/src/registry/agent-registry.ts +65 -2
  216. package/src/registry/persisted-agents.ts +341 -16
  217. package/src/sdk.ts +6 -49
  218. package/src/secrets/index.ts +52 -1
  219. package/src/session/agent-session-events.ts +2 -2
  220. package/src/session/agent-session.ts +54 -16
  221. package/src/session/messages.ts +1 -0
  222. package/src/session/prewalk.ts +57 -17
  223. package/src/session/session-context.ts +3 -5
  224. package/src/session/session-entries.ts +10 -0
  225. package/src/session/session-handoff.ts +5 -1
  226. package/src/session/session-listing.ts +1 -1
  227. package/src/session/session-loader.ts +74 -8
  228. package/src/session/session-manager.ts +22 -2
  229. package/src/session/session-paths.ts +57 -9
  230. package/src/session/session-tools.ts +65 -4
  231. package/src/session/todo-tracker.ts +11 -1
  232. package/src/session/turn-recovery.ts +96 -74
  233. package/src/slash-commands/acp-builtins.ts +1 -1
  234. package/src/slash-commands/available-commands.ts +1 -1
  235. package/src/slash-commands/builtin-registry.ts +12 -8
  236. package/src/task/executor.ts +70 -20
  237. package/src/task/index.ts +30 -34
  238. package/src/task/isolation-runner.ts +24 -11
  239. package/src/task/persisted-revive.ts +12 -5
  240. package/src/task/read-only-policy.ts +27 -0
  241. package/src/task/structured-subagent.ts +14 -4
  242. package/src/task/types.ts +4 -0
  243. package/src/tools/auto-generated-guard.ts +1 -1
  244. package/src/tools/bash-interactive.ts +4 -4
  245. package/src/tools/bash-skill-urls.ts +15 -0
  246. package/src/tools/bash.ts +16 -17
  247. package/src/tools/browser/cmux/cmux-tab.ts +66 -18
  248. package/src/tools/browser/launch.ts +41 -12
  249. package/src/tools/browser/readable.ts +9 -9
  250. package/src/tools/browser/tab-supervisor.ts +12 -3
  251. package/src/tools/browser/tab-worker-entry.ts +1 -1
  252. package/src/tools/browser/tab-worker.ts +101 -12
  253. package/src/tools/debug.ts +1 -1
  254. package/src/tools/fetch.ts +1 -1
  255. package/src/tools/jtd-to-json-schema.ts +123 -21
  256. package/src/tools/path-utils.ts +9 -2
  257. package/src/tools/read.ts +21 -9
  258. package/src/tools/run-scope.ts +290 -4
  259. package/src/tools/shell-tokenize.ts +98 -0
  260. package/src/tools/terminal-output.ts +1 -1
  261. package/src/tools/todo.ts +1 -1
  262. package/src/utils/clipboard.ts +10 -2
  263. package/src/utils/external-editor.ts +4 -2
  264. package/src/utils/jj.ts +1 -1
  265. package/src/utils/turndown.ts +1 -2
  266. package/src/vibe/runtime.ts +5 -5
  267. package/src/web/scrapers/arxiv.ts +1 -1
  268. package/src/web/scrapers/go-pkg.ts +1 -1
  269. package/src/web/scrapers/iacr.ts +1 -1
  270. package/src/web/scrapers/readthedocs.ts +2 -1
  271. package/src/web/scrapers/twitter.ts +2 -2
  272. package/src/web/scrapers/types.ts +1 -1
  273. package/src/web/scrapers/wikipedia.ts +1 -1
  274. package/src/web/search/providers/browser-headers.ts +12 -39
  275. package/src/web/search/providers/codex.ts +3 -26
  276. package/src/web/search/providers/ecosia.ts +1 -1
  277. package/src/web/search/providers/exa.ts +1 -1
  278. package/src/web/search/providers/google.ts +1 -1
  279. package/src/web/search/providers/mojeek.ts +1 -1
  280. package/src/web/search/providers/startpage.ts +1 -1
  281. package/src/markit/converters/mammoth.d.ts +0 -24
@@ -2,8 +2,8 @@ import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
2
2
  import type { Api, Model } from "@oh-my-pi/pi-ai";
3
3
  import { Markdown } from "@oh-my-pi/pi-tui";
4
4
  import { prompt } from "@oh-my-pi/pi-utils";
5
+ import chalk from "@oh-my-pi/pi-utils/chalk";
5
6
  import { INTENT_FIELD } from "@oh-my-pi/pi-wire";
6
- import chalk from "chalk";
7
7
  import typesDescriptionPrompt from "../../commit/prompts/types-description.md" with { type: "text" };
8
8
  import type { ModelRegistry } from "../../config/model-registry";
9
9
  import type { Settings } from "../../config/settings";
@@ -11,6 +11,7 @@ import { ModelRegistry } from "../../config/model-registry";
11
11
  import { Settings } from "../../config/settings";
12
12
  import { discoverAuthStorage, discoverContextFiles, loadCliExtensionProviders } from "../../sdk";
13
13
  import * as git from "../../utils/git";
14
+ import { abortOnGitFailure, pushOrAbort } from "../execute";
14
15
  import { type ExistingChangelogEntries, runCommitAgentSession } from "./agent";
15
16
  import { generateFallbackProposal } from "./fallback";
16
17
  import { assignLockFilesToPlan } from "./lock-files";
@@ -25,7 +26,7 @@ interface CommitExecutionContext {
25
26
  push: boolean;
26
27
  }
27
28
 
28
- export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
29
+ export async function runAgenticCommit(args: CommitCommandArgs): Promise<{ usedFallback: boolean }> {
29
30
  const cwd = getProjectDir();
30
31
  const [settings, authStorage] = await Promise.all([Settings.init({ cwd }), discoverAuthStorage()]);
31
32
 
@@ -56,8 +57,13 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
56
57
  );
57
58
 
58
59
  if (stagedFiles.length === 0) {
60
+ if (args.push) {
61
+ process.stdout.write("No changes to commit; pushing existing commits...\n");
62
+ await pushOrAbort(cwd);
63
+ return { usedFallback: false };
64
+ }
59
65
  process.stderr.write("No changes to commit.\n");
60
- return;
66
+ return { usedFallback: false };
61
67
  }
62
68
 
63
69
  if (!args.noChangelog) {
@@ -94,7 +100,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
94
100
  process.stdout.write("● Forcing fallback commit generation...\n");
95
101
  const fallbackProposal = generateFallbackProposal(numstat);
96
102
  await runSingleCommit(fallbackProposal, { cwd, dryRun: args.dryRun, push: args.push });
97
- return;
103
+ return { usedFallback: true };
98
104
  }
99
105
 
100
106
  const trivialChange = detectTrivialChange(diff);
@@ -111,7 +117,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
111
117
  warnings: [],
112
118
  };
113
119
  await runSingleCommit(trivialProposal, { cwd, dryRun: args.dryRun, push: args.push });
114
- return;
120
+ return { usedFallback: false };
115
121
  }
116
122
 
117
123
  let existingChangelogEntries: ExistingChangelogEntries[] | undefined;
@@ -124,6 +130,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
124
130
 
125
131
  process.stdout.write("● Starting commit agent...\n");
126
132
  let agentSessionCompleted = false;
133
+ let usedFallback = false;
127
134
 
128
135
  try {
129
136
  await runCommitAgentSession({
@@ -141,7 +148,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
141
148
  existingChangelogEntries,
142
149
  onComplete: async commitState => {
143
150
  agentSessionCompleted = true;
144
- await completeAgentCommitState(commitState, {
151
+ usedFallback = await completeAgentCommitState(commitState, {
145
152
  cwd,
146
153
  dryRun: args.dryRun,
147
154
  push: args.push,
@@ -151,7 +158,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
151
158
  });
152
159
  },
153
160
  });
154
- return;
161
+ return { usedFallback };
155
162
  } catch (error) {
156
163
  if (agentSessionCompleted) {
157
164
  throw error;
@@ -164,7 +171,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
164
171
  process.stdout.write("● Using fallback commit generation...\n");
165
172
  const fallbackProposal = generateFallbackProposal(numstat);
166
173
  await runSingleCommit(fallbackProposal, { cwd, dryRun: args.dryRun, push: args.push });
167
- return;
174
+ return { usedFallback: true };
168
175
  }
169
176
  }
170
177
 
@@ -175,7 +182,7 @@ async function completeAgentCommitState(
175
182
  changelogTargets: string[];
176
183
  numstat: NumstatEntry[];
177
184
  },
178
- ): Promise<void> {
185
+ ): Promise<boolean> {
179
186
  let usedFallback = false;
180
187
  if (!commitState.proposal && !commitState.splitProposal) {
181
188
  if ($env.PI_COMMIT_NO_FALLBACK?.toLowerCase() !== "true") {
@@ -211,7 +218,7 @@ async function completeAgentCommitState(
211
218
 
212
219
  if (commitState.proposal) {
213
220
  await runSingleCommit(commitState.proposal, ctx);
214
- return;
221
+ return usedFallback;
215
222
  }
216
223
 
217
224
  if (commitState.splitProposal) {
@@ -221,7 +228,7 @@ async function completeAgentCommitState(
221
228
  push: ctx.push,
222
229
  additionalFiles: updatedChangelogFiles,
223
230
  });
224
- return;
231
+ return usedFallback;
225
232
  }
226
233
 
227
234
  throw new Error("Commit agent did not provide a proposal.");
@@ -238,12 +245,14 @@ async function runSingleCommit(proposal: CommitProposal, ctx: CommitExecutionCon
238
245
  return;
239
246
  }
240
247
  process.stdout.write("● Creating commit...\n");
241
- await git.commit(ctx.cwd, commitMessage);
242
- process.stdout.write("Commit created.\n");
243
- if (ctx.push) {
244
- await git.push(ctx.cwd);
245
- process.stdout.write("Pushed to remote.\n");
248
+ try {
249
+ await git.commit(ctx.cwd, commitMessage);
250
+ } catch (error) {
251
+ if (error instanceof git.GitCommandError) abortOnGitFailure("Commit failed", error);
252
+ throw error;
246
253
  }
254
+ process.stdout.write("Commit created.\n");
255
+ if (ctx.push) await pushOrAbort(ctx.cwd);
247
256
  }
248
257
 
249
258
  async function runSplitCommit(
@@ -296,7 +305,7 @@ async function runSplitCommit(
296
305
  process.stdout.write("● Creating split commits...\n");
297
306
  const stagedDiff = await git.diff(ctx.cwd, { cached: true, binary: true });
298
307
  await git.stage.reset(ctx.cwd);
299
- for (const commitIndex of order) {
308
+ for (const [position, commitIndex] of order.entries()) {
300
309
  const commit = plan.commits[commitIndex];
301
310
  await git.stage.hunks(ctx.cwd, commit.changes, { rawDiff: stagedDiff, diffCached: true });
302
311
  const analysis: ConventionalAnalysis = {
@@ -306,14 +315,23 @@ async function runSplitCommit(
306
315
  issueRefs: commit.issueRefs,
307
316
  };
308
317
  const message = formatCommitMessage(analysis, commit.summary);
309
- await git.commit(ctx.cwd, message);
318
+ try {
319
+ await git.commit(ctx.cwd, message);
320
+ } catch (error) {
321
+ if (error instanceof git.GitCommandError) {
322
+ const stagedNow = await git.diff.changedFiles(ctx.cwd, { cached: true });
323
+ abortOnGitFailure(
324
+ `Commit ${position + 1} of ${order.length} failed`,
325
+ error,
326
+ `${position} of ${order.length} commits created; ${stagedNow.length} file(s) remain staged. No changes were lost.`,
327
+ );
328
+ }
329
+ throw error;
330
+ }
310
331
  await git.stage.reset(ctx.cwd);
311
332
  }
312
333
  process.stdout.write("Split commits created.\n");
313
- if (ctx.push) {
314
- await git.push(ctx.cwd);
315
- process.stdout.write("Pushed to remote.\n");
316
- }
334
+ if (ctx.push) await pushOrAbort(ctx.cwd);
317
335
  }
318
336
 
319
337
  function appendFilesToLastCommit(plan: SplitCommitPlan, files: string[]): void {
package/src/commit/cli.ts CHANGED
@@ -1,4 +1,4 @@
1
- import chalk from "chalk";
1
+ import chalk from "@oh-my-pi/pi-utils/chalk";
2
2
  import type { CommitCommandArgs } from "./types";
3
3
 
4
4
  const FLAG_ALIASES = new Map<string, string>([
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Shared commit-execution helpers for the agentic and legacy `omp commit`
3
+ * pipelines: readable failure reporting for refusing git hooks and a push
4
+ * wrapper that keeps a requested `--push` honest.
5
+ */
6
+
7
+ import * as git from "../utils/git";
8
+
9
+ /**
10
+ * A commit or push failure that has already been reported to the user with a
11
+ * readable message. It is thrown so the CLI exits non-zero without the runtime
12
+ * dumping a stack trace — in a bundled build that trace is several kilobytes of
13
+ * minified source, which buries the one line the user needs (issue #7834).
14
+ *
15
+ * `runCommitCommand`'s caller (`commands/commit.ts`) maps this to exit code 1.
16
+ */
17
+ export class CommitAbortedError extends Error {
18
+ constructor() {
19
+ super("commit aborted");
20
+ this.name = "CommitAbortedError";
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Print a git-command failure — most often a `pre-commit`/`commit-msg` hook
26
+ * that exited non-zero — as an indented message under `context`, then abort.
27
+ *
28
+ * @param context Label for the failed step, e.g. `"Commit 1 of 2 failed"`.
29
+ * @param error The failure to surface; its captured stderr/stdout is shown.
30
+ * @param note Optional trailing status line (split-plan progress, recovery).
31
+ */
32
+ export function abortOnGitFailure(context: string, error: git.GitCommandError, note?: string): never {
33
+ const detail = error.result.stderr.trim() || error.result.stdout.trim() || error.message;
34
+ const body = detail
35
+ .split("\n")
36
+ .map(line => ` ${line}`)
37
+ .join("\n");
38
+ process.stderr.write(`✗ ${context}:\n${body}\n`);
39
+ if (note) process.stderr.write(` ${note}\n`);
40
+ throw new CommitAbortedError();
41
+ }
42
+
43
+ /**
44
+ * Push the current branch, reporting a refused push (missing upstream, rejected
45
+ * ref) through {@link abortOnGitFailure} instead of letting the raw
46
+ * `GitCommandError` escape. Prints the success line on completion.
47
+ */
48
+ export async function pushOrAbort(cwd: string): Promise<void> {
49
+ try {
50
+ await git.push(cwd);
51
+ } catch (error) {
52
+ if (error instanceof git.GitCommandError) abortOnGitFailure("Push failed", error);
53
+ throw error;
54
+ }
55
+ process.stdout.write("Pushed to remote.\n");
56
+ }
@@ -2,4 +2,5 @@
2
2
  * Entry points for the omp commit command.
3
3
  */
4
4
 
5
- export { runCommitCommand } from "./pipeline";
5
+ export * from "./execute";
6
+ export * from "./pipeline";
@@ -16,6 +16,7 @@ import {
16
16
  validateSummary,
17
17
  } from "./analysis";
18
18
  import { runChangelogFlow } from "./changelog";
19
+ import { abortOnGitFailure, pushOrAbort } from "./execute";
19
20
  import { runMapReduceAnalysis, shouldUseMapReduce } from "./map-reduce";
20
21
  import { formatCommitMessage } from "./message";
21
22
  import { resolvePrimaryModel, resolveSmolModel } from "./model-selection";
@@ -30,10 +31,15 @@ const TYPES_DESCRIPTION = (): string => (typesDescription ??= prompt.render(type
30
31
 
31
32
  /**
32
33
  * Execute the omp commit pipeline for staged changes.
34
+ *
35
+ * Returns whether the commit was generated by the mechanical fallback rather
36
+ * than by the commit agent, so callers can signal a degraded outcome (issue
37
+ * #7835). The legacy pipeline has no fallback: model failures throw.
33
38
  */
34
- export async function runCommitCommand(args: CommitCommandArgs): Promise<void> {
39
+ export async function runCommitCommand(args: CommitCommandArgs): Promise<{ usedFallback: boolean }> {
35
40
  if (args.legacy) {
36
- return runLegacyCommitCommand(args);
41
+ await runLegacyCommitCommand(args);
42
+ return { usedFallback: false };
37
43
  }
38
44
  return runAgenticCommit(args);
39
45
  }
@@ -65,6 +71,11 @@ async function runLegacyCommitCommand(args: CommitCommandArgs): Promise<void> {
65
71
  stagedFiles = await git.diff.changedFiles(cwd, { cached: true });
66
72
  }
67
73
  if (stagedFiles.length === 0) {
74
+ if (args.push) {
75
+ process.stdout.write("No changes to commit; pushing existing commits...\n");
76
+ await pushOrAbort(cwd);
77
+ return;
78
+ }
68
79
  process.stderr.write("No changes to commit.\n");
69
80
  return;
70
81
  }
@@ -130,12 +141,14 @@ async function runLegacyCommitCommand(args: CommitCommandArgs): Promise<void> {
130
141
  return;
131
142
  }
132
143
 
133
- await git.commit(cwd, commitMessage);
134
- process.stdout.write("Commit created.\n");
135
- if (args.push) {
136
- await git.push(cwd);
137
- process.stdout.write("Pushed to remote.\n");
144
+ try {
145
+ await git.commit(cwd, commitMessage);
146
+ } catch (error) {
147
+ if (error instanceof git.GitCommandError) abortOnGitFailure("Commit failed", error);
148
+ throw error;
138
149
  }
150
+ process.stdout.write("Commit created.\n");
151
+ if (args.push) await pushOrAbort(cwd);
139
152
  }
140
153
 
141
154
  async function generateAnalysis(input: {
@@ -974,8 +974,8 @@ export async function discoverProxyModels(
974
974
  const reference = resolveModelReference(id, getBundledModelReferenceIndex());
975
975
  const discoveryName = typeof item.name === "string" ? item.name.trim() : "";
976
976
  const displayName =
977
- reference?.name ??
978
977
  (discoveryName && discoveryName !== id ? discoveryName : undefined) ??
978
+ reference?.name ??
979
979
  stripBracketedModelIdAffixes(id) ??
980
980
  id;
981
981
  discovered.push(
@@ -65,7 +65,7 @@ const BUILT_IN_DISCOVERY_CACHE_TTL_MS = 2 * 60 * 60 * 1000;
65
65
  const BUILT_IN_DISCOVERY_NON_AUTHORITATIVE_RETRY_MS = 5 * 60 * 1000;
66
66
 
67
67
  import type { ApiKeyResolver, FetchImpl } from "@oh-my-pi/pi-ai";
68
- import { registerOAuthProvider, unregisterOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
68
+ import { registerOAuthProvider, unregisterOAuthProvider, unregisterOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
69
69
  import type { OAuthCredentials, OAuthLoginCallbacks } from "@oh-my-pi/pi-ai/oauth/types";
70
70
  import { setCodexAttestationProvider } from "@oh-my-pi/pi-ai/providers/openai-codex-responses";
71
71
  import { getProviderDefinition } from "@oh-my-pi/pi-ai/registry";
@@ -2514,6 +2514,26 @@ export class ModelRegistry {
2514
2514
  this.#reloadStaticModels();
2515
2515
  }
2516
2516
 
2517
+ /**
2518
+ * Remove one extension-registered provider and restore its static models.
2519
+ */
2520
+ unregisterProvider(providerName: string): void {
2521
+ const sourceId = this.#runtimeProviderSourceByName.get(providerName);
2522
+ if (sourceId) {
2523
+ const sourceProviders = this.#runtimeProvidersBySource.get(sourceId);
2524
+ sourceProviders?.delete(providerName);
2525
+ if (sourceProviders?.size === 0) {
2526
+ this.#runtimeProvidersBySource.delete(sourceId);
2527
+ }
2528
+ this.#runtimeProviderSourceByName.delete(providerName);
2529
+ }
2530
+ unregisterOAuthProvider(providerName);
2531
+ this.#ensureFullSnapshot();
2532
+ this.#clearRuntimeProviderState(providerName);
2533
+ this.#lastStaticLoadMtime = null;
2534
+ this.#reloadStaticModels();
2535
+ }
2536
+
2517
2537
  /**
2518
2538
  * Remove registrations for extension sources that are no longer active.
2519
2539
  */
@@ -27,7 +27,7 @@ import { DEFAULT_MODEL_PER_PROVIDER } from "@oh-my-pi/pi-catalog/provider-models
27
27
  import { resolveBareVariantAlias, resolveVariantAlias } from "@oh-my-pi/pi-catalog/variant-collapse";
28
28
  import { fuzzyMatch } from "@oh-my-pi/pi-tui";
29
29
  import { logger } from "@oh-my-pi/pi-utils";
30
- import chalk from "chalk";
30
+ import chalk from "@oh-my-pi/pi-utils/chalk";
31
31
  import MODEL_PRIO from "../priority.json" with { type: "json" };
32
32
  import {
33
33
  AUTO_THINKING,
@@ -932,6 +932,28 @@ function normalizeModelPatternList(value: string | string[] | undefined): string
932
932
  return patterns.map(pattern => pattern.trim()).filter(Boolean);
933
933
  }
934
934
 
935
+ /**
936
+ * Extract the first explicit model-role alias from a raw model selection.
937
+ *
938
+ * This intentionally runs before role expansion so callers can retain the
939
+ * source identity (`@smol`, `pi/slow`, or `*`) even when it resolves to a
940
+ * concrete provider/model or inherited fallback. Bare role names and explicit
941
+ * provider/model selectors are not role aliases.
942
+ */
943
+ export function resolveExplicitModelRole(
944
+ value: string | string[] | undefined,
945
+ settings?: ModelRoleLookup,
946
+ ): string | undefined {
947
+ for (const pattern of normalizeModelPatternList(value)) {
948
+ const prefixLength = modelRoleAliasPrefixLength(pattern);
949
+ if (prefixLength === undefined) continue;
950
+ const { base } = splitThinkingSuffix(pattern, prefixLength, MAX_THINKING_SUFFIX_OPTIONS);
951
+ const role = getModelRoleAlias(base, settings);
952
+ if (role) return role;
953
+ }
954
+ return undefined;
955
+ }
956
+
935
957
  function isSessionInheritedAgentPattern(value: string): boolean {
936
958
  return (
937
959
  value === DEFAULT_MODEL_ROLE ||
@@ -1068,6 +1090,8 @@ export function resolveConfiguredModelPatterns(
1068
1090
  });
1069
1091
  }
1070
1092
  export interface AgentModelPatternResolutionOptions {
1093
+ /** Highest-priority request selector, when supplied by a caller. */
1094
+ requestModel?: string | string[];
1071
1095
  settingsOverride?: string | string[];
1072
1096
  agentModel?: string | string[];
1073
1097
  settings?: Settings;
@@ -1075,11 +1099,25 @@ export interface AgentModelPatternResolutionOptions {
1075
1099
  fallbackModelPattern?: string;
1076
1100
  }
1077
1101
 
1078
- export function resolveAgentModelPatterns(options: AgentModelPatternResolutionOptions): string[] {
1079
- const { settingsOverride, agentModel, settings, activeModelPattern, fallbackModelPattern } = options;
1102
+ interface EffectiveAgentModelSelection {
1103
+ source?: string | string[];
1104
+ patterns: string[];
1105
+ }
1106
+
1107
+ function resolveEffectiveAgentModelSelection(
1108
+ options: AgentModelPatternResolutionOptions,
1109
+ ): EffectiveAgentModelSelection {
1110
+ const { requestModel, settingsOverride, agentModel, settings, activeModelPattern, fallbackModelPattern } = options;
1111
+
1112
+ const requestPatterns = resolveConfiguredModelPatterns(requestModel, settings);
1113
+ if (requestPatterns.length > 0) {
1114
+ return { source: requestModel, patterns: requestPatterns };
1115
+ }
1080
1116
 
1081
1117
  const overridePatterns = resolveConfiguredModelPatterns(settingsOverride, settings);
1082
- if (overridePatterns.length > 0) return overridePatterns;
1118
+ if (overridePatterns.length > 0) {
1119
+ return { source: settingsOverride, patterns: overridePatterns };
1120
+ }
1083
1121
 
1084
1122
  const normalizedAgentPatterns = normalizeModelPatternList(agentModel);
1085
1123
  const configuredAgentPatterns = resolveConfiguredModelPatterns(agentModel, settings);
@@ -1090,14 +1128,23 @@ export function resolveAgentModelPatterns(options: AgentModelPatternResolutionOp
1090
1128
  singleAgentPattern === formatModelRoleAlias("task") ||
1091
1129
  singleAgentPattern === `${LEGACY_MODEL_ROLE_ALIAS_PREFIX}task`
1092
1130
  ) {
1093
- return configuredAgentPatterns;
1131
+ return { source: agentModel, patterns: configuredAgentPatterns };
1094
1132
  }
1095
- if (!agentInheritsSessionModel) return configuredAgentPatterns;
1133
+ if (!agentInheritsSessionModel) return { source: agentModel, patterns: configuredAgentPatterns };
1096
1134
  }
1097
1135
 
1098
1136
  const fallback =
1099
1137
  activeModelPattern?.trim() || fallbackModelPattern?.trim() || settings?.getModelRole("default")?.trim() || "";
1100
- return resolveConfiguredModelPatterns(fallback, settings);
1138
+ return { patterns: resolveConfiguredModelPatterns(fallback, settings) };
1139
+ }
1140
+
1141
+ /** Return the raw selector source that supplies the effective agent patterns. */
1142
+ export function resolveAgentModelSource(options: AgentModelPatternResolutionOptions): string | string[] | undefined {
1143
+ return resolveEffectiveAgentModelSelection(options).source;
1144
+ }
1145
+
1146
+ export function resolveAgentModelPatterns(options: AgentModelPatternResolutionOptions): string[] {
1147
+ return resolveEffectiveAgentModelSelection(options).patterns;
1101
1148
  }
1102
1149
  /** Default prewalk hand-off target when no explicit target is configured. */
1103
1150
  export const DEFAULT_PREWALK_TARGET = "@smol";
@@ -5406,19 +5406,13 @@ export const SETTINGS_SCHEMA = {
5406
5406
 
5407
5407
  // Exa
5408
5408
  "exa.enabled": {
5409
- type: "boolean",
5410
- default: true,
5411
- ui: { tab: "providers", group: "Services", label: "Exa", description: "Master toggle for all Exa search tools" },
5412
- },
5413
-
5414
- "exa.enableSearch": {
5415
5409
  type: "boolean",
5416
5410
  default: true,
5417
5411
  ui: {
5418
5412
  tab: "providers",
5419
5413
  group: "Services",
5420
- label: "Exa Search",
5421
- description: "Enable Exa basic search, deep search, code search, and crawl tools",
5414
+ label: "Exa",
5415
+ description: "Enable the Exa web search provider",
5422
5416
  },
5423
5417
  },
5424
5418
 
@@ -5433,28 +5427,6 @@ export const SETTINGS_SCHEMA = {
5433
5427
  },
5434
5428
  },
5435
5429
 
5436
- "exa.enableResearcher": {
5437
- type: "boolean",
5438
- default: false,
5439
- ui: {
5440
- tab: "providers",
5441
- group: "Services",
5442
- label: "Exa Researcher",
5443
- description: "Enable the Exa researcher tool for AI-powered deep research",
5444
- },
5445
- },
5446
-
5447
- "exa.enableWebsets": {
5448
- type: "boolean",
5449
- default: false,
5450
- ui: {
5451
- tab: "providers",
5452
- group: "Services",
5453
- label: "Exa Websets",
5454
- description: "Enable Exa webset management and enrichment tools",
5455
- },
5456
- },
5457
-
5458
5430
  // SearXNG
5459
5431
  "searxng.endpoint": {
5460
5432
  type: "string",
@@ -5794,10 +5766,7 @@ export interface TtsrSettings {
5794
5766
 
5795
5767
  export interface ExaSettings {
5796
5768
  enabled: boolean;
5797
- enableSearch: boolean;
5798
5769
  searchDelayMs: number;
5799
- enableResearcher: boolean;
5800
- enableWebsets: boolean;
5801
5770
  }
5802
5771
 
5803
5772
  export interface StatusLineSettings {
@@ -1860,6 +1860,52 @@ export class Settings {
1860
1860
  : undefined,
1861
1861
  );
1862
1862
 
1863
+ // Consolidate the retired Exa suite toggles onto the sole remaining
1864
+ // provider switch. The old runtime required both `enabled` and
1865
+ // `enableSearch`, so preserve that AND semantics when both are present.
1866
+ // Researcher and Websets were removed with the standalone Exa tools.
1867
+ const exaObj = isRecord(raw.exa) ? raw.exa : undefined;
1868
+ const exaEnabledValues = [
1869
+ exaObj?.enabled,
1870
+ raw["exa.enabled"],
1871
+ exaObj?.enableSearch,
1872
+ raw["exa.enableSearch"],
1873
+ ].filter((value): value is boolean => typeof value === "boolean");
1874
+ const hasFlatExaSetting =
1875
+ "exa.enabled" in raw ||
1876
+ "exa.enableSearch" in raw ||
1877
+ "exa.enableResearcher" in raw ||
1878
+ "exa.enableWebsets" in raw;
1879
+ if (exaObj || hasFlatExaSetting) {
1880
+ const exaRoot = exaObj ?? {};
1881
+ if (exaEnabledValues.length > 0) {
1882
+ exaRoot.enabled = exaEnabledValues.every(Boolean);
1883
+ }
1884
+ delete exaRoot.enableSearch;
1885
+ delete exaRoot.enableResearcher;
1886
+ delete exaRoot.enableWebsets;
1887
+ if (Object.keys(exaRoot).length > 0) {
1888
+ raw.exa = exaRoot;
1889
+ } else {
1890
+ delete raw.exa;
1891
+ }
1892
+ delete raw["exa.enabled"];
1893
+ delete raw["exa.enableSearch"];
1894
+ delete raw["exa.enableResearcher"];
1895
+ delete raw["exa.enableWebsets"];
1896
+ }
1897
+
1898
+ // computer.backend and model-specific controller routing were removed
1899
+ // when the computer tool moved to one native desktop implementation.
1900
+ const computerObj = isRecord(raw.computer) ? raw.computer : undefined;
1901
+ if (computerObj && "backend" in computerObj) {
1902
+ delete computerObj.backend;
1903
+ if (Object.keys(computerObj).length === 0) {
1904
+ delete raw.computer;
1905
+ }
1906
+ }
1907
+ delete raw["computer.backend"];
1908
+
1863
1909
  return raw;
1864
1910
  }
1865
1911