@oh-my-pi/pi-coding-agent 15.5.15 → 15.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/dist/types/capability/rule-buckets.d.ts +30 -0
  3. package/dist/types/capability/rule.d.ts +7 -0
  4. package/dist/types/cli/classify-install-target.d.ts +0 -10
  5. package/dist/types/cli/completion-gen.d.ts +80 -0
  6. package/dist/types/cli/initial-message.d.ts +1 -1
  7. package/dist/types/cli/tiny-models-cli.d.ts +9 -0
  8. package/dist/types/commands/complete.d.ts +6 -0
  9. package/dist/types/commands/completions.d.ts +13 -0
  10. package/dist/types/commands/setup.d.ts +10 -1
  11. package/dist/types/commands/tiny-models.d.ts +22 -0
  12. package/dist/types/commit/analysis/conventional.d.ts +1 -1
  13. package/dist/types/commit/analysis/summary.d.ts +1 -1
  14. package/dist/types/commit/changelog/generate.d.ts +1 -1
  15. package/dist/types/commit/changelog/index.d.ts +2 -2
  16. package/dist/types/commit/map-reduce/map-phase.d.ts +1 -1
  17. package/dist/types/commit/map-reduce/reduce-phase.d.ts +1 -1
  18. package/dist/types/config/model-id-affixes.d.ts +10 -0
  19. package/dist/types/config/settings-schema.d.ts +402 -17
  20. package/dist/types/discovery/builtin-defaults.d.ts +1 -0
  21. package/dist/types/discovery/builtin-rules/index.d.ts +7 -0
  22. package/dist/types/discovery/helpers.d.ts +1 -1
  23. package/dist/types/discovery/index.d.ts +1 -0
  24. package/dist/types/discovery/substitute-plugin-root.d.ts +0 -4
  25. package/dist/types/edit/hashline/block-resolver.d.ts +9 -0
  26. package/dist/types/edit/hashline/index.d.ts +1 -0
  27. package/dist/types/eval/js/shared/rewrite-imports.d.ts +16 -1
  28. package/dist/types/eval/py/kernel.d.ts +3 -0
  29. package/dist/types/eval/py/runtime.d.ts +11 -1
  30. package/dist/types/export/html/template.generated.d.ts +1 -1
  31. package/dist/types/internal-urls/agent-protocol.d.ts +2 -1
  32. package/dist/types/internal-urls/artifact-protocol.d.ts +2 -1
  33. package/dist/types/internal-urls/local-protocol.d.ts +2 -1
  34. package/dist/types/internal-urls/memory-protocol.d.ts +2 -1
  35. package/dist/types/internal-urls/omp-protocol.d.ts +2 -1
  36. package/dist/types/internal-urls/router.d.ts +8 -1
  37. package/dist/types/internal-urls/rule-protocol.d.ts +2 -1
  38. package/dist/types/internal-urls/skill-protocol.d.ts +2 -1
  39. package/dist/types/internal-urls/types.d.ts +26 -0
  40. package/dist/types/main.d.ts +1 -0
  41. package/dist/types/memory-backend/index.d.ts +1 -0
  42. package/dist/types/memory-backend/resolve.d.ts +2 -1
  43. package/dist/types/memory-backend/types.d.ts +7 -1
  44. package/dist/types/mnemosyne/backend.d.ts +4 -0
  45. package/dist/types/mnemosyne/config.d.ts +29 -0
  46. package/dist/types/mnemosyne/index.d.ts +3 -0
  47. package/dist/types/mnemosyne/state.d.ts +72 -0
  48. package/dist/types/modes/components/custom-editor.d.ts +2 -3
  49. package/dist/types/modes/components/hook-selector.d.ts +27 -0
  50. package/dist/types/modes/components/index.d.ts +2 -0
  51. package/dist/types/modes/components/segment-track.d.ts +22 -0
  52. package/dist/types/modes/components/status-line/context-thresholds.d.ts +6 -0
  53. package/dist/types/modes/components/tiny-title-download-progress.d.ts +11 -0
  54. package/dist/types/modes/components/welcome.d.ts +22 -0
  55. package/dist/types/modes/controllers/extension-ui-controller.d.ts +4 -1
  56. package/dist/types/modes/gradient-highlight.d.ts +23 -0
  57. package/dist/types/modes/interactive-mode.d.ts +7 -4
  58. package/dist/types/modes/internal-url-autocomplete.d.ts +43 -0
  59. package/dist/types/modes/orchestrate.d.ts +10 -0
  60. package/dist/types/modes/setup-wizard/index.d.ts +16 -0
  61. package/dist/types/modes/setup-wizard/scenes/glyph.d.ts +2 -0
  62. package/dist/types/modes/setup-wizard/scenes/outro.d.ts +2 -0
  63. package/dist/types/modes/setup-wizard/scenes/providers.d.ts +2 -0
  64. package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +19 -0
  65. package/dist/types/modes/setup-wizard/scenes/splash.d.ts +11 -0
  66. package/dist/types/modes/setup-wizard/scenes/theme.d.ts +2 -0
  67. package/dist/types/modes/setup-wizard/scenes/types.d.ts +43 -0
  68. package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +19 -0
  69. package/dist/types/modes/setup-wizard/wizard-overlay.d.ts +14 -0
  70. package/dist/types/modes/theme/defaults/index.d.ts +8406 -8406
  71. package/dist/types/modes/theme/shimmer.d.ts +2 -0
  72. package/dist/types/modes/theme/theme.d.ts +11 -0
  73. package/dist/types/modes/types.d.ts +5 -1
  74. package/dist/types/modes/ultrathink.d.ts +3 -3
  75. package/dist/types/modes/utils/keybinding-matchers.d.ts +5 -0
  76. package/dist/types/sdk.d.ts +3 -0
  77. package/dist/types/session/agent-session.d.ts +33 -0
  78. package/dist/types/system-prompt.d.ts +2 -0
  79. package/dist/types/task/executor.d.ts +2 -0
  80. package/dist/types/task/render.d.ts +5 -1
  81. package/dist/types/tiny/device.d.ts +78 -0
  82. package/dist/types/tiny/dtype.d.ts +85 -0
  83. package/dist/types/tiny/models.d.ts +185 -0
  84. package/dist/types/tiny/text.d.ts +19 -0
  85. package/dist/types/tiny/title-client.d.ts +32 -0
  86. package/dist/types/tiny/title-protocol.d.ts +74 -0
  87. package/dist/types/tiny/worker.d.ts +2 -0
  88. package/dist/types/tools/bash.d.ts +3 -2
  89. package/dist/types/tools/eval.d.ts +1 -1
  90. package/dist/types/tools/index.d.ts +7 -4
  91. package/dist/types/tools/memory-edit.d.ts +40 -0
  92. package/dist/types/tools/{hindsight-recall.d.ts → memory-recall.d.ts} +6 -6
  93. package/dist/types/tools/{hindsight-reflect.d.ts → memory-reflect.d.ts} +6 -6
  94. package/dist/types/tools/memory-render.d.ts +60 -0
  95. package/dist/types/tools/{hindsight-retain.d.ts → memory-retain.d.ts} +6 -6
  96. package/dist/types/tools/todo-write.d.ts +8 -0
  97. package/dist/types/tools/tool-result.d.ts +2 -0
  98. package/dist/types/tui/code-cell.d.ts +2 -0
  99. package/dist/types/tui/output-block.d.ts +17 -0
  100. package/dist/types/utils/title-generator.d.ts +3 -0
  101. package/package.json +18 -14
  102. package/scripts/build-binary.ts +1 -0
  103. package/src/capability/rule-buckets.ts +64 -0
  104. package/src/capability/rule.ts +8 -0
  105. package/src/cli/completion-gen.ts +550 -0
  106. package/src/cli/setup-cli.ts +5 -3
  107. package/src/cli/tiny-models-cli.ts +127 -0
  108. package/src/cli-commands.ts +3 -0
  109. package/src/cli.ts +9 -15
  110. package/src/commands/complete.ts +66 -0
  111. package/src/commands/completions.ts +60 -0
  112. package/src/commands/setup.ts +29 -4
  113. package/src/commands/tiny-models.ts +36 -0
  114. package/src/config/model-equivalence.ts +43 -2
  115. package/src/config/model-id-affixes.ts +64 -0
  116. package/src/config/model-registry.ts +84 -10
  117. package/src/config/settings-schema.ts +275 -15
  118. package/src/discovery/builtin-defaults.ts +39 -0
  119. package/src/discovery/builtin-rules/index.ts +48 -0
  120. package/src/discovery/builtin-rules/rs-box-leak.md +48 -0
  121. package/src/discovery/builtin-rules/rs-future-prelude.md +23 -0
  122. package/src/discovery/builtin-rules/rs-lazylock.md +51 -0
  123. package/src/discovery/builtin-rules/rs-match-ergonomics.md +67 -0
  124. package/src/discovery/builtin-rules/rs-parking-lot.md +44 -0
  125. package/src/discovery/builtin-rules/rs-result-type.md +19 -0
  126. package/src/discovery/builtin-rules/ts-bare-catch.md +38 -0
  127. package/src/discovery/builtin-rules/ts-import-type.md +42 -0
  128. package/src/discovery/builtin-rules/ts-no-any.md +56 -0
  129. package/src/discovery/builtin-rules/ts-no-dynamic-import.md +39 -0
  130. package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
  131. package/src/discovery/builtin-rules/ts-no-tiny-functions.md +50 -0
  132. package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +65 -0
  133. package/src/discovery/builtin-rules/ts-set-map.md +28 -0
  134. package/src/discovery/index.ts +1 -0
  135. package/src/edit/hashline/block-resolver.ts +14 -0
  136. package/src/edit/hashline/diff.ts +9 -8
  137. package/src/edit/hashline/execute.ts +2 -1
  138. package/src/edit/hashline/index.ts +1 -0
  139. package/src/eval/__tests__/shared-executors.test.ts +36 -0
  140. package/src/eval/js/shared/local-module-loader.ts +13 -1
  141. package/src/eval/js/shared/rewrite-imports.ts +31 -26
  142. package/src/eval/py/kernel.ts +37 -15
  143. package/src/eval/py/runtime.ts +57 -28
  144. package/src/export/html/template.generated.ts +1 -1
  145. package/src/export/html/template.js +0 -12
  146. package/src/export/ttsr.ts +2 -0
  147. package/src/internal-urls/agent-protocol.ts +18 -1
  148. package/src/internal-urls/artifact-protocol.ts +19 -1
  149. package/src/internal-urls/docs-index.generated.ts +8 -7
  150. package/src/internal-urls/local-protocol.ts +14 -1
  151. package/src/internal-urls/memory-protocol.ts +6 -1
  152. package/src/internal-urls/omp-protocol.ts +5 -1
  153. package/src/internal-urls/router.ts +20 -1
  154. package/src/internal-urls/rule-protocol.ts +8 -1
  155. package/src/internal-urls/skill-protocol.ts +8 -1
  156. package/src/internal-urls/types.ts +27 -0
  157. package/src/lsp/render.ts +1 -1
  158. package/src/main.ts +18 -1
  159. package/src/mcp/oauth-flow.ts +2 -2
  160. package/src/memory-backend/index.ts +1 -0
  161. package/src/memory-backend/resolve.ts +4 -1
  162. package/src/memory-backend/types.ts +8 -1
  163. package/src/mnemosyne/backend.ts +374 -0
  164. package/src/mnemosyne/config.ts +160 -0
  165. package/src/mnemosyne/index.ts +3 -0
  166. package/src/mnemosyne/state.ts +548 -0
  167. package/src/modes/acp/acp-agent.ts +11 -6
  168. package/src/modes/components/agent-dashboard.ts +4 -4
  169. package/src/modes/components/custom-editor.ts +3 -2
  170. package/src/modes/components/diff.ts +2 -2
  171. package/src/modes/components/extensions/extension-list.ts +3 -2
  172. package/src/modes/components/footer.ts +5 -6
  173. package/src/modes/components/history-search.ts +3 -3
  174. package/src/modes/components/hook-selector.ts +92 -8
  175. package/src/modes/components/index.ts +2 -0
  176. package/src/modes/components/mcp-add-wizard.ts +3 -3
  177. package/src/modes/components/model-selector.ts +5 -4
  178. package/src/modes/components/oauth-selector.ts +3 -3
  179. package/src/modes/components/segment-track.ts +52 -0
  180. package/src/modes/components/session-observer-overlay.ts +19 -13
  181. package/src/modes/components/session-selector.ts +3 -3
  182. package/src/modes/components/settings-defs.ts +7 -0
  183. package/src/modes/components/status-line/context-thresholds.ts +11 -0
  184. package/src/modes/components/status-line/segments.ts +2 -2
  185. package/src/modes/components/tiny-title-download-progress.ts +90 -0
  186. package/src/modes/components/tips.txt +13 -0
  187. package/src/modes/components/tool-execution.ts +72 -4
  188. package/src/modes/components/tree-selector.ts +3 -3
  189. package/src/modes/components/user-message-selector.ts +3 -3
  190. package/src/modes/components/welcome.ts +102 -43
  191. package/src/modes/controllers/command-controller.ts +16 -1
  192. package/src/modes/controllers/extension-ui-controller.ts +3 -1
  193. package/src/modes/controllers/input-controller.ts +69 -21
  194. package/src/modes/gradient-highlight.ts +70 -0
  195. package/src/modes/interactive-mode.ts +75 -114
  196. package/src/modes/internal-url-autocomplete.ts +143 -0
  197. package/src/modes/orchestrate.ts +36 -0
  198. package/src/modes/prompt-action-autocomplete.ts +12 -0
  199. package/src/modes/setup-wizard/index.ts +88 -0
  200. package/src/modes/setup-wizard/scenes/glyph.ts +96 -0
  201. package/src/modes/setup-wizard/scenes/outro.ts +35 -0
  202. package/src/modes/setup-wizard/scenes/providers.ts +69 -0
  203. package/src/modes/setup-wizard/scenes/sign-in.ts +193 -0
  204. package/src/modes/setup-wizard/scenes/splash.ts +201 -0
  205. package/src/modes/setup-wizard/scenes/theme.ts +299 -0
  206. package/src/modes/setup-wizard/scenes/types.ts +48 -0
  207. package/src/modes/setup-wizard/scenes/web-search.ts +128 -0
  208. package/src/modes/setup-wizard/wizard-overlay.ts +275 -0
  209. package/src/modes/theme/shimmer.ts +5 -0
  210. package/src/modes/theme/theme.ts +44 -20
  211. package/src/modes/types.ts +6 -1
  212. package/src/modes/ultrathink.ts +9 -53
  213. package/src/modes/utils/keybinding-matchers.ts +11 -0
  214. package/src/prompts/system/memory-consolidation-system.md +8 -0
  215. package/src/prompts/system/memory-extraction-system.md +26 -0
  216. package/src/prompts/{commands/orchestrate.md → system/orchestrate-notice.md} +6 -17
  217. package/src/prompts/system/system-prompt.md +2 -0
  218. package/src/prompts/system/tiny-title-system.md +8 -0
  219. package/src/prompts/tools/memory-edit.md +8 -0
  220. package/src/prompts/tools/read.md +4 -0
  221. package/src/prompts/tools/task.md +4 -7
  222. package/src/sdk.ts +13 -21
  223. package/src/session/agent-session.ts +128 -44
  224. package/src/slash-commands/builtin-registry.ts +18 -1
  225. package/src/system-prompt.ts +4 -0
  226. package/src/task/commands.ts +1 -5
  227. package/src/task/executor.ts +8 -0
  228. package/src/task/index.ts +2 -0
  229. package/src/task/render.ts +69 -26
  230. package/src/tiny/device.ts +117 -0
  231. package/src/tiny/dtype.ts +101 -0
  232. package/src/tiny/models.ts +218 -0
  233. package/src/tiny/text.ts +54 -0
  234. package/src/tiny/title-client.ts +395 -0
  235. package/src/tiny/title-protocol.ts +51 -0
  236. package/src/tiny/worker.ts +587 -0
  237. package/src/tools/bash.ts +74 -29
  238. package/src/tools/browser/tab-worker.ts +1 -1
  239. package/src/tools/eval.ts +9 -4
  240. package/src/tools/index.ts +17 -22
  241. package/src/tools/memory-edit.ts +59 -0
  242. package/src/tools/memory-recall.ts +100 -0
  243. package/src/tools/memory-reflect.ts +88 -0
  244. package/src/tools/memory-render.ts +185 -0
  245. package/src/tools/memory-retain.ts +91 -0
  246. package/src/tools/read.ts +1 -0
  247. package/src/tools/renderers.ts +4 -2
  248. package/src/tools/todo-write.ts +128 -29
  249. package/src/tools/tool-result.ts +8 -0
  250. package/src/tui/code-cell.ts +6 -1
  251. package/src/tui/output-block.ts +199 -38
  252. package/src/utils/title-generator.ts +115 -13
  253. package/dist/types/tools/recipe/index.d.ts +0 -46
  254. package/dist/types/tools/recipe/render.d.ts +0 -36
  255. package/dist/types/tools/recipe/runner.d.ts +0 -60
  256. package/dist/types/tools/recipe/runners/cargo.d.ts +0 -16
  257. package/dist/types/tools/recipe/runners/index.d.ts +0 -2
  258. package/dist/types/tools/recipe/runners/just.d.ts +0 -2
  259. package/dist/types/tools/recipe/runners/make.d.ts +0 -2
  260. package/dist/types/tools/recipe/runners/pkg.d.ts +0 -2
  261. package/dist/types/tools/recipe/runners/task.d.ts +0 -2
  262. package/src/prompts/tools/recipe.md +0 -16
  263. package/src/tools/hindsight-recall.ts +0 -69
  264. package/src/tools/hindsight-reflect.ts +0 -58
  265. package/src/tools/hindsight-retain.ts +0 -57
  266. package/src/tools/recipe/index.ts +0 -81
  267. package/src/tools/recipe/render.ts +0 -19
  268. package/src/tools/recipe/runner.ts +0 -219
  269. package/src/tools/recipe/runners/cargo.ts +0 -131
  270. package/src/tools/recipe/runners/index.ts +0 -8
  271. package/src/tools/recipe/runners/just.ts +0 -73
  272. package/src/tools/recipe/runners/make.ts +0 -101
  273. package/src/tools/recipe/runners/pkg.ts +0 -167
  274. package/src/tools/recipe/runners/task.ts +0 -72
@@ -17,6 +17,8 @@ export const commands: CommandEntry[] = [
17
17
  { name: "auth-gateway", load: () => import("./commands/auth-gateway").then(m => m.default) },
18
18
  { name: "agents", load: () => import("./commands/agents").then(m => m.default) },
19
19
  { name: "commit", load: () => import("./commands/commit").then(m => m.default) },
20
+ { name: "completions", load: () => import("./commands/completions").then(m => m.default) },
21
+ { name: "__complete", load: () => import("./commands/complete").then(m => m.default) },
20
22
  { name: "config", load: () => import("./commands/config").then(m => m.default) },
21
23
  { name: "grep", load: () => import("./commands/grep").then(m => m.default) },
22
24
  { name: "grievances", load: () => import("./commands/grievances").then(m => m.default) },
@@ -28,6 +30,7 @@ export const commands: CommandEntry[] = [
28
30
  { name: "ssh", load: () => import("./commands/ssh").then(m => m.default) },
29
31
  { name: "stats", load: () => import("./commands/stats").then(m => m.default) },
30
32
  { name: "update", load: () => import("./commands/update").then(m => m.default) },
33
+ { name: "tiny-models", load: () => import("./commands/tiny-models").then(m => m.default) },
31
34
  { name: "worktree", load: () => import("./commands/worktree").then(m => m.default), aliases: ["wt"] },
32
35
  { name: "search", load: () => import("./commands/web-search").then(m => m.default), aliases: ["q"] },
33
36
  ];
package/src/cli.ts CHANGED
@@ -1,16 +1,10 @@
1
1
  #!/usr/bin/env bun
2
- import { APP_NAME, MIN_BUN_VERSION, procmgr, VERSION } from "@oh-my-pi/pi-utils";
3
-
4
- // Strip macOS malloc-stack-logging env vars before any subprocess is spawned.
5
- // Otherwise every child bun process (subagents, plugin installs, ptree spawns,
6
- // etc.) prints a `MallocStackLogging: can't turn off …` warning to stderr.
7
- procmgr.scrubProcessEnv();
8
-
9
2
  /**
10
3
  * CLI entry point — registers all commands explicitly and delegates to the
11
4
  * lightweight CLI runner from pi-utils.
12
5
  */
13
6
  import { type CliConfig, run } from "@oh-my-pi/pi-utils/cli";
7
+ import { APP_NAME, MIN_BUN_VERSION, VERSION } from "@oh-my-pi/pi-utils/dirs";
14
8
  import { commands, isSubcommand } from "./cli-commands";
15
9
 
16
10
  if (Bun.semver.order(Bun.version, MIN_BUN_VERSION) < 0) {
@@ -32,20 +26,20 @@ async function showHelp(config: CliConfig): Promise<void> {
32
26
  }
33
27
  }
34
28
  /**
35
- * Smoke-test entry. Spawns the stats sync worker, pings it, exits.
29
+ * Smoke-test entry. Spawns bundled workers, pings them, exits.
36
30
  *
37
31
  * Purpose: catch the silent worker-load regressions that hit compiled
38
- * binaries (issues #1011 and #1027). Neither `--version` nor
39
- * `stats --summary` actually spawns a Worker on a fresh install the
40
- * sync path early-returns when no session files exist. This probe is the
41
- * minimal end-to-end test that proves `new Worker(...)` resolves and the
42
- * bundled worker module evaluates successfully. Wired into
43
- * `scripts/install-tests/run-ci.sh` so binary / source-link / tarball
44
- * installs all exercise it on every CI run.
32
+ * binaries (issues #1011 and #1027). Version/help paths do not spawn worker
33
+ * modules on a fresh install, so this probe is the minimal end-to-end test
34
+ * that proves `new Worker(...)` resolves and bundled worker modules evaluate.
35
+ * Wired into `scripts/install-tests/run-ci.sh` so binary / source-link /
36
+ * tarball installs all exercise it on every CI run.
45
37
  */
46
38
  async function runSmokeTest(): Promise<void> {
47
39
  const { smokeTestSyncWorker } = await import("@oh-my-pi/omp-stats");
40
+ const { smokeTestTinyTitleWorker } = await import("./tiny/title-client");
48
41
  await smokeTestSyncWorker();
42
+ await smokeTestTinyTitleWorker();
49
43
  process.stdout.write("smoke-test: ok\n");
50
44
  }
51
45
 
@@ -0,0 +1,66 @@
1
+ /**
2
+ * `omp __complete <kind> [-- <prefix>]` — dynamic completion candidates.
3
+ *
4
+ * Hidden helper invoked by the generated shell completion scripts to resolve
5
+ * values that can't be baked into the script: the live model catalog and
6
+ * on-disk sessions. Output is one `value\tdescription` line per candidate
7
+ * (tab-separated); shells that show descriptions parse the tab, bash uses the
8
+ * first field. The import surface is kept deliberately narrow so a TAB press
9
+ * doesn't pay for the full agent boot.
10
+ */
11
+ import { type GeneratedProvider, getBundledModels, getBundledProviders } from "@oh-my-pi/pi-ai/models";
12
+ import { Command } from "@oh-my-pi/pi-utils/cli";
13
+ import { SessionManager } from "../session/session-manager";
14
+
15
+ export default class Complete extends Command {
16
+ static hidden = true;
17
+ static strict = false;
18
+
19
+ async run(): Promise<void> {
20
+ const argv = this.argv.filter(token => token !== "--");
21
+ const kind = argv[0];
22
+ const prefix = argv.length > 1 ? argv[argv.length - 1] : "";
23
+ if (kind === "models") {
24
+ completeModels(prefix);
25
+ } else if (kind === "sessions") {
26
+ await completeSessions(prefix);
27
+ }
28
+ }
29
+ }
30
+
31
+ /** Strip control chars that would corrupt the tab-separated line protocol. */
32
+ function clean(text: string): string {
33
+ return text.replace(/[\t\r\n]+/g, " ").trim();
34
+ }
35
+
36
+ function completeModels(prefix: string): void {
37
+ const needle = prefix.toLowerCase();
38
+ const seen = new Set<string>();
39
+ const lines: string[] = [];
40
+ for (const provider of getBundledProviders()) {
41
+ for (const model of getBundledModels(provider as GeneratedProvider)) {
42
+ // Offer both the fully-qualified `provider/id` and the bare `id`
43
+ // (matches the fuzzy resolution `--model` accepts).
44
+ const candidates = [`${model.provider}/${model.id}`, model.id];
45
+ for (const candidate of candidates) {
46
+ if (seen.has(candidate)) continue;
47
+ seen.add(candidate);
48
+ if (needle && !candidate.toLowerCase().includes(needle)) continue;
49
+ lines.push(`${candidate}\t${model.provider}`);
50
+ }
51
+ }
52
+ }
53
+ lines.sort();
54
+ if (lines.length > 0) process.stdout.write(`${lines.join("\n")}\n`);
55
+ }
56
+
57
+ async function completeSessions(prefix: string): Promise<void> {
58
+ const sessions = await SessionManager.list(process.cwd());
59
+ const lines: string[] = [];
60
+ for (const session of sessions) {
61
+ if (prefix && !session.id.startsWith(prefix)) continue;
62
+ const label = clean(session.title ?? session.firstMessage ?? "").slice(0, 72);
63
+ lines.push(`${session.id}\t${label}`);
64
+ }
65
+ if (lines.length > 0) process.stdout.write(`${lines.join("\n")}\n`);
66
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * `omp completions <bash|zsh|fish>` — print a shell completion script.
3
+ *
4
+ * The script is derived entirely from the declarative command/flag metadata
5
+ * (see `cli/completion-gen.ts`), so it never drifts from the actual CLI surface.
6
+ */
7
+ import { APP_NAME, VERSION } from "@oh-my-pi/pi-utils";
8
+ import { Args, type CliConfig, Command, type CommandCtor } from "@oh-my-pi/pi-utils/cli";
9
+ import { buildSpec, generateCompletion, type Shell } from "../cli/completion-gen";
10
+ import { commands } from "../cli-commands";
11
+
12
+ /** Entry name of the default command whose flags become top-level completions. */
13
+ const ROOT_COMMAND = "launch";
14
+ const SHELLS = ["bash", "zsh", "fish"] as const;
15
+
16
+ export default class Completions extends Command {
17
+ static description = "Print a shell completion script (bash, zsh, or fish)";
18
+
19
+ static args = {
20
+ shell: Args.string({
21
+ description: "Target shell",
22
+ required: true,
23
+ options: SHELLS,
24
+ }),
25
+ };
26
+
27
+ static examples = [
28
+ `# zsh — eval at startup, or write to a file in $fpath\n eval "$(${APP_NAME} completions zsh)"`,
29
+ `# bash\n eval "$(${APP_NAME} completions bash)"`,
30
+ `# fish\n ${APP_NAME} completions fish > ~/.config/fish/completions/${APP_NAME}.fish`,
31
+ ];
32
+
33
+ async run(): Promise<void> {
34
+ const shell = this.argv[0];
35
+ if (!isShell(shell)) {
36
+ process.stderr.write(`Usage: ${APP_NAME} completions <${SHELLS.join("|")}>\n`);
37
+ process.exitCode = 1;
38
+ return;
39
+ }
40
+
41
+ // Load every command class so we can read its static flag/arg descriptors,
42
+ // and collect aliases from both the registration table and the class.
43
+ const loaded = await Promise.all(commands.map(async entry => ({ entry, Cmd: await entry.load() })));
44
+ const map = new Map<string, CommandCtor>();
45
+ const aliasMap = new Map<string, readonly string[]>();
46
+ for (const { entry, Cmd } of loaded) {
47
+ map.set(entry.name, Cmd);
48
+ const merged = new Set<string>([...(Cmd.aliases ?? []), ...(entry.aliases ?? [])]);
49
+ aliasMap.set(entry.name, [...merged]);
50
+ }
51
+
52
+ const config: CliConfig = { bin: APP_NAME, version: VERSION, commands: map };
53
+ const spec = buildSpec(config, ROOT_COMMAND, aliasMap);
54
+ process.stdout.write(generateCompletion(shell, spec));
55
+ }
56
+ }
57
+
58
+ function isShell(value: string | undefined): value is Shell {
59
+ return value === "bash" || value === "zsh" || value === "fish";
60
+ }
@@ -1,18 +1,39 @@
1
1
  /**
2
- * Install dependencies for optional features.
2
+ * Run onboarding setup or install dependencies for optional features.
3
3
  */
4
4
  import { Args, Command, Flags, renderCommandHelp } from "@oh-my-pi/pi-utils/cli";
5
+ import { parseArgs } from "../cli/args";
5
6
  import { runSetupCommand, type SetupCommandArgs, type SetupComponent } from "../cli/setup-cli";
7
+ import { runRootCommand } from "../main";
6
8
  import { initTheme } from "../modes/theme/theme";
7
9
 
8
10
  const COMPONENTS: SetupComponent[] = ["python", "stt"];
9
11
 
12
+ export interface OnboardingSetupDependencies {
13
+ runRoot?: typeof runRootCommand;
14
+ stdinIsTTY?: boolean;
15
+ stdoutIsTTY?: boolean;
16
+ writeStderr?: (text: string) => void;
17
+ exit?: (code: number) => never;
18
+ }
19
+
20
+ export async function runOnboardingSetup(deps: OnboardingSetupDependencies = {}): Promise<void> {
21
+ const stdinIsTTY = deps.stdinIsTTY ?? process.stdin.isTTY;
22
+ const stdoutIsTTY = deps.stdoutIsTTY ?? process.stdout.isTTY;
23
+ if (!stdinIsTTY || !stdoutIsTTY) {
24
+ (deps.writeStderr ?? (text => process.stderr.write(text)))("omp setup requires an interactive TTY.\n");
25
+ (deps.exit ?? process.exit)(1);
26
+ return;
27
+ }
28
+ await (deps.runRoot ?? runRootCommand)(parseArgs([]), [], { forceSetupWizard: true });
29
+ }
30
+
10
31
  export default class Setup extends Command {
11
- static description = "Install dependencies for optional features";
32
+ static description = "Run onboarding setup or install dependencies for optional features";
12
33
 
13
34
  static args = {
14
35
  component: Args.string({
15
- description: "Component to install",
36
+ description: "Optional component to install",
16
37
  required: false,
17
38
  options: COMPONENTS,
18
39
  }),
@@ -26,7 +47,11 @@ export default class Setup extends Command {
26
47
  async run(): Promise<void> {
27
48
  const { args, flags } = await this.parse(Setup);
28
49
  if (!args.component) {
29
- renderCommandHelp("omp", "setup", Setup);
50
+ if (flags.check || flags.json) {
51
+ renderCommandHelp("omp", "setup", Setup);
52
+ return;
53
+ }
54
+ await runOnboardingSetup();
30
55
  return;
31
56
  }
32
57
  const cmd: SetupCommandArgs = {
@@ -0,0 +1,36 @@
1
+ import { Args, Command, Flags } from "@oh-my-pi/pi-utils/cli";
2
+ import { runTinyModelsCommand, type TinyModelsAction, type TinyModelsCommandArgs } from "../cli/tiny-models-cli";
3
+
4
+ const ACTIONS: TinyModelsAction[] = ["download", "list"];
5
+
6
+ export default class TinyModels extends Command {
7
+ static description = "Download tiny local models (session titles + memory)";
8
+
9
+ static args = {
10
+ action: Args.string({
11
+ description: "Action to perform",
12
+ required: false,
13
+ options: ACTIONS,
14
+ }),
15
+ model: Args.string({
16
+ description: "Model key, or all",
17
+ required: false,
18
+ }),
19
+ };
20
+
21
+ static flags = {
22
+ json: Flags.boolean({ description: "Output JSON" }),
23
+ };
24
+
25
+ async run(): Promise<void> {
26
+ const { args, flags } = await this.parse(TinyModels);
27
+ const command: TinyModelsCommandArgs = {
28
+ action: (args.action ?? "download") as TinyModelsAction,
29
+ model: args.model,
30
+ flags: {
31
+ json: flags.json,
32
+ },
33
+ };
34
+ await runTinyModelsCommand(command);
35
+ }
36
+ }
@@ -1,4 +1,9 @@
1
1
  import { type Api, getBundledModels, getBundledProviders, type Model } from "@oh-my-pi/pi-ai";
2
+ import {
3
+ getBracketStrippedModelIdCandidates,
4
+ getLongestModelLikeIdSegment,
5
+ getModelLikeIdSegments,
6
+ } from "./model-id-affixes";
2
7
 
3
8
  export type CanonicalModelSource = "override" | "bundled" | "heuristic" | "fallback";
4
9
 
@@ -29,6 +34,7 @@ export interface CanonicalModelIndex {
29
34
  interface CanonicalReferenceData {
30
35
  references: Map<string, Model<Api>>;
31
36
  officialIds: Set<string>;
37
+ suffixAliases: Map<string, string>;
32
38
  }
33
39
 
34
40
  interface CompiledEquivalenceConfig {
@@ -70,6 +76,26 @@ function shouldReplaceReference(existing: Model<Api> | undefined, candidate: Mod
70
76
  return existing.provider !== "openai" && candidate.provider === "openai";
71
77
  }
72
78
 
79
+ function buildCanonicalSuffixAliasMap(references: ReadonlyMap<string, Model<Api>>): Map<string, string> {
80
+ const aliases = new Map<string, string>();
81
+ for (const reference of references.values()) {
82
+ const slashIndex = reference.id.lastIndexOf("/");
83
+ if (slashIndex === -1) {
84
+ continue;
85
+ }
86
+ const suffix = reference.id.slice(slashIndex + 1);
87
+ const alias = getLongestModelLikeIdSegment(suffix);
88
+ if (!alias) {
89
+ continue;
90
+ }
91
+ const existing = aliases.get(alias);
92
+ if (!existing || compareCandidatePreference(reference.id, existing) < 0) {
93
+ aliases.set(alias, reference.id);
94
+ }
95
+ }
96
+ return new Map([...aliases.entries()].map(([alias, referenceId]) => [normalizeCanonicalIdKey(alias), referenceId]));
97
+ }
98
+
73
99
  function createCanonicalReferenceData(): CanonicalReferenceData {
74
100
  if (referenceDataCache) {
75
101
  return referenceDataCache;
@@ -85,9 +111,11 @@ function createCanonicalReferenceData(): CanonicalReferenceData {
85
111
  }
86
112
  }
87
113
  const officialIds = new Set(references.keys());
114
+ const suffixAliases = buildCanonicalSuffixAliasMap(references);
88
115
  referenceDataCache = {
89
116
  references: Object.freeze(references) as Map<string, Model<Api>>,
90
117
  officialIds: Object.freeze(officialIds) as Set<string>,
118
+ suffixAliases: Object.freeze(suffixAliases) as Map<string, string>,
91
119
  };
92
120
  return referenceDataCache;
93
121
  }
@@ -590,6 +618,13 @@ function expandHeavyCanonicalCandidates(normalized: string, queue: string[]): vo
590
618
  queue.push(wrapperCandidate);
591
619
  }
592
620
 
621
+ for (const strippedAffixCandidate of getBracketStrippedModelIdCandidates(normalized)) {
622
+ queue.push(strippedAffixCandidate);
623
+ }
624
+ for (const segment of getModelLikeIdSegments(normalized)) {
625
+ queue.push(segment);
626
+ }
627
+
593
628
  const strippedSyntheticPrefix = stripSyntheticPrefix(normalized);
594
629
  if (strippedSyntheticPrefix) {
595
630
  queue.push(strippedSyntheticPrefix);
@@ -718,9 +753,15 @@ function resolveCanonicalIdForModel(
718
753
  }
719
754
 
720
755
  const heuristicCandidates = getHeuristicCanonicalCandidates(model.id, referenceData.officialIds);
721
- const officialMatches = heuristicCandidates.filter(candidate => referenceData.officialIds.has(candidate));
756
+ const officialMatches = new Set(heuristicCandidates.filter(candidate => referenceData.officialIds.has(candidate)));
757
+ for (const candidate of heuristicCandidates) {
758
+ const aliased = referenceData.suffixAliases.get(normalizeCanonicalIdKey(candidate));
759
+ if (aliased) {
760
+ officialMatches.add(aliased);
761
+ }
762
+ }
722
763
  const preferredFallback = getPreferredFallbackCanonicalCandidate(model.id, heuristicCandidates);
723
- const match = selectBestOfficialCandidate(officialMatches);
764
+ const match = selectBestOfficialCandidate([...officialMatches]);
724
765
  if (match) {
725
766
  if (
726
767
  preferredFallback &&
@@ -0,0 +1,64 @@
1
+ const LEADING_BRACKETED_AFFIX_PATTERN = /^(?:\s*(?:\[|【)[^\]】]+(?:\]|】)\s*)+/u;
2
+ const TRAILING_BRACKETED_AFFIX_PATTERN = /(?:\s*(?:\[|【)[^\]】]+(?:\]|】)\s*)+$/u;
3
+ const MODEL_ID_SEGMENT_PATTERN = /[a-z0-9.:-]+/g;
4
+ const MODEL_FAMILY_PREFIX_PATTERN =
5
+ /^(claude|gemini|gpt|grok|glm|qwen|deepseek|kimi|mimo|doubao|ernie|gpt-oss|gemma|minimax|step|command|jamba|llama|o[1345])/i;
6
+
7
+ function hasDigit(value: string): boolean {
8
+ return /\d/.test(value);
9
+ }
10
+
11
+ function compareSegmentPreference(left: string, right: string): number {
12
+ if (left.length !== right.length) {
13
+ return right.length - left.length;
14
+ }
15
+ return left.localeCompare(right);
16
+ }
17
+
18
+ export function getModelLikeIdSegments(modelId: string): string[] {
19
+ const normalized = normalizeModelIdWhitespace(modelId).toLowerCase();
20
+ if (!normalized) return [];
21
+ const segments = (normalized.match(MODEL_ID_SEGMENT_PATTERN) ?? []).filter(
22
+ segment => MODEL_FAMILY_PREFIX_PATTERN.test(segment) && hasDigit(segment),
23
+ );
24
+ const unique = [...new Set(segments)];
25
+ unique.sort(compareSegmentPreference);
26
+ return unique;
27
+ }
28
+
29
+ export function getLongestModelLikeIdSegment(modelId: string): string | undefined {
30
+ return getModelLikeIdSegments(modelId)[0];
31
+ }
32
+
33
+ function normalizeModelIdWhitespace(value: string): string {
34
+ return value.trim().replace(/\s+/g, " ");
35
+ }
36
+
37
+ /**
38
+ * Strip reseller / wrapper tags that are injected as bracketed affixes around an
39
+ * upstream model id, e.g.
40
+ * "[Kiro] claude-opus-4-8" -> "claude-opus-4-8"
41
+ * "[gcli转] gemini-3.1-pro-preview [假流]" -> "gemini-3.1-pro-preview"
42
+ */
43
+ export function getBracketStrippedModelIdCandidates(modelId: string): string[] {
44
+ const normalized = normalizeModelIdWhitespace(modelId);
45
+ if (!normalized) return [];
46
+
47
+ const candidates = new Set<string>();
48
+ const withoutLeading = normalizeModelIdWhitespace(normalized.replace(LEADING_BRACKETED_AFFIX_PATTERN, ""));
49
+ const withoutTrailing = normalizeModelIdWhitespace(normalized.replace(TRAILING_BRACKETED_AFFIX_PATTERN, ""));
50
+ const withoutBoth = normalizeModelIdWhitespace(
51
+ normalized.replace(LEADING_BRACKETED_AFFIX_PATTERN, "").replace(TRAILING_BRACKETED_AFFIX_PATTERN, ""),
52
+ );
53
+
54
+ for (const candidate of [withoutBoth, withoutLeading, withoutTrailing]) {
55
+ if (candidate && candidate !== normalized) {
56
+ candidates.add(candidate);
57
+ }
58
+ }
59
+ return [...candidates];
60
+ }
61
+
62
+ export function stripBracketedModelIdAffixes(modelId: string): string | undefined {
63
+ return getBracketStrippedModelIdCandidates(modelId)[0];
64
+ }
@@ -42,6 +42,12 @@ import {
42
42
  formatCanonicalVariantSelector,
43
43
  type ModelEquivalenceConfig,
44
44
  } from "./model-equivalence";
45
+ import {
46
+ getBracketStrippedModelIdCandidates,
47
+ getLongestModelLikeIdSegment,
48
+ getModelLikeIdSegments,
49
+ stripBracketedModelIdAffixes,
50
+ } from "./model-id-affixes";
45
51
  import {
46
52
  type ModelOverride,
47
53
  type ModelsConfig,
@@ -650,20 +656,53 @@ function shouldReplaceCustomReference(existing: Model<Api> | undefined, candidat
650
656
  return existing.provider !== "openai" && candidate.provider === "openai";
651
657
  }
652
658
 
659
+ function normalizeCustomReferenceKey(value: string): string {
660
+ return value.trim().toLowerCase();
661
+ }
662
+
653
663
  function buildCustomReferenceMap(): Map<string, Model<Api>> {
654
664
  const references = new Map<string, Model<Api>>();
655
665
  for (const provider of getBundledProviders()) {
656
666
  for (const model of getBundledModels(provider as Parameters<typeof getBundledModels>[0])) {
657
667
  const candidate = model as Model<Api>;
658
- if (shouldReplaceCustomReference(references.get(candidate.id), candidate)) {
659
- references.set(candidate.id, candidate);
668
+ const key = normalizeCustomReferenceKey(candidate.id);
669
+ if (shouldReplaceCustomReference(references.get(key), candidate)) {
670
+ references.set(key, candidate);
660
671
  }
661
672
  }
662
673
  }
663
674
  return references;
664
675
  }
665
676
 
677
+ function buildCustomReferenceSuffixAliasMap(exactReferences: ReadonlyMap<string, Model<Api>>): Map<string, Model<Api>> {
678
+ const aliases = new Map<string, Model<Api>>();
679
+ for (const reference of exactReferences.values()) {
680
+ const slashIndex = reference.id.lastIndexOf("/");
681
+ if (slashIndex === -1) {
682
+ continue;
683
+ }
684
+ const suffix = reference.id.slice(slashIndex + 1);
685
+ const alias = getLongestModelLikeIdSegment(suffix);
686
+ if (!alias) {
687
+ continue;
688
+ }
689
+ if (shouldReplaceCustomReference(aliases.get(alias), reference)) {
690
+ aliases.set(alias, reference);
691
+ }
692
+ }
693
+ return aliases;
694
+ }
695
+
666
696
  const customReferenceMap = buildCustomReferenceMap();
697
+ const customReferenceSuffixAliasMap = buildCustomReferenceSuffixAliasMap(customReferenceMap);
698
+
699
+ const CUSTOM_REFERENCE_TRAILING_MARKER_PATTERN =
700
+ /[-:](?:thinking|customtools|high|low|medium|minimal|xhigh|free|cloud|exacto|nitro|original|optimized|nvfp4|fp8|fp4|bf16|int8|int4|search)$/i;
701
+
702
+ function stripCustomReferenceTrailingMarker(candidate: string): string | undefined {
703
+ const match = CUSTOM_REFERENCE_TRAILING_MARKER_PATTERN.exec(candidate);
704
+ return match ? candidate.slice(0, match.index) : undefined;
705
+ }
667
706
 
668
707
  function getCustomReferenceCandidateIds(modelId: string): string[] {
669
708
  const candidates = new Set<string>();
@@ -673,23 +712,46 @@ function getCustomReferenceCandidateIds(modelId: string): string[] {
673
712
  if (!candidate || candidates.has(candidate)) continue;
674
713
  candidates.add(candidate);
675
714
 
715
+ for (const stripped of getBracketStrippedModelIdCandidates(candidate)) {
716
+ queue.push(stripped);
717
+ }
718
+ for (const segment of getModelLikeIdSegments(candidate)) {
719
+ queue.push(segment);
720
+ }
721
+
676
722
  for (const suffix of [":cloud", "-cloud"] as const) {
677
723
  if (candidate.toLowerCase().endsWith(suffix)) {
678
724
  queue.push(candidate.slice(0, -suffix.length));
679
725
  }
680
726
  }
681
727
 
728
+ const slashIndex = candidate.lastIndexOf("/");
729
+ if (slashIndex !== -1) {
730
+ queue.push(candidate.slice(slashIndex + 1));
731
+ }
732
+
682
733
  const colonToDash = candidate.replace(/:/g, "-");
683
734
  if (colonToDash !== candidate) {
684
735
  queue.push(colonToDash);
685
736
  }
737
+
738
+ const lowercased = candidate.toLowerCase();
739
+ if (lowercased !== candidate) {
740
+ queue.push(lowercased);
741
+ }
742
+
743
+ const strippedMarker = stripCustomReferenceTrailingMarker(candidate);
744
+ if (strippedMarker) {
745
+ queue.push(strippedMarker);
746
+ }
686
747
  }
687
748
  return [...candidates];
688
749
  }
689
750
 
690
751
  function resolveCustomModelReference(modelId: string): Model<Api> | undefined {
691
752
  for (const candidate of getCustomReferenceCandidateIds(modelId)) {
692
- const reference = customReferenceMap.get(candidate);
753
+ const key = normalizeCustomReferenceKey(candidate);
754
+ const reference = customReferenceMap.get(key) ?? customReferenceSuffixAliasMap.get(key);
693
755
  if (reference) return reference;
694
756
  }
695
757
  return undefined;
@@ -1785,7 +1847,7 @@ export class ModelRegistry {
1785
1847
  throw new Error(`HTTP ${response.status} from ${modelsUrl}`);
1786
1848
  }
1787
1849
  const payload = (await response.json()) as {
1788
- data?: Array<{ id?: string; supported_endpoint_types?: string[] }>;
1850
+ data?: Array<{ id?: string; name?: string; supported_endpoint_types?: string[] }>;
1789
1851
  };
1790
1852
  const items = payload.data ?? [];
1791
1853
  const discovered: Model<Api>[] = [];
@@ -1800,23 +1862,35 @@ export class ModelRegistry {
1800
1862
  : providerConfig.api;
1801
1863
  if (!api) continue;
1802
1864
  const isAnthropic = api === "anthropic-messages";
1865
+ const reference = resolveCustomModelReference(id);
1866
+ const discoveryName = typeof item.name === "string" ? item.name.trim() : "";
1867
+ const displayName =
1868
+ reference?.name ??
1869
+ (discoveryName && discoveryName !== id ? discoveryName : undefined) ??
1870
+ stripBracketedModelIdAffixes(id) ??
1871
+ id;
1803
1872
  discovered.push(
1804
1873
  enrichModelThinking({
1805
1874
  id,
1806
- name: id,
1875
+ name: displayName,
1807
1876
  api,
1808
1877
  provider: providerConfig.provider,
1809
1878
  baseUrl,
1810
- reasoning: false,
1811
- input: ["text"],
1879
+ reasoning: reference?.reasoning ?? false,
1880
+ thinking: reference?.thinking,
1881
+ input: reference?.input ?? ["text"],
1882
+ // Proxy pricing is provider-specific and usually does not match
1883
+ // upstream bundled catalogs, so keep costs local-unknown even when
1884
+ // we successfully recover the upstream model identity.
1812
1885
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
1813
- contextWindow: 128000,
1814
- maxTokens: 8192,
1886
+ contextWindow: reference?.contextWindow ?? 128000,
1887
+ maxTokens: reference?.maxTokens ?? 8192,
1815
1888
  headers,
1816
1889
  // OpenAI-compat fields are no-ops on anthropic models; the
1817
1890
  // Anthropic SDK ignores them. Provider-level disableStrictTools
1818
1891
  // flows in via #applyProviderCompat for the third-party-Anthropic
1819
- // path.
1892
+ // path. Cross-wire bundled compat is intentionally not copied:
1893
+ // request-shaping fields are provider-wire specific.
1820
1894
  compat: isAnthropic
1821
1895
  ? undefined
1822
1896
  : {