@oh-my-pi/pi-coding-agent 16.0.4 → 16.0.6

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 (270) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/dist/cli.js +2027 -1396
  3. package/dist/types/advisor/advise-tool.d.ts +31 -19
  4. package/dist/types/autoresearch/tools/init-experiment.d.ts +13 -17
  5. package/dist/types/autoresearch/tools/log-experiment.d.ts +17 -19
  6. package/dist/types/autoresearch/tools/run-experiment.d.ts +3 -4
  7. package/dist/types/autoresearch/tools/update-notes.d.ts +4 -5
  8. package/dist/types/cli/args.d.ts +1 -0
  9. package/dist/types/cli/bench-cli.d.ts +6 -0
  10. package/dist/types/cli/ttsr-cli.d.ts +39 -0
  11. package/dist/types/commands/launch.d.ts +3 -0
  12. package/dist/types/commands/ttsr.d.ts +57 -0
  13. package/dist/types/commit/agentic/tools/analyze-file.d.ts +4 -5
  14. package/dist/types/commit/agentic/tools/git-file-diff.d.ts +4 -5
  15. package/dist/types/commit/agentic/tools/git-hunk.d.ts +5 -6
  16. package/dist/types/commit/agentic/tools/git-overview.d.ts +4 -5
  17. package/dist/types/commit/agentic/tools/propose-changelog.d.ts +23 -24
  18. package/dist/types/commit/agentic/tools/propose-commit.d.ts +11 -32
  19. package/dist/types/commit/agentic/tools/recent-commits.d.ts +3 -4
  20. package/dist/types/commit/agentic/tools/schemas.d.ts +6 -27
  21. package/dist/types/commit/agentic/tools/split-commit.d.ts +28 -49
  22. package/dist/types/commit/changelog/generate.d.ts +12 -13
  23. package/dist/types/commit/shared-llm.d.ts +10 -37
  24. package/dist/types/config/config-file.d.ts +4 -4
  25. package/dist/types/config/keybindings.d.ts +5 -0
  26. package/dist/types/config/models-config-schema.d.ts +625 -990
  27. package/dist/types/config/models-config.d.ts +229 -217
  28. package/dist/types/config/settings-schema.d.ts +144 -25
  29. package/dist/types/edit/hashline/params.d.ts +7 -11
  30. package/dist/types/edit/index.d.ts +2 -1
  31. package/dist/types/edit/modes/apply-patch.d.ts +4 -5
  32. package/dist/types/edit/modes/patch.d.ts +15 -24
  33. package/dist/types/edit/modes/replace.d.ts +16 -17
  34. package/dist/types/eval/js/index.d.ts +1 -0
  35. package/dist/types/extensibility/custom-commands/types.d.ts +6 -3
  36. package/dist/types/extensibility/custom-tools/types.d.ts +8 -5
  37. package/dist/types/extensibility/extensions/runner.d.ts +5 -2
  38. package/dist/types/extensibility/extensions/types.d.ts +14 -10
  39. package/dist/types/extensibility/hooks/types.d.ts +7 -4
  40. package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +13 -5
  41. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +17 -0
  42. package/dist/types/extensibility/shared-events.d.ts +22 -1
  43. package/dist/types/extensibility/typebox.d.ts +80 -58
  44. package/dist/types/goals/tools/goal-tool.d.ts +11 -24
  45. package/dist/types/index.d.ts +2 -0
  46. package/dist/types/lsp/index.d.ts +11 -26
  47. package/dist/types/lsp/types.d.ts +12 -28
  48. package/dist/types/main.d.ts +1 -0
  49. package/dist/types/mcp/client.d.ts +8 -0
  50. package/dist/types/modes/components/btw-panel.d.ts +1 -0
  51. package/dist/types/modes/components/custom-editor.d.ts +3 -1
  52. package/dist/types/modes/components/status-line/component.d.ts +1 -1
  53. package/dist/types/modes/components/status-line/context-thresholds.d.ts +0 -1
  54. package/dist/types/modes/controllers/btw-controller.d.ts +2 -0
  55. package/dist/types/modes/controllers/input-controller.d.ts +1 -0
  56. package/dist/types/modes/interactive-mode.d.ts +3 -0
  57. package/dist/types/modes/rpc/rpc-types.d.ts +1 -1
  58. package/dist/types/modes/setup-wizard/index.d.ts +1 -0
  59. package/dist/types/modes/setup-wizard/startup-splash.d.ts +7 -0
  60. package/dist/types/modes/theme/theme.d.ts +1 -1
  61. package/dist/types/modes/types.d.ts +3 -0
  62. package/dist/types/modes/utils/context-usage.d.ts +12 -0
  63. package/dist/types/sdk.d.ts +8 -1
  64. package/dist/types/session/agent-session.d.ts +24 -0
  65. package/dist/types/session/session-persistence.d.ts +4 -0
  66. package/dist/types/startup-splash.d.ts +12 -0
  67. package/dist/types/task/types.d.ts +47 -48
  68. package/dist/types/tools/ask.d.ts +26 -27
  69. package/dist/types/tools/ast-edit.d.ts +17 -17
  70. package/dist/types/tools/ast-grep.d.ts +12 -13
  71. package/dist/types/tools/bash.d.ts +20 -17
  72. package/dist/types/tools/browser.d.ts +46 -71
  73. package/dist/types/tools/checkpoint.d.ts +14 -15
  74. package/dist/types/tools/debug.d.ts +82 -145
  75. package/dist/types/tools/eval.d.ts +30 -40
  76. package/dist/types/tools/find.d.ts +17 -18
  77. package/dist/types/tools/gh.d.ts +49 -78
  78. package/dist/types/tools/image-gen.d.ts +20 -36
  79. package/dist/types/tools/inspect-image.d.ts +10 -11
  80. package/dist/types/tools/irc.d.ts +22 -33
  81. package/dist/types/tools/job.d.ts +11 -12
  82. package/dist/types/tools/learn.d.ts +21 -28
  83. package/dist/types/tools/manage-skill.d.ts +13 -22
  84. package/dist/types/tools/memory-edit.d.ts +15 -24
  85. package/dist/types/tools/memory-recall.d.ts +7 -8
  86. package/dist/types/tools/memory-reflect.d.ts +9 -10
  87. package/dist/types/tools/memory-retain.d.ts +13 -14
  88. package/dist/types/tools/read.d.ts +8 -8
  89. package/dist/types/tools/resolve.d.ts +11 -18
  90. package/dist/types/tools/review.d.ts +9 -15
  91. package/dist/types/tools/search-tool-bm25.d.ts +9 -10
  92. package/dist/types/tools/search.d.ts +16 -17
  93. package/dist/types/tools/ssh.d.ts +14 -15
  94. package/dist/types/tools/todo.d.ts +27 -43
  95. package/dist/types/tools/tts.d.ts +8 -9
  96. package/dist/types/tools/write.d.ts +9 -10
  97. package/dist/types/tui/code-cell.d.ts +2 -0
  98. package/dist/types/tui/index.d.ts +1 -0
  99. package/dist/types/tui/width-aware-text.d.ts +23 -0
  100. package/dist/types/utils/image-vision-fallback.d.ts +28 -0
  101. package/dist/types/utils/markit.d.ts +10 -1
  102. package/dist/types/web/search/index.d.ts +17 -28
  103. package/dist/types/web/search/providers/base.d.ts +1 -0
  104. package/dist/types/web/search/providers/gemini.d.ts +1 -0
  105. package/dist/types/web/search/providers/perplexity.d.ts +0 -2
  106. package/dist/types/web/search/types.d.ts +32 -26
  107. package/package.json +14 -13
  108. package/scripts/omp +1 -1
  109. package/src/advisor/__tests__/advisor.test.ts +103 -1
  110. package/src/advisor/advise-tool.ts +47 -11
  111. package/src/autoresearch/tools/init-experiment.ts +13 -16
  112. package/src/autoresearch/tools/log-experiment.ts +15 -18
  113. package/src/autoresearch/tools/run-experiment.ts +3 -3
  114. package/src/autoresearch/tools/update-notes.ts +4 -4
  115. package/src/cli/args.ts +1 -0
  116. package/src/cli/bench-cli.ts +30 -7
  117. package/src/cli/flag-tables.ts +8 -0
  118. package/src/cli/ttsr-cli.ts +995 -0
  119. package/src/cli-commands.ts +1 -0
  120. package/src/cli.ts +7 -1
  121. package/src/collab/host.ts +2 -2
  122. package/src/commands/launch.ts +3 -0
  123. package/src/commands/ttsr.ts +125 -0
  124. package/src/commit/agentic/tools/analyze-file.ts +4 -4
  125. package/src/commit/agentic/tools/git-file-diff.ts +4 -4
  126. package/src/commit/agentic/tools/git-hunk.ts +7 -5
  127. package/src/commit/agentic/tools/git-overview.ts +4 -4
  128. package/src/commit/agentic/tools/propose-changelog.ts +18 -15
  129. package/src/commit/agentic/tools/propose-commit.ts +6 -6
  130. package/src/commit/agentic/tools/recent-commits.ts +3 -3
  131. package/src/commit/agentic/tools/schemas.ts +8 -20
  132. package/src/commit/agentic/tools/split-commit.ts +19 -23
  133. package/src/commit/analysis/summary.ts +7 -5
  134. package/src/commit/changelog/generate.ts +15 -11
  135. package/src/commit/shared-llm.ts +17 -24
  136. package/src/config/config-file.ts +13 -15
  137. package/src/config/keybindings.ts +6 -0
  138. package/src/config/models-config-schema.ts +206 -179
  139. package/src/config/settings-schema.ts +118 -2
  140. package/src/discovery/builtin-rules/index.ts +2 -0
  141. package/src/discovery/builtin-rules/ts-import-type.md +2 -2
  142. package/src/discovery/builtin-rules/ts-no-any.md +11 -2
  143. package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +55 -0
  144. package/src/edit/hashline/params.ts +12 -11
  145. package/src/edit/index.ts +5 -4
  146. package/src/edit/modes/apply-patch.ts +4 -4
  147. package/src/edit/modes/patch.ts +15 -18
  148. package/src/edit/modes/replace.ts +13 -17
  149. package/src/edit/renderer.ts +0 -1
  150. package/src/eval/agent-bridge.ts +11 -13
  151. package/src/eval/completion-bridge.ts +25 -17
  152. package/src/eval/js/context-manager.ts +17 -2
  153. package/src/eval/js/index.ts +1 -1
  154. package/src/eval/py/executor.ts +2 -2
  155. package/src/eval/py/runner.py +44 -0
  156. package/src/extensibility/custom-commands/loader.ts +5 -3
  157. package/src/extensibility/custom-commands/types.ts +6 -3
  158. package/src/extensibility/custom-tools/loader.ts +4 -2
  159. package/src/extensibility/custom-tools/types.ts +8 -5
  160. package/src/extensibility/extensions/loader.ts +4 -2
  161. package/src/extensibility/extensions/runner.ts +20 -2
  162. package/src/extensibility/extensions/types.ts +22 -8
  163. package/src/extensibility/hooks/loader.ts +5 -2
  164. package/src/extensibility/hooks/types.ts +7 -4
  165. package/src/extensibility/legacy-pi-ai-shim.ts +42 -5
  166. package/src/extensibility/legacy-pi-coding-agent-shim.ts +113 -0
  167. package/src/extensibility/plugins/legacy-pi-compat.ts +13 -13
  168. package/src/extensibility/shared-events.ts +24 -0
  169. package/src/extensibility/tool-proxy.ts +4 -1
  170. package/src/extensibility/typebox.ts +778 -251
  171. package/src/goals/guided-setup.ts +12 -3
  172. package/src/goals/tools/goal-tool.ts +6 -6
  173. package/src/index.ts +2 -0
  174. package/src/internal-urls/docs-index.generated.ts +15 -13
  175. package/src/lsp/types.ts +13 -27
  176. package/src/main.ts +29 -21
  177. package/src/mcp/client.ts +38 -13
  178. package/src/mcp/render.ts +102 -89
  179. package/src/modes/components/agent-hub.ts +11 -4
  180. package/src/modes/components/branch-summary-message.ts +1 -0
  181. package/src/modes/components/btw-panel.ts +5 -1
  182. package/src/modes/components/collab-prompt-message.ts +9 -7
  183. package/src/modes/components/compaction-summary-message.ts +1 -0
  184. package/src/modes/components/custom-editor.ts +18 -0
  185. package/src/modes/components/custom-message.ts +1 -0
  186. package/src/modes/components/footer.ts +6 -5
  187. package/src/modes/components/hook-message.ts +1 -0
  188. package/src/modes/components/read-tool-group.ts +9 -3
  189. package/src/modes/components/skill-message.ts +1 -0
  190. package/src/modes/components/status-line/component.ts +139 -15
  191. package/src/modes/components/status-line/context-thresholds.ts +0 -1
  192. package/src/modes/components/todo-reminder.ts +1 -0
  193. package/src/modes/components/tool-execution.ts +17 -10
  194. package/src/modes/components/ttsr-notification.ts +1 -0
  195. package/src/modes/components/user-message.ts +6 -6
  196. package/src/modes/controllers/btw-controller.ts +69 -1
  197. package/src/modes/controllers/event-controller.ts +2 -7
  198. package/src/modes/controllers/input-controller.ts +29 -0
  199. package/src/modes/controllers/selector-controller.ts +10 -3
  200. package/src/modes/interactive-mode.ts +42 -10
  201. package/src/modes/rpc/rpc-types.ts +1 -1
  202. package/src/modes/setup-wizard/index.ts +1 -0
  203. package/src/modes/setup-wizard/scenes/sign-in.ts +77 -5
  204. package/src/modes/setup-wizard/startup-splash.ts +107 -0
  205. package/src/modes/theme/theme.ts +133 -143
  206. package/src/modes/types.ts +3 -0
  207. package/src/modes/utils/context-usage.ts +37 -20
  208. package/src/modes/utils/hotkeys-markdown.ts +1 -0
  209. package/src/prompts/system/system-prompt.md +1 -0
  210. package/src/prompts/tools/image-attachment-describe-system.md +8 -0
  211. package/src/prompts/tools/image-attachment-describe.md +10 -0
  212. package/src/sdk.ts +35 -22
  213. package/src/session/agent-session.ts +715 -255
  214. package/src/session/session-history-format.ts +11 -2
  215. package/src/session/session-loader.ts +19 -32
  216. package/src/session/session-persistence.ts +27 -11
  217. package/src/session/snapcompact-inline.ts +1 -1
  218. package/src/slash-commands/builtin-registry.ts +4 -11
  219. package/src/ssh/connection-manager.ts +3 -2
  220. package/src/startup-splash.ts +19 -0
  221. package/src/task/executor.ts +12 -7
  222. package/src/task/types.ts +44 -41
  223. package/src/tool-discovery/tool-index.ts +17 -4
  224. package/src/tools/ask.ts +14 -14
  225. package/src/tools/ast-edit.ts +17 -14
  226. package/src/tools/ast-grep.ts +10 -9
  227. package/src/tools/bash.ts +15 -10
  228. package/src/tools/browser/launch.ts +13 -0
  229. package/src/tools/browser.ts +26 -32
  230. package/src/tools/checkpoint.ts +7 -7
  231. package/src/tools/debug.ts +72 -69
  232. package/src/tools/eval.ts +18 -19
  233. package/src/tools/find.ts +20 -13
  234. package/src/tools/gh.ts +29 -49
  235. package/src/tools/image-gen.ts +94 -57
  236. package/src/tools/inspect-image.ts +8 -9
  237. package/src/tools/irc.ts +12 -12
  238. package/src/tools/job.ts +6 -6
  239. package/src/tools/learn.ts +11 -14
  240. package/src/tools/manage-skill.ts +19 -23
  241. package/src/tools/memory-edit.ts +8 -8
  242. package/src/tools/memory-recall.ts +4 -4
  243. package/src/tools/memory-reflect.ts +5 -5
  244. package/src/tools/memory-retain.ts +9 -11
  245. package/src/tools/puppeteer/02_stealth_hairline.txt +1 -1
  246. package/src/tools/puppeteer/04_stealth_iframe.txt +4 -4
  247. package/src/tools/puppeteer/05_stealth_webgl.txt +1 -1
  248. package/src/tools/puppeteer/10_stealth_plugins.txt +6 -4
  249. package/src/tools/puppeteer/12_stealth_codecs.txt +2 -2
  250. package/src/tools/puppeteer/13_stealth_worker.txt +1 -1
  251. package/src/tools/read.ts +197 -19
  252. package/src/tools/report-tool-issue.ts +6 -6
  253. package/src/tools/resolve.ts +6 -6
  254. package/src/tools/review.ts +10 -12
  255. package/src/tools/search-tool-bm25.ts +5 -5
  256. package/src/tools/search.ts +20 -29
  257. package/src/tools/ssh.ts +8 -8
  258. package/src/tools/todo.ts +16 -19
  259. package/src/tools/tts.ts +16 -15
  260. package/src/tools/write.ts +5 -5
  261. package/src/tui/code-cell.ts +44 -3
  262. package/src/tui/index.ts +1 -0
  263. package/src/tui/width-aware-text.ts +58 -0
  264. package/src/utils/image-vision-fallback.ts +197 -0
  265. package/src/utils/markit.ts +17 -2
  266. package/src/web/search/index.ts +21 -9
  267. package/src/web/search/providers/base.ts +1 -0
  268. package/src/web/search/providers/gemini.ts +56 -18
  269. package/src/web/search/providers/perplexity.ts +373 -126
  270. package/src/web/search/types.ts +28 -48
@@ -39,6 +39,7 @@ export const commands: CommandEntry[] = [
39
39
  { name: "usage", load: () => import("./commands/usage").then(m => m.default) },
40
40
  { name: "tiny-models", load: () => import("./commands/tiny-models").then(m => m.default) },
41
41
  { name: "token", load: () => import("./commands/token").then(m => m.default) },
42
+ { name: "ttsr", load: () => import("./commands/ttsr").then(m => m.default) },
42
43
  { name: "worktree", load: () => import("./commands/worktree").then(m => m.default), aliases: ["wt"] },
43
44
  { name: "search", load: () => import("./commands/web-search").then(m => m.default), aliases: ["q"] },
44
45
  ];
package/src/cli.ts CHANGED
@@ -275,7 +275,13 @@ export async function runCli(argv: string[]): Promise<void> {
275
275
  // Declare this module as the worker-host entry now that the active profile
276
276
  // is resolved. The worker-host module is side-effect-free; importing
277
277
  // `@oh-my-pi/pi-utils/env` here would snapshot the wrong agent `.env`.
278
- declareWorkerHostEntry();
278
+ // Gated on `import.meta.main`: only the real CLI process entry is a valid
279
+ // worker host. Worker-thread re-entry already returned above at the
280
+ // `__omp_worker_` dispatch, and importers (`runCli` in profile-CLI tests,
281
+ // SDK embedding) have `import.meta.main === false` — declaring there would
282
+ // poison `workerHostEntry()` for the whole test process, forcing eval/stats/
283
+ // browser workers onto the same-realm inline fallback.
284
+ if (import.meta.main) declareWorkerHostEntry();
279
285
 
280
286
  if (resolvedArgv[0] === "--smoke-test") {
281
287
  await runSmokeTest();
@@ -415,7 +415,7 @@ export class CollabHost {
415
415
  // render exactly the same anchored, provider-real count the host's own
416
416
  // status line shows.
417
417
  const breakdown = this.#ctx.statusLine.getCachedContextBreakdown();
418
- const tokens = breakdown.usedTokens;
418
+ const tokens = breakdown.usedTokens ?? 0;
419
419
  return {
420
420
  isStreaming: session.isStreaming,
421
421
  isAborting: session.isAborting,
@@ -427,7 +427,7 @@ export class CollabHost {
427
427
  contextUsage: {
428
428
  tokens,
429
429
  contextWindow: breakdown.contextWindow,
430
- percent: tokens !== null && breakdown.contextWindow > 0 ? (tokens / breakdown.contextWindow) * 100 : null,
430
+ percent: breakdown.contextWindow > 0 ? (tokens / breakdown.contextWindow) * 100 : 0,
431
431
  },
432
432
  participants: this.participants,
433
433
  };
@@ -136,6 +136,9 @@ export default class Index extends Command {
136
136
  "no-title": Flags.boolean({
137
137
  description: "Disable title auto-generation",
138
138
  }),
139
+ "max-time": Flags.string({
140
+ description: "Stop the session after this many seconds",
141
+ }),
139
142
  // `--auto-approve` / `--yolo`: declared here so oclif's auto-generated `--help` lists it.
140
143
  // Runtime parsing happens in `cli/args.ts parseArgs` (line 176 in that file) — `runRootCommand`
141
144
  // consumes the manual-parser output, not these oclif flag values. If you rename or remove
@@ -0,0 +1,125 @@
1
+ import { existsSync } from "node:fs";
2
+ import * as path from "node:path";
3
+ /**
4
+ * `omp ttsr` — inspect and test Time-Traveling Stream Rules.
5
+ *
6
+ * `omp ttsr test` feeds a snippet (inline, --file, or stdin) through the real
7
+ * TTSR matching pipeline and reports which rules would trigger. `omp ttsr list`
8
+ * shows every TTSR-registered rule the current project/user config would load.
9
+ */
10
+ import { Args, Command, Flags } from "@oh-my-pi/pi-utils/cli";
11
+ import {
12
+ runTtsrCommand,
13
+ TTSR_ACTIONS,
14
+ TTSR_SOURCES,
15
+ type TtsrCommandArgs,
16
+ type TtsrScanArgs,
17
+ type TtsrTestArgs,
18
+ } from "../cli/ttsr-cli";
19
+ import type { TtsrMatchSource } from "../export/ttsr";
20
+
21
+ export default class Ttsr extends Command {
22
+ static description = "Inspect and test Time-Traveling Stream Rules (TTSR)";
23
+
24
+ static args = {
25
+ action: Args.string({
26
+ description: "TTSR action",
27
+ required: false,
28
+ options: TTSR_ACTIONS,
29
+ }),
30
+ snippet: Args.string({
31
+ description: "Inline snippet text to test (ttsr test) or directory to scan (ttsr scan)",
32
+ required: false,
33
+ }),
34
+ };
35
+
36
+ static flags = {
37
+ file: Flags.string({ description: "Snippet file path, or - for stdin (ttsr test)" }),
38
+ rule: Flags.string({
39
+ char: "r",
40
+ description: "Rule markdown file to test in isolation (skips project rule loading)",
41
+ }),
42
+ source: Flags.string({
43
+ description: "Match source: text, thinking, or tool (inferred from --file when omitted)",
44
+ options: TTSR_SOURCES,
45
+ }),
46
+ tool: Flags.string({
47
+ description: "Tool name when source is tool (e.g. edit, write); defaults to edit",
48
+ }),
49
+ path: Flags.string({
50
+ char: "p",
51
+ description: "Candidate file path for scope/glob matching and AST language inference",
52
+ }),
53
+ verbose: Flags.boolean({ char: "v", description: "Show every evaluated rule, not just triggered ones" }),
54
+ json: Flags.boolean({ description: "Output JSON" }),
55
+ "no-gitignore": Flags.boolean({ description: "Include files excluded by .gitignore (ttsr scan)" }),
56
+ "max-bytes": Flags.integer({
57
+ description: "Maximum file size to scan in bytes; 0 disables the limit (ttsr scan)",
58
+ }),
59
+ };
60
+
61
+ static examples = [
62
+ "omp ttsr list",
63
+ "omp ttsr test 'const x: any = 1'",
64
+ "omp ttsr test src/foo.ts",
65
+ "omp ttsr test --file src/foo.ts",
66
+ "omp ttsr test --file src/foo.ts --source text",
67
+ "omp ttsr test --rule .omp/rules/no-any.md --source tool --path src/foo.ts 'const x: any = 1'",
68
+ "echo 'Box::leak(&mut v)' | omp ttsr test --file - --path src/lib.rs",
69
+ "omp ttsr test --source tool --tool edit --path src/foo.ts 'const x: any = 1'",
70
+ "omp ttsr scan",
71
+ "omp ttsr scan src/",
72
+ "omp ttsr scan -r .omp/rules/no-any.md src/",
73
+ ];
74
+
75
+ async run(): Promise<void> {
76
+ const { args, flags } = await this.parse(Ttsr);
77
+ const action = (args.action ?? "list") as (typeof TTSR_ACTIONS)[number];
78
+
79
+ // A positional that resolves to an existing file is a snippet file, not
80
+ // inline text — so `omp ttsr test src/foo.ts` works without --file.
81
+ // --file always wins over the positional.
82
+ let file = flags.file;
83
+ let snippet = args.snippet;
84
+ if (action === "test" && snippet && !file) {
85
+ const resolved = path.resolve(snippet);
86
+ if (existsSync(resolved)) {
87
+ file = resolved;
88
+ snippet = undefined;
89
+ }
90
+ }
91
+
92
+ const test: TtsrTestArgs | undefined =
93
+ action === "test"
94
+ ? {
95
+ snippet,
96
+ file,
97
+ rule: flags.rule,
98
+ source: flags.source as TtsrMatchSource | undefined,
99
+ tool: flags.tool,
100
+ filePath: flags.path,
101
+ verbose: flags.verbose,
102
+ }
103
+ : undefined;
104
+
105
+ const scan: TtsrScanArgs | undefined =
106
+ action === "scan"
107
+ ? {
108
+ directory: args.snippet,
109
+ rule: flags.rule,
110
+ gitignore: !flags["no-gitignore"],
111
+ maxBytes: flags["max-bytes"],
112
+ verbose: flags.verbose,
113
+ }
114
+ : undefined;
115
+
116
+ const cmd: TtsrCommandArgs = {
117
+ action,
118
+ test,
119
+ scan,
120
+ json: flags.json,
121
+ };
122
+
123
+ await runTtsrCommand(cmd);
124
+ }
125
+ }
@@ -1,5 +1,5 @@
1
1
  import { prompt } from "@oh-my-pi/pi-utils";
2
- import { z } from "zod/v4";
2
+ import { type } from "arktype";
3
3
  import analyzeFilePrompt from "../../../commit/agentic/prompts/analyze-file.md" with { type: "text" };
4
4
  import type { CommitAgentState } from "../../../commit/agentic/state";
5
5
  import type { NumstatEntry } from "../../../commit/types";
@@ -12,9 +12,9 @@ import type { TaskParams } from "../../../task/types";
12
12
  import type { ToolSession } from "../../../tools";
13
13
  import { getFilePriority } from "./git-file-diff";
14
14
 
15
- const analyzeFileSchema = z.object({
16
- files: z.array(z.string().describe("file path")).min(1),
17
- goal: z.string().describe("analysis focus").optional(),
15
+ const analyzeFileSchema = type({
16
+ files: type("string").describe("file path").array().atLeastLength(1),
17
+ "goal?": type("string").describe("analysis focus"),
18
18
  });
19
19
 
20
20
  const analyzeFileOutputSchema = {
@@ -1,4 +1,4 @@
1
- import { z } from "zod/v4";
1
+ import { type } from "arktype";
2
2
  import type { CommitAgentState } from "../../../commit/agentic/state";
3
3
  import type { CustomTool } from "../../../extensibility/custom-tools/types";
4
4
  import * as git from "../../../utils/git";
@@ -131,9 +131,9 @@ function processDiffs(files: string[], diffs: Map<string, string>): { result: st
131
131
  return { result: parts.join("\n\n"), truncatedFiles };
132
132
  }
133
133
 
134
- const gitFileDiffSchema = z.object({
135
- files: z.array(z.string().describe("file to diff")).min(1).max(10),
136
- staged: z.boolean().describe("use staged changes (default true)").optional(),
134
+ const gitFileDiffSchema = type({
135
+ files: type("string").describe("file to diff").array().atLeastLength(1).atMostLength(10),
136
+ "staged?": type("boolean").describe("use staged changes (default true)"),
137
137
  });
138
138
 
139
139
  export function createGitFileDiffTool(cwd: string, state: CommitAgentState): CustomTool<typeof gitFileDiffSchema> {
@@ -1,12 +1,14 @@
1
- import { z } from "zod/v4";
1
+ import { type } from "arktype";
2
2
  import type { DiffHunk, FileHunks } from "../../../commit/types";
3
3
  import type { CustomTool } from "../../../extensibility/custom-tools/types";
4
4
  import * as git from "../../../utils/git";
5
5
 
6
- const gitHunkSchema = z.object({
7
- file: z.string().describe("file path"),
8
- hunks: z.array(z.number().describe("1-based hunk index")).min(1).optional(),
9
- staged: z.boolean().describe("use staged changes (default true)").optional(),
6
+ const hunkIndexType = type("number").describe("1-based hunk index");
7
+
8
+ const gitHunkSchema = type({
9
+ file: type("string").describe("file path"),
10
+ "hunks?": hunkIndexType.array().atLeastLength(1),
11
+ "staged?": type("boolean").describe("use staged changes (default true)"),
10
12
  });
11
13
 
12
14
  function selectHunks(fileHunks: FileHunks, requested?: number[]): DiffHunk[] {
@@ -1,4 +1,4 @@
1
- import { z } from "zod/v4";
1
+ import { type } from "arktype";
2
2
  import type { CommitAgentState, GitOverviewSnapshot } from "../../../commit/agentic/state";
3
3
  import { extractScopeCandidates } from "../../../commit/analysis/scope";
4
4
  import type { CustomTool } from "../../../extensibility/custom-tools/types";
@@ -42,9 +42,9 @@ function filterExcludedFiles(files: string[]): { filtered: string[]; excluded: s
42
42
  return { filtered, excluded };
43
43
  }
44
44
 
45
- const gitOverviewSchema = z.object({
46
- staged: z.boolean().describe("use staged changes (default true)").optional(),
47
- include_untracked: z.boolean().describe("include untracked when unstaged").optional(),
45
+ const gitOverviewSchema = type({
46
+ "staged?": type("boolean").describe("use staged changes (default true)"),
47
+ "include_untracked?": type("boolean").describe("include untracked when unstaged"),
48
48
  });
49
49
 
50
50
  export function createGitOverviewTool(cwd: string, state: CommitAgentState): CustomTool<typeof gitOverviewSchema> {
@@ -1,27 +1,30 @@
1
- import { z } from "zod/v4";
1
+ import { type } from "arktype";
2
2
  import type { CommitAgentState } from "../../../commit/agentic/state";
3
3
  import { CHANGELOG_CATEGORIES, type ChangelogCategory } from "../../../commit/types";
4
4
  import type { CustomTool } from "../../../extensibility/custom-tools/types";
5
5
 
6
- const changelogEntryProperties = CHANGELOG_CATEGORIES.reduce<Record<ChangelogCategory, z.ZodType>>(
7
- (acc, category) => {
8
- acc[category] = z.array(z.string()).optional();
9
- return acc;
10
- },
11
- {} as Record<ChangelogCategory, z.ZodType>,
12
- );
6
+ const changelogCategoryProperties = {
7
+ "Breaking Changes?": "string[]",
8
+ "Added?": "string[]",
9
+ "Changed?": "string[]",
10
+ "Deprecated?": "string[]",
11
+ "Removed?": "string[]",
12
+ "Fixed?": "string[]",
13
+ "Security?": "string[]",
14
+ } as const;
13
15
 
14
- const changelogEntriesSchema = z.object(changelogEntryProperties);
15
- const changelogDeletionsSchema = z.object(changelogEntryProperties).describe("entries to remove");
16
+ const changelogEntriesSchema = type({
17
+ ...changelogCategoryProperties,
18
+ });
16
19
 
17
- const changelogEntrySchema = z.object({
18
- path: z.string(),
20
+ const changelogEntrySchema = type({
21
+ path: "string",
19
22
  entries: changelogEntriesSchema,
20
- deletions: changelogDeletionsSchema.optional(),
23
+ "deletions?": changelogEntriesSchema.describe("entries to remove"),
21
24
  });
22
25
 
23
- const proposeChangelogSchema = z.object({
24
- entries: z.array(changelogEntrySchema),
26
+ const proposeChangelogSchema = type({
27
+ entries: changelogEntrySchema.array(),
25
28
  });
26
29
 
27
30
  interface ChangelogResponse {
@@ -1,4 +1,4 @@
1
- import { z } from "zod/v4";
1
+ import { type } from "arktype";
2
2
  import type { CommitAgentState } from "../../../commit/agentic/state";
3
3
  import {
4
4
  capDetails,
@@ -15,12 +15,12 @@ import type { CustomTool } from "../../../extensibility/custom-tools/types";
15
15
  import * as git from "../../../utils/git";
16
16
  import { commitTypeSchema, detailSchema } from "./schemas.js";
17
17
 
18
- const proposeCommitSchema = z.object({
18
+ const proposeCommitSchema = type({
19
19
  type: commitTypeSchema,
20
- scope: z.union([z.string(), z.null()]),
21
- summary: z.string(),
22
- details: z.array(detailSchema),
23
- issue_refs: z.array(z.string()),
20
+ scope: type("string").or("null"),
21
+ summary: "string",
22
+ details: detailSchema.array(),
23
+ issue_refs: "string[]",
24
24
  });
25
25
 
26
26
  interface ProposalResponse {
@@ -1,9 +1,9 @@
1
- import { z } from "zod/v4";
1
+ import { type } from "arktype";
2
2
  import type { CustomTool } from "../../../extensibility/custom-tools/types";
3
3
  import * as git from "../../../utils/git";
4
4
 
5
- const recentCommitsSchema = z.object({
6
- count: z.number().min(1).max(50).describe("commit count").optional(),
5
+ const recentCommitsSchema = type({
6
+ "count?": type("1 <= number <= 50").describe("commit count"),
7
7
  });
8
8
 
9
9
  interface RecentCommitStats {
@@ -1,23 +1,11 @@
1
- import { z } from "zod/v4";
1
+ import { type } from "arktype";
2
2
 
3
- export const commitTypeSchema = z.enum([
4
- "feat",
5
- "fix",
6
- "refactor",
7
- "perf",
8
- "docs",
9
- "test",
10
- "build",
11
- "ci",
12
- "chore",
13
- "style",
14
- "revert",
15
- ] as const);
3
+ export const commitTypeSchema = type(
4
+ "'feat' | 'fix' | 'refactor' | 'perf' | 'docs' | 'test' | 'build' | 'ci' | 'chore' | 'style' | 'revert'",
5
+ );
16
6
 
17
- export const detailSchema = z.object({
18
- text: z.string(),
19
- changelog_category: z
20
- .enum(["Added", "Changed", "Fixed", "Deprecated", "Removed", "Security", "Breaking Changes"])
21
- .optional(),
22
- user_visible: z.boolean().optional(),
7
+ export const detailSchema = type({
8
+ text: "string",
9
+ "changelog_category?": "'Added' | 'Changed' | 'Fixed' | 'Deprecated' | 'Removed' | 'Security' | 'Breaking Changes'",
10
+ "user_visible?": "boolean",
23
11
  });
@@ -1,4 +1,4 @@
1
- import { z } from "zod/v4";
1
+ import { type } from "arktype";
2
2
  import type { CommitAgentState, SplitCommitGroup, SplitCommitPlan } from "../../../commit/agentic/state";
3
3
  import { computeDependencyOrder } from "../../../commit/agentic/topo-sort";
4
4
  import {
@@ -15,32 +15,28 @@ import type { CustomTool } from "../../../extensibility/custom-tools/types";
15
15
  import * as git from "../../../utils/git";
16
16
  import { commitTypeSchema, detailSchema } from "./schemas.js";
17
17
 
18
- const hunkSelectorSchema = z.discriminatedUnion("type", [
19
- z.object({ type: z.literal("all") }),
20
- z.object({ type: z.literal("indices"), indices: z.array(z.number()).min(1) }),
21
- z.object({ type: z.literal("lines"), start: z.number(), end: z.number() }),
22
- ]);
18
+ const hunkSelectorSchema = type({ type: "'all'" })
19
+ .or({ type: "'indices'", indices: "number[]" })
20
+ .or({ type: "'lines'", start: "number", end: "number" });
23
21
 
24
- const fileChangeSchema = z.object({
25
- path: z.string(),
22
+ const fileChangeSchema = type({
23
+ path: "string",
26
24
  hunks: hunkSelectorSchema,
27
25
  });
28
26
 
29
- const splitCommitSchema = z.object({
30
- commits: z
31
- .array(
32
- z.object({
33
- changes: z.array(fileChangeSchema).min(1),
34
- type: commitTypeSchema,
35
- scope: z.union([z.string(), z.null()]),
36
- summary: z.string(),
37
- details: z.array(detailSchema).optional(),
38
- issue_refs: z.array(z.string()).optional(),
39
- rationale: z.string().optional(),
40
- dependencies: z.array(z.number()).optional(),
41
- }),
42
- )
43
- .min(2),
27
+ const commitItemSchema = type({
28
+ changes: fileChangeSchema.array(),
29
+ type: commitTypeSchema,
30
+ scope: type("string").or("null"),
31
+ summary: "string",
32
+ "details?": detailSchema.array(),
33
+ "issue_refs?": "string[]",
34
+ "rationale?": "string",
35
+ "dependencies?": "number[]",
36
+ });
37
+
38
+ const splitCommitSchema = type({
39
+ commits: commitItemSchema.array(),
44
40
  });
45
41
 
46
42
  interface SplitCommitResponse {
@@ -2,19 +2,21 @@ import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
2
2
  import type { Api, ApiKey, AssistantMessage, Model } from "@oh-my-pi/pi-ai";
3
3
  import { completeSimple, validateToolCall } from "@oh-my-pi/pi-ai";
4
4
  import { prompt } from "@oh-my-pi/pi-utils";
5
- import { z } from "zod/v4";
5
+ import { type } from "arktype";
6
6
  import summarySystemPrompt from "../../commit/prompts/summary-system.md" with { type: "text" };
7
7
  import summaryUserPrompt from "../../commit/prompts/summary-user.md" with { type: "text" };
8
8
  import type { CommitSummary } from "../../commit/types";
9
9
  import { toReasoningEffort } from "../../thinking";
10
10
  import { extractTextContent, extractToolCall } from "../utils";
11
11
 
12
+ const SummaryToolSchema = type({
13
+ summary: "string",
14
+ });
15
+
12
16
  const SummaryTool = {
13
17
  name: "create_commit_summary",
14
18
  description: "Generate the summary line for a conventional commit message.",
15
- parameters: z.object({
16
- summary: z.string(),
17
- }),
19
+ parameters: SummaryToolSchema,
18
20
  };
19
21
 
20
22
  export interface SummaryInput {
@@ -83,7 +85,7 @@ function renderSummaryPrompt({
83
85
  function parseSummaryFromResponse(message: AssistantMessage, commitType: string, scope: string | null): CommitSummary {
84
86
  const toolCall = extractToolCall(message, "create_commit_summary");
85
87
  if (toolCall) {
86
- const parsed = validateToolCall([SummaryTool], toolCall) as z.infer<(typeof SummaryTool)["parameters"]>;
88
+ const parsed = validateToolCall([SummaryTool], toolCall) as (typeof SummaryToolSchema)["infer"];
87
89
  return { summary: stripTypePrefix(parsed.summary, commitType, scope) };
88
90
  }
89
91
  const text = extractTextContent(message);
@@ -2,25 +2,29 @@ import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
2
2
  import type { Api, ApiKey, AssistantMessage, Model } from "@oh-my-pi/pi-ai";
3
3
  import { completeSimple, validateToolCall } from "@oh-my-pi/pi-ai";
4
4
  import { prompt } from "@oh-my-pi/pi-utils";
5
- import { z } from "zod/v4";
5
+ import { type } from "arktype";
6
6
  import changelogSystemPrompt from "../../commit/prompts/changelog-system.md" with { type: "text" };
7
7
  import changelogUserPrompt from "../../commit/prompts/changelog-user.md" with { type: "text" };
8
- import { CHANGELOG_CATEGORIES, type ChangelogCategory, type ChangelogGenerationResult } from "../../commit/types";
8
+ import type { ChangelogGenerationResult } from "../../commit/types";
9
9
  import { toReasoningEffort } from "../../thinking";
10
10
  import { extractTextContent, extractToolCall, parseJsonPayload } from "../utils";
11
11
 
12
- const changelogEntryShape = Object.fromEntries(
13
- CHANGELOG_CATEGORIES.map(c => [c, z.array(z.string()).optional()] as const),
14
- ) as Record<ChangelogCategory, z.ZodOptional<z.ZodArray<z.ZodString>>>;
15
-
16
- const changelogEntriesSchema = z.object(changelogEntryShape);
12
+ // Build the changelog entry schema with arktype
13
+ // Each category maps to an optional array of strings
14
+ const changelogEntriesSchema = type({
15
+ "Breaking Changes?": "string[]",
16
+ "Added?": "string[]",
17
+ "Changed?": "string[]",
18
+ "Deprecated?": "string[]",
19
+ "Removed?": "string[]",
20
+ "Fixed?": "string[]",
21
+ "Security?": "string[]",
22
+ });
17
23
 
18
24
  export const changelogTool = {
19
25
  name: "create_changelog_entries",
20
26
  description: "Generate changelog entries grouped by Keep a Changelog categories.",
21
- parameters: z.object({
22
- entries: changelogEntriesSchema,
23
- }),
27
+ parameters: type({ entries: changelogEntriesSchema }),
24
28
  };
25
29
 
26
30
  export interface ChangelogPromptInput {
@@ -68,7 +72,7 @@ export async function generateChangelogEntries({
68
72
  function parseChangelogResponse(message: AssistantMessage): ChangelogGenerationResult {
69
73
  const toolCall = extractToolCall(message, "create_changelog_entries");
70
74
  if (toolCall) {
71
- const parsed = validateToolCall([changelogTool], toolCall) as z.infer<(typeof changelogTool)["parameters"]>;
75
+ const parsed = validateToolCall([changelogTool], toolCall) as typeof changelogTool.parameters.infer;
72
76
  return { entries: parsed.entries ?? {} };
73
77
  }
74
78
 
@@ -1,36 +1,29 @@
1
1
  import type { AssistantMessage } from "@oh-my-pi/pi-ai";
2
- import { validateToolCall } from "@oh-my-pi/pi-ai";
3
- import { z } from "zod/v4";
2
+ import { type as t, validateToolCall } from "@oh-my-pi/pi-ai";
4
3
  import type { ChangelogCategory, ConventionalAnalysis } from "./types";
5
4
  import { extractTextContent, extractToolCall, normalizeAnalysis, parseJsonPayload } from "./utils";
6
5
 
7
- const changelogCategoryLiteral = z.enum([
8
- "Added",
9
- "Changed",
10
- "Fixed",
11
- "Deprecated",
12
- "Removed",
13
- "Security",
14
- "Breaking Changes",
15
- ]);
6
+ const changelogCategoryLiteral = t(
7
+ "'Added' | 'Changed' | 'Fixed' | 'Deprecated' | 'Removed' | 'Security' | 'Breaking Changes'",
8
+ );
16
9
 
17
10
  /**
18
- * Shared Zod schema for the `create_conventional_analysis` tool used by
11
+ * Shared arktype schema for the `create_conventional_analysis` tool used by
19
12
  * both the single-pass analysis call and the map-reduce reduce phase. Schemas
20
13
  * are identical across phases — only the surrounding tool `description`
21
14
  * differs to reflect the input the phase is summarizing.
22
15
  */
23
- export const conventionalAnalysisParameters = z.object({
24
- type: z.enum(["feat", "fix", "refactor", "docs", "test", "chore", "style", "perf", "build", "ci", "revert"]),
25
- scope: z.union([z.string(), z.null()]),
26
- details: z.array(
27
- z.object({
28
- text: z.string(),
29
- changelog_category: changelogCategoryLiteral.optional(),
30
- user_visible: z.boolean().optional(),
31
- }),
32
- ),
33
- issue_refs: z.array(z.string()),
16
+ const detailItem = t({
17
+ text: "string",
18
+ "changelog_category?": changelogCategoryLiteral,
19
+ "user_visible?": "boolean",
20
+ });
21
+
22
+ export const conventionalAnalysisParameters = t({
23
+ type: "'feat' | 'fix' | 'refactor' | 'docs' | 'test' | 'chore' | 'style' | 'perf' | 'build' | 'ci' | 'revert'",
24
+ scope: t("string").or("null"),
25
+ details: detailItem.array(),
26
+ issue_refs: "string[]",
34
27
  });
35
28
 
36
29
  export interface ConventionalAnalysisTool {
@@ -68,7 +61,7 @@ export function parseConventionalAnalysisResponse(
68
61
  ): ConventionalAnalysis {
69
62
  const toolCall = extractToolCall(message, tool.name);
70
63
  if (toolCall) {
71
- const parsed = validateToolCall([tool], toolCall) as z.infer<typeof conventionalAnalysisParameters>;
64
+ const parsed = validateToolCall([tool], toolCall) as any;
72
65
  return normalizeAnalysis(parsed);
73
66
  }
74
67
  const text = extractTextContent(message);