@oh-my-pi/pi-coding-agent 8.1.0 → 8.2.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 (402) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/docs/session.md +111 -46
  3. package/examples/custom-tools/hello/index.ts +1 -1
  4. package/examples/custom-tools/todo/index.ts +3 -4
  5. package/examples/extensions/api-demo.ts +0 -1
  6. package/examples/extensions/chalk-logger.ts +2 -3
  7. package/examples/extensions/hello.ts +0 -1
  8. package/examples/extensions/pirate.ts +0 -1
  9. package/examples/extensions/plan-mode.ts +15 -16
  10. package/examples/extensions/todo.ts +3 -4
  11. package/examples/extensions/tools.ts +1 -2
  12. package/examples/extensions/with-deps/index.ts +0 -1
  13. package/examples/hooks/auto-commit-on-exit.ts +1 -2
  14. package/examples/hooks/confirm-destructive.ts +0 -1
  15. package/examples/hooks/custom-compaction.ts +1 -2
  16. package/examples/hooks/dirty-repo-guard.ts +0 -1
  17. package/examples/hooks/file-trigger.ts +3 -4
  18. package/examples/hooks/git-checkpoint.ts +0 -1
  19. package/examples/hooks/handoff.ts +3 -4
  20. package/examples/hooks/permission-gate.ts +1 -2
  21. package/examples/hooks/protected-paths.ts +1 -2
  22. package/examples/hooks/qna.ts +2 -3
  23. package/examples/hooks/snake.ts +4 -5
  24. package/examples/hooks/status-line.ts +0 -1
  25. package/examples/sdk/01-minimal.ts +2 -3
  26. package/examples/sdk/02-custom-model.ts +2 -3
  27. package/examples/sdk/03-custom-prompt.ts +3 -4
  28. package/examples/sdk/04-skills.ts +2 -3
  29. package/examples/sdk/06-extensions.ts +1 -2
  30. package/examples/sdk/06-hooks.ts +6 -7
  31. package/examples/sdk/07-context-files.ts +0 -1
  32. package/examples/sdk/08-prompt-templates.ts +0 -1
  33. package/examples/sdk/08-slash-commands.ts +0 -1
  34. package/examples/sdk/09-api-keys-and-oauth.ts +0 -1
  35. package/examples/sdk/10-settings.ts +0 -1
  36. package/examples/sdk/11-sessions.ts +0 -1
  37. package/package.json +51 -23
  38. package/scripts/format-prompts.ts +0 -1
  39. package/src/capability/context-file.ts +2 -3
  40. package/src/capability/extension-module.ts +2 -3
  41. package/src/capability/extension.ts +2 -3
  42. package/src/capability/fs.ts +20 -21
  43. package/src/capability/hook.ts +2 -3
  44. package/src/capability/index.ts +15 -16
  45. package/src/capability/instruction.ts +2 -3
  46. package/src/capability/mcp.ts +2 -3
  47. package/src/capability/prompt.ts +2 -3
  48. package/src/capability/rule.ts +2 -3
  49. package/src/capability/settings.ts +1 -2
  50. package/src/capability/skill.ts +2 -3
  51. package/src/capability/slash-command.ts +2 -3
  52. package/src/capability/ssh.ts +2 -3
  53. package/src/capability/system-prompt.ts +2 -3
  54. package/src/capability/tool.ts +2 -3
  55. package/src/cli/args.ts +5 -6
  56. package/src/cli/config-cli.ts +6 -7
  57. package/src/cli/file-processor.ts +19 -17
  58. package/src/cli/jupyter-cli.ts +105 -0
  59. package/src/cli/list-models.ts +10 -11
  60. package/src/cli/plugin-cli.ts +20 -21
  61. package/src/cli/session-picker.ts +2 -3
  62. package/src/cli/setup-cli.ts +2 -3
  63. package/src/cli/stats-cli.ts +2 -3
  64. package/src/cli/update-cli.ts +25 -22
  65. package/src/commit/agentic/agent.ts +21 -23
  66. package/src/commit/agentic/fallback.ts +9 -9
  67. package/src/commit/agentic/index.ts +30 -38
  68. package/src/commit/agentic/state.ts +1 -6
  69. package/src/commit/agentic/tools/analyze-file.ts +15 -15
  70. package/src/commit/agentic/tools/git-file-diff.ts +3 -3
  71. package/src/commit/agentic/tools/git-hunk.ts +7 -7
  72. package/src/commit/agentic/tools/git-overview.ts +5 -5
  73. package/src/commit/agentic/tools/index.ts +14 -14
  74. package/src/commit/agentic/tools/propose-changelog.ts +6 -6
  75. package/src/commit/agentic/tools/propose-commit.ts +8 -8
  76. package/src/commit/agentic/tools/recent-commits.ts +2 -2
  77. package/src/commit/agentic/tools/split-commit.ts +19 -23
  78. package/src/commit/agentic/topo-sort.ts +1 -1
  79. package/src/commit/agentic/trivial.ts +3 -3
  80. package/src/commit/agentic/validation.ts +12 -12
  81. package/src/commit/analysis/conventional.ts +7 -11
  82. package/src/commit/analysis/index.ts +4 -4
  83. package/src/commit/analysis/scope.ts +4 -4
  84. package/src/commit/analysis/summary.ts +7 -9
  85. package/src/commit/analysis/validation.ts +1 -1
  86. package/src/commit/changelog/detect.ts +6 -6
  87. package/src/commit/changelog/generate.ts +7 -9
  88. package/src/commit/changelog/index.ts +13 -13
  89. package/src/commit/changelog/parse.ts +2 -2
  90. package/src/commit/cli.ts +1 -1
  91. package/src/commit/git/diff.ts +3 -3
  92. package/src/commit/git/index.ts +19 -24
  93. package/src/commit/index.ts +1 -1
  94. package/src/commit/map-reduce/index.ts +9 -9
  95. package/src/commit/map-reduce/map-phase.ts +19 -34
  96. package/src/commit/map-reduce/reduce-phase.ts +9 -11
  97. package/src/commit/message.ts +2 -2
  98. package/src/commit/model-selection.ts +3 -7
  99. package/src/commit/pipeline.ts +20 -22
  100. package/src/commit/utils/exclusions.ts +3 -3
  101. package/src/config/file-lock.ts +17 -7
  102. package/src/config/keybindings.ts +6 -8
  103. package/src/config/model-registry.ts +55 -37
  104. package/src/config/model-resolver.ts +18 -19
  105. package/src/config/prompt-templates.ts +11 -11
  106. package/src/config/settings-manager.ts +50 -34
  107. package/src/config.ts +60 -62
  108. package/src/cursor.ts +11 -9
  109. package/src/discovery/agents-md.ts +11 -12
  110. package/src/discovery/builtin.ts +68 -73
  111. package/src/discovery/claude.ts +41 -42
  112. package/src/discovery/cline.ts +11 -12
  113. package/src/discovery/codex.ts +52 -53
  114. package/src/discovery/cursor.ts +9 -10
  115. package/src/discovery/gemini.ts +17 -22
  116. package/src/discovery/github.ts +13 -14
  117. package/src/discovery/helpers.ts +35 -34
  118. package/src/discovery/index.ts +16 -18
  119. package/src/discovery/mcp-json.ts +8 -9
  120. package/src/discovery/ssh.ts +8 -9
  121. package/src/discovery/vscode.ts +4 -5
  122. package/src/discovery/windsurf.ts +6 -7
  123. package/src/exa/company.ts +1 -2
  124. package/src/exa/index.ts +2 -3
  125. package/src/exa/linkedin.ts +1 -2
  126. package/src/exa/mcp-client.ts +14 -16
  127. package/src/exa/render.ts +10 -11
  128. package/src/exa/researcher.ts +1 -2
  129. package/src/exa/search.ts +1 -2
  130. package/src/exa/types.ts +0 -1
  131. package/src/exa/websets.ts +1 -2
  132. package/src/exec/bash-executor.ts +3 -4
  133. package/src/exec/exec.ts +0 -1
  134. package/src/export/custom-share.ts +5 -6
  135. package/src/export/html/index.ts +24 -21
  136. package/src/export/ttsr.ts +2 -3
  137. package/src/extensibility/custom-commands/bundled/review/index.ts +7 -8
  138. package/src/extensibility/custom-commands/loader.ts +17 -14
  139. package/src/extensibility/custom-commands/types.ts +1 -2
  140. package/src/extensibility/custom-tools/loader.ts +10 -11
  141. package/src/extensibility/custom-tools/types.ts +6 -7
  142. package/src/extensibility/custom-tools/wrapper.ts +2 -3
  143. package/src/extensibility/extensions/loader.ts +75 -53
  144. package/src/extensibility/extensions/runner.ts +11 -12
  145. package/src/extensibility/extensions/types.ts +19 -26
  146. package/src/extensibility/extensions/wrapper.ts +3 -4
  147. package/src/extensibility/hooks/index.ts +1 -1
  148. package/src/extensibility/hooks/loader.ts +8 -9
  149. package/src/extensibility/hooks/runner.ts +7 -8
  150. package/src/extensibility/hooks/tool-wrapper.ts +0 -1
  151. package/src/extensibility/hooks/types.ts +10 -17
  152. package/src/extensibility/plugins/doctor.ts +3 -3
  153. package/src/extensibility/plugins/installer.ts +27 -27
  154. package/src/extensibility/plugins/loader.ts +59 -56
  155. package/src/extensibility/plugins/manager.ts +211 -171
  156. package/src/extensibility/plugins/parser.ts +1 -1
  157. package/src/extensibility/plugins/paths.ts +8 -8
  158. package/src/extensibility/skills.ts +63 -60
  159. package/src/extensibility/slash-commands.ts +10 -10
  160. package/src/index.ts +46 -46
  161. package/src/internal-urls/agent-protocol.ts +21 -11
  162. package/src/internal-urls/artifact-protocol.ts +17 -13
  163. package/src/internal-urls/router.ts +1 -2
  164. package/src/internal-urls/rule-protocol.ts +3 -4
  165. package/src/internal-urls/skill-protocol.ts +3 -4
  166. package/src/ipy/executor.ts +14 -10
  167. package/src/ipy/gateway-coordinator.ts +79 -90
  168. package/src/ipy/kernel.ts +32 -30
  169. package/src/ipy/modules.ts +13 -13
  170. package/src/lsp/client.ts +21 -10
  171. package/src/lsp/clients/biome-client.ts +1 -2
  172. package/src/lsp/clients/index.ts +3 -3
  173. package/src/lsp/clients/lsp-linter-client.ts +4 -5
  174. package/src/lsp/config.ts +15 -15
  175. package/src/lsp/edits.ts +4 -5
  176. package/src/lsp/index.ts +43 -44
  177. package/src/lsp/lspmux.ts +8 -8
  178. package/src/lsp/render.ts +10 -16
  179. package/src/lsp/utils.ts +3 -3
  180. package/src/main.ts +55 -34
  181. package/src/mcp/client.ts +2 -3
  182. package/src/mcp/config.ts +5 -6
  183. package/src/mcp/json-rpc.ts +0 -1
  184. package/src/mcp/loader.ts +3 -4
  185. package/src/mcp/manager.ts +17 -18
  186. package/src/mcp/tool-bridge.ts +4 -9
  187. package/src/mcp/tool-cache.ts +2 -3
  188. package/src/mcp/transports/http.ts +2 -4
  189. package/src/mcp/transports/stdio.ts +1 -2
  190. package/src/migrations.ts +60 -49
  191. package/src/modes/components/armin.ts +4 -5
  192. package/src/modes/components/assistant-message.ts +6 -6
  193. package/src/modes/components/bash-execution.ts +7 -8
  194. package/src/modes/components/bordered-loader.ts +3 -3
  195. package/src/modes/components/branch-summary-message.ts +3 -3
  196. package/src/modes/components/compaction-summary-message.ts +3 -3
  197. package/src/modes/components/countdown-timer.ts +0 -1
  198. package/src/modes/components/custom-message.ts +5 -5
  199. package/src/modes/components/diff.ts +1 -1
  200. package/src/modes/components/dynamic-border.ts +2 -2
  201. package/src/modes/components/extensions/extension-dashboard.ts +6 -7
  202. package/src/modes/components/extensions/extension-list.ts +2 -3
  203. package/src/modes/components/extensions/inspector-panel.ts +3 -4
  204. package/src/modes/components/extensions/state-manager.ts +25 -26
  205. package/src/modes/components/extensions/types.ts +1 -2
  206. package/src/modes/components/footer.ts +47 -43
  207. package/src/modes/components/history-search.ts +2 -2
  208. package/src/modes/components/hook-editor.ts +3 -4
  209. package/src/modes/components/hook-input.ts +2 -3
  210. package/src/modes/components/hook-message.ts +5 -5
  211. package/src/modes/components/hook-selector.ts +2 -3
  212. package/src/modes/components/keybinding-hints.ts +2 -3
  213. package/src/modes/components/login-dialog.ts +2 -2
  214. package/src/modes/components/model-selector.ts +12 -12
  215. package/src/modes/components/oauth-selector.ts +2 -2
  216. package/src/modes/components/plugin-settings.ts +20 -20
  217. package/src/modes/components/python-execution.ts +7 -8
  218. package/src/modes/components/queue-mode-selector.ts +3 -3
  219. package/src/modes/components/read-tool-group.ts +2 -2
  220. package/src/modes/components/session-selector.ts +4 -4
  221. package/src/modes/components/settings-defs.ts +77 -69
  222. package/src/modes/components/settings-selector.ts +16 -16
  223. package/src/modes/components/show-images-selector.ts +2 -2
  224. package/src/modes/components/status-line/segments.ts +4 -4
  225. package/src/modes/components/status-line/separators.ts +1 -1
  226. package/src/modes/components/status-line/types.ts +2 -2
  227. package/src/modes/components/status-line-segment-editor.ts +7 -8
  228. package/src/modes/components/status-line.ts +12 -12
  229. package/src/modes/components/theme-selector.ts +8 -7
  230. package/src/modes/components/thinking-selector.ts +4 -4
  231. package/src/modes/components/todo-display.ts +2 -2
  232. package/src/modes/components/todo-reminder.ts +4 -4
  233. package/src/modes/components/tool-execution.ts +11 -16
  234. package/src/modes/components/tree-selector.ts +11 -11
  235. package/src/modes/components/ttsr-notification.ts +5 -5
  236. package/src/modes/components/user-message-selector.ts +1 -1
  237. package/src/modes/components/user-message.ts +1 -1
  238. package/src/modes/components/visual-truncate.ts +0 -1
  239. package/src/modes/components/welcome.ts +4 -4
  240. package/src/modes/controllers/command-controller.ts +46 -47
  241. package/src/modes/controllers/event-controller.ts +16 -20
  242. package/src/modes/controllers/extension-ui-controller.ts +40 -46
  243. package/src/modes/controllers/input-controller.ts +17 -18
  244. package/src/modes/controllers/selector-controller.ts +103 -91
  245. package/src/modes/index.ts +3 -3
  246. package/src/modes/interactive-mode.ts +27 -29
  247. package/src/modes/print-mode.ts +12 -13
  248. package/src/modes/rpc/rpc-client.ts +7 -8
  249. package/src/modes/rpc/rpc-mode.ts +24 -25
  250. package/src/modes/rpc/rpc-types.ts +3 -4
  251. package/src/modes/theme/mermaid-cache.ts +2 -2
  252. package/src/modes/theme/theme.ts +128 -53
  253. package/src/modes/types.ts +10 -10
  254. package/src/modes/utils/ui-helpers.ts +17 -17
  255. package/src/patch/applicator.ts +18 -19
  256. package/src/patch/diff.ts +1 -2
  257. package/src/patch/fuzzy.ts +1 -2
  258. package/src/patch/index.ts +10 -11
  259. package/src/patch/normalize.ts +4 -4
  260. package/src/patch/normative.ts +1 -2
  261. package/src/patch/parser.ts +8 -9
  262. package/src/patch/shared.ts +12 -13
  263. package/src/sdk.ts +60 -63
  264. package/src/session/agent-session.ts +83 -84
  265. package/src/session/agent-storage.ts +11 -11
  266. package/src/session/artifacts.ts +8 -9
  267. package/src/session/auth-storage.ts +25 -29
  268. package/src/session/compaction/branch-summarization.ts +7 -10
  269. package/src/session/compaction/compaction.ts +8 -19
  270. package/src/session/compaction/utils.ts +6 -9
  271. package/src/session/history-storage.ts +10 -10
  272. package/src/session/messages.ts +4 -5
  273. package/src/session/session-manager.ts +76 -65
  274. package/src/session/session-storage.ts +57 -69
  275. package/src/session/storage-migration.ts +2 -3
  276. package/src/session/streaming-output.ts +2 -2
  277. package/src/ssh/connection-manager.ts +43 -50
  278. package/src/ssh/ssh-executor.ts +2 -2
  279. package/src/ssh/sshfs-mount.ts +11 -18
  280. package/src/system-prompt.ts +27 -34
  281. package/src/task/agents.ts +45 -30
  282. package/src/task/commands.ts +6 -7
  283. package/src/task/discovery.ts +39 -76
  284. package/src/task/executor.ts +14 -15
  285. package/src/task/index.ts +33 -36
  286. package/src/task/output-manager.ts +3 -4
  287. package/src/task/parallel.ts +0 -1
  288. package/src/task/render.ts +19 -20
  289. package/src/task/subprocess-tool-registry.ts +1 -2
  290. package/src/task/worker-protocol.ts +3 -3
  291. package/src/task/worker.ts +32 -38
  292. package/src/task/worktree.ts +19 -19
  293. package/src/tools/ask.ts +8 -9
  294. package/src/tools/bash-interceptor.ts +1 -5
  295. package/src/tools/bash.ts +19 -18
  296. package/src/tools/calculator.ts +12 -12
  297. package/src/tools/complete.ts +3 -4
  298. package/src/tools/context.ts +2 -2
  299. package/src/tools/fetch.ts +23 -26
  300. package/src/tools/find.ts +15 -16
  301. package/src/tools/gemini-image.ts +14 -14
  302. package/src/tools/grep.ts +27 -27
  303. package/src/tools/index.ts +78 -56
  304. package/src/tools/list-limit.ts +1 -1
  305. package/src/tools/ls.ts +7 -7
  306. package/src/tools/notebook.ts +5 -5
  307. package/src/tools/output-meta.ts +3 -4
  308. package/src/tools/output-utils.ts +1 -1
  309. package/src/tools/path-utils.ts +5 -5
  310. package/src/tools/python.ts +36 -37
  311. package/src/tools/read.ts +23 -23
  312. package/src/tools/render-utils.ts +8 -9
  313. package/src/tools/renderers.ts +6 -7
  314. package/src/tools/review.ts +8 -11
  315. package/src/tools/ssh.ts +31 -30
  316. package/src/tools/todo-write.ts +13 -13
  317. package/src/tools/tool-errors.ts +3 -3
  318. package/src/tools/tool-result.ts +3 -8
  319. package/src/tools/write.ts +11 -16
  320. package/src/tui/code-cell.ts +3 -9
  321. package/src/tui/file-list.ts +3 -4
  322. package/src/tui/output-block.ts +1 -2
  323. package/src/tui/status-line.ts +2 -3
  324. package/src/tui/tree-list.ts +2 -3
  325. package/src/tui/types.ts +1 -2
  326. package/src/tui/utils.ts +2 -3
  327. package/src/utils/changelog.ts +9 -10
  328. package/src/utils/clipboard.ts +11 -11
  329. package/src/utils/file-mentions.ts +4 -10
  330. package/src/utils/frontmatter.ts +6 -3
  331. package/src/utils/fuzzy.ts +2 -2
  332. package/src/utils/image-convert.ts +1 -1
  333. package/src/utils/image-resize.ts +1 -1
  334. package/src/utils/mime.ts +2 -2
  335. package/src/utils/shell-snapshot.ts +11 -13
  336. package/src/utils/shell.ts +4 -5
  337. package/src/utils/title-generator.ts +8 -9
  338. package/src/utils/tools-manager.ts +23 -23
  339. package/src/vendor/photon/index.js +1099 -1059
  340. package/src/vendor/photon/photon_rs_bg.wasm +0 -0
  341. package/src/web/scrapers/artifacthub.ts +1 -1
  342. package/src/web/scrapers/arxiv.ts +2 -2
  343. package/src/web/scrapers/bluesky.ts +2 -2
  344. package/src/web/scrapers/cheatsh.ts +1 -1
  345. package/src/web/scrapers/chocolatey.ts +2 -2
  346. package/src/web/scrapers/choosealicense.ts +5 -5
  347. package/src/web/scrapers/cisa-kev.ts +1 -1
  348. package/src/web/scrapers/crossref.ts +2 -2
  349. package/src/web/scrapers/devto.ts +3 -3
  350. package/src/web/scrapers/discogs.ts +3 -4
  351. package/src/web/scrapers/discourse.ts +1 -1
  352. package/src/web/scrapers/dockerhub.ts +1 -1
  353. package/src/web/scrapers/fdroid.ts +2 -2
  354. package/src/web/scrapers/firefox-addons.ts +3 -3
  355. package/src/web/scrapers/flathub.ts +1 -1
  356. package/src/web/scrapers/github.ts +3 -3
  357. package/src/web/scrapers/gitlab.ts +4 -4
  358. package/src/web/scrapers/hackernews.ts +2 -2
  359. package/src/web/scrapers/huggingface.ts +1 -1
  360. package/src/web/scrapers/iacr.ts +2 -2
  361. package/src/web/scrapers/index.ts +0 -1
  362. package/src/web/scrapers/jetbrains-marketplace.ts +1 -1
  363. package/src/web/scrapers/lemmy.ts +2 -2
  364. package/src/web/scrapers/maven.ts +2 -2
  365. package/src/web/scrapers/mdn.ts +2 -4
  366. package/src/web/scrapers/metacpan.ts +2 -2
  367. package/src/web/scrapers/musicbrainz.ts +1 -2
  368. package/src/web/scrapers/npm.ts +1 -1
  369. package/src/web/scrapers/nuget.ts +2 -2
  370. package/src/web/scrapers/nvd.ts +3 -3
  371. package/src/web/scrapers/ollama.ts +7 -9
  372. package/src/web/scrapers/opencorporates.ts +2 -2
  373. package/src/web/scrapers/openlibrary.ts +6 -6
  374. package/src/web/scrapers/orcid.ts +0 -1
  375. package/src/web/scrapers/osv.ts +2 -2
  376. package/src/web/scrapers/packagist.ts +1 -1
  377. package/src/web/scrapers/pubmed.ts +1 -2
  378. package/src/web/scrapers/rawg.ts +2 -2
  379. package/src/web/scrapers/readthedocs.ts +1 -2
  380. package/src/web/scrapers/repology.ts +2 -2
  381. package/src/web/scrapers/rfc.ts +1 -1
  382. package/src/web/scrapers/searchcode.ts +2 -2
  383. package/src/web/scrapers/semantic-scholar.ts +1 -1
  384. package/src/web/scrapers/snapcraft.ts +2 -2
  385. package/src/web/scrapers/sourcegraph.ts +1 -1
  386. package/src/web/scrapers/spdx.ts +3 -3
  387. package/src/web/scrapers/spotify.ts +0 -1
  388. package/src/web/scrapers/twitter.ts +1 -1
  389. package/src/web/scrapers/types.ts +1 -2
  390. package/src/web/scrapers/utils.ts +5 -5
  391. package/src/web/scrapers/wikidata.ts +3 -3
  392. package/src/web/scrapers/youtube.ts +9 -14
  393. package/src/web/search/auth.ts +4 -9
  394. package/src/web/search/index.ts +11 -21
  395. package/src/web/search/providers/anthropic.ts +3 -9
  396. package/src/web/search/providers/exa.ts +6 -10
  397. package/src/web/search/providers/perplexity.ts +5 -5
  398. package/src/web/search/render.ts +16 -18
  399. package/scripts/generate-wasm-b64.ts +0 -24
  400. package/src/commit/map-reduce/.map-phase.ts.kate-swp +0 -0
  401. package/src/task/.executor.ts.kate-swp +0 -0
  402. package/src/vendor/photon/photon_rs_bg.wasm.b64.js +0 -1
@@ -1,10 +1,10 @@
1
- import { relative, resolve } from "node:path";
1
+ import * as path from "node:path";
2
2
  import type { Api, Model } from "@oh-my-pi/pi-ai";
3
- import { detectChangelogBoundaries } from "@oh-my-pi/pi-coding-agent/commit/changelog/detect";
4
- import { generateChangelogEntries } from "@oh-my-pi/pi-coding-agent/commit/changelog/generate";
5
- import { parseUnreleasedSection } from "@oh-my-pi/pi-coding-agent/commit/changelog/parse";
6
- import type { ControlledGit } from "@oh-my-pi/pi-coding-agent/commit/git";
7
3
  import { logger } from "@oh-my-pi/pi-utils";
4
+ import type { ControlledGit } from "../../commit/git";
5
+ import { detectChangelogBoundaries } from "./detect";
6
+ import { generateChangelogEntries } from "./generate";
7
+ import { parseUnreleasedSection } from "./parse";
8
8
 
9
9
  const CHANGELOG_SECTIONS = ["Breaking Changes", "Added", "Changed", "Deprecated", "Removed", "Fixed", "Security"];
10
10
 
@@ -67,7 +67,7 @@ export async function runChangelogFlow({
67
67
  continue;
68
68
  }
69
69
  const existingEntries = formatExistingEntries(unreleased.entries);
70
- const isPackageChangelog = resolve(boundary.changelogPath) !== resolve(cwd, "CHANGELOG.md");
70
+ const isPackageChangelog = path.resolve(boundary.changelogPath) !== path.resolve(cwd, "CHANGELOG.md");
71
71
  const generated = await generateChangelogEntries({
72
72
  model,
73
73
  apiKey,
@@ -82,7 +82,7 @@ export async function runChangelogFlow({
82
82
  const updatedContent = applyChangelogEntries(changelogContent, unreleased, generated.entries);
83
83
  if (!dryRun) {
84
84
  await Bun.write(boundary.changelogPath, updatedContent);
85
- await git.stageFiles([relative(cwd, boundary.changelogPath)]);
85
+ await git.stageFiles([path.relative(cwd, boundary.changelogPath)]);
86
86
  }
87
87
  updated.push(boundary.changelogPath);
88
88
  }
@@ -127,7 +127,7 @@ export async function applyChangelogProposals({
127
127
  const updatedContent = applyChangelogEntries(changelogContent, unreleased, normalized, normalizedDeletions);
128
128
  if (!dryRun) {
129
129
  await Bun.write(proposal.path, updatedContent);
130
- await git.stageFiles([relative(cwd, proposal.path)]);
130
+ await git.stageFiles([path.relative(cwd, proposal.path)]);
131
131
  }
132
132
  updated.push(proposal.path);
133
133
  }
@@ -178,8 +178,8 @@ function applyDeletions(
178
178
  ): Record<string, string[]> {
179
179
  const result: Record<string, string[]> = {};
180
180
  for (const [section, items] of Object.entries(existing)) {
181
- const toDelete = new Set((deletions[section] ?? []).map((d) => d.toLowerCase()));
182
- const filtered = items.filter((item) => !toDelete.has(item.toLowerCase()));
181
+ const toDelete = new Set((deletions[section] ?? []).map(d => d.toLowerCase()));
182
+ const filtered = items.filter(item => !toDelete.has(item.toLowerCase()));
183
183
  if (filtered.length > 0) {
184
184
  result[section] = filtered;
185
185
  }
@@ -194,7 +194,7 @@ function mergeEntries(
194
194
  const merged: Record<string, string[]> = { ...existing };
195
195
  for (const [section, items] of Object.entries(incoming)) {
196
196
  const current = merged[section] ?? [];
197
- const lower = new Set(current.map((item) => item.toLowerCase()));
197
+ const lower = new Set(current.map(item => item.toLowerCase()));
198
198
  for (const item of items) {
199
199
  if (!lower.has(item.toLowerCase())) {
200
200
  current.push(item);
@@ -225,9 +225,9 @@ function renderUnreleasedSections(entries: Record<string, string[]>): string[] {
225
225
  function normalizeEntries(entries: Record<string, string[]>): Record<string, string[]> {
226
226
  const result: Record<string, string[]> = {};
227
227
  for (const [section, items] of Object.entries(entries)) {
228
- const trimmed = items.map((item) => item.trim().replace(/\.$/, "")).filter((item) => item.length > 0);
228
+ const trimmed = items.map(item => item.trim().replace(/\.$/, "")).filter(item => item.length > 0);
229
229
  if (trimmed.length === 0) continue;
230
- result[section] = Array.from(new Set(trimmed.map((item) => item.trim())));
230
+ result[section] = Array.from(new Set(trimmed.map(item => item.trim())));
231
231
  }
232
232
  return result;
233
233
  }
@@ -1,11 +1,11 @@
1
- import type { UnreleasedSection } from "@oh-my-pi/pi-coding-agent/commit/types";
1
+ import type { UnreleasedSection } from "../../commit/types";
2
2
 
3
3
  const UNRELEASED_PATTERN = /^##\s+\[?Unreleased\]?/i;
4
4
  const SECTION_PATTERN = /^###\s+(.*)$/;
5
5
 
6
6
  export function parseUnreleasedSection(content: string): UnreleasedSection {
7
7
  const lines = content.split("\n");
8
- const startIndex = lines.findIndex((line) => UNRELEASED_PATTERN.test(line.trim()));
8
+ const startIndex = lines.findIndex(line => UNRELEASED_PATTERN.test(line.trim()));
9
9
  if (startIndex === -1) {
10
10
  throw new Error("No [Unreleased] section found in changelog");
11
11
  }
package/src/commit/cli.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { CommitCommandArgs } from "@oh-my-pi/pi-coding-agent/commit/types";
2
1
  import chalk from "chalk";
2
+ import type { CommitCommandArgs } from "./types";
3
3
 
4
4
  const FLAG_ALIASES = new Map<string, string>([
5
5
  ["-c", "--context"],
@@ -1,4 +1,4 @@
1
- import type { DiffHunk, FileDiff, FileHunks, NumstatEntry } from "@oh-my-pi/pi-coding-agent/commit/types";
1
+ import type { DiffHunk, FileDiff, FileHunks, NumstatEntry } from "../../commit/types";
2
2
 
3
3
  export function parseNumstat(output: string): NumstatEntry[] {
4
4
  const entries: NumstatEntry[] = [];
@@ -31,7 +31,7 @@ export function parseFileDiffs(diff: string): FileDiff[] {
31
31
  if (!match) continue;
32
32
  const filename = match[2];
33
33
  const content = part;
34
- const isBinary = lines.some((line) => line.startsWith("Binary files "));
34
+ const isBinary = lines.some(line => line.startsWith("Binary files "));
35
35
  let additions = 0;
36
36
  let deletions = 0;
37
37
  for (const line of lines) {
@@ -52,7 +52,7 @@ export function parseFileDiffs(diff: string): FileDiff[] {
52
52
 
53
53
  export function parseDiffHunks(diff: string): FileHunks[] {
54
54
  const files = parseFileDiffs(diff);
55
- return files.map((file) => parseFileHunks(file));
55
+ return files.map(file => parseFileHunks(file));
56
56
  }
57
57
 
58
58
  export function parseFileHunks(fileDiff: FileDiff): FileHunks {
@@ -1,17 +1,12 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { rm } from "node:fs/promises";
3
- import { tmpdir } from "node:os";
4
- import { join } from "node:path";
5
- import {
6
- parseDiffHunks,
7
- parseFileDiffs,
8
- parseFileHunks,
9
- parseNumstat,
10
- } from "@oh-my-pi/pi-coding-agent/commit/git/diff";
11
- import { GitError } from "@oh-my-pi/pi-coding-agent/commit/git/errors";
12
- import { commit, push, resetStaging, runGitCommand, stageFiles } from "@oh-my-pi/pi-coding-agent/commit/git/operations";
13
- import type { FileDiff, FileHunks, NumstatEntry } from "@oh-my-pi/pi-coding-agent/commit/types";
2
+ import * as fs from "node:fs/promises";
3
+ import * as os from "node:os";
4
+ import * as path from "node:path";
14
5
  import { logger } from "@oh-my-pi/pi-utils";
6
+ import type { FileDiff, FileHunks, NumstatEntry } from "../../commit/types";
7
+ import { parseDiffHunks, parseFileDiffs, parseFileHunks, parseNumstat } from "./diff";
8
+ import { GitError } from "./errors";
9
+ import { commit, push, resetStaging, runGitCommand, stageFiles } from "./operations";
15
10
 
16
11
  export type HunkSelection = {
17
12
  path: string;
@@ -41,7 +36,7 @@ export class ControlledGit {
41
36
  this.ensureSuccess(result, "git diff --name-only");
42
37
  return result.stdout
43
38
  .split("\n")
44
- .map((line) => line.trim())
39
+ .map(line => line.trim())
45
40
  .filter(Boolean);
46
41
  }
47
42
 
@@ -71,7 +66,7 @@ export class ControlledGit {
71
66
  this.ensureSuccess(result, "git log");
72
67
  return result.stdout
73
68
  .split("\n")
74
- .map((line) => line.trim())
69
+ .map(line => line.trim())
75
70
  .filter(Boolean);
76
71
  }
77
72
 
@@ -80,7 +75,7 @@ export class ControlledGit {
80
75
  this.ensureSuccess(result, "git diff --cached --name-only");
81
76
  return result.stdout
82
77
  .split("\n")
83
- .map((line) => line.trim())
78
+ .map(line => line.trim())
84
79
  .filter(Boolean);
85
80
  }
86
81
 
@@ -89,7 +84,7 @@ export class ControlledGit {
89
84
  this.ensureSuccess(result, "git ls-files --others --exclude-standard");
90
85
  return result.stdout
91
86
  .split("\n")
92
- .map((line) => line.trim())
87
+ .map(line => line.trim())
93
88
  .filter(Boolean);
94
89
  }
95
90
 
@@ -107,7 +102,7 @@ export class ControlledGit {
107
102
  if (selections.length === 0) return;
108
103
  const diff = await this.getDiff(false);
109
104
  const fileDiffs = parseFileDiffs(diff);
110
- const fileDiffMap = new Map(fileDiffs.map((entry) => [entry.filename, entry]));
105
+ const fileDiffMap = new Map(fileDiffs.map(entry => [entry.filename, entry]));
111
106
  const patchParts: string[] = [];
112
107
  for (const selection of selections) {
113
108
  const fileDiff = fileDiffMap.get(selection.path);
@@ -133,19 +128,19 @@ export class ControlledGit {
133
128
  throw new GitError("git apply --cached", `No hunks selected for ${selection.path}`);
134
129
  }
135
130
  const header = extractFileHeader(fileDiff.content);
136
- const filePatch = [header, ...selectedHunks.map((hunk) => hunk.content)].join("\n");
131
+ const filePatch = [header, ...selectedHunks.map(hunk => hunk.content)].join("\n");
137
132
  patchParts.push(filePatch);
138
133
  }
139
134
 
140
135
  const patch = joinPatch(patchParts);
141
136
  if (!patch.trim()) return;
142
- const tempPath = join(tmpdir(), `omp-hunks-${randomUUID()}.patch`);
137
+ const tempPath = path.join(os.tmpdir(), `omp-hunks-${randomUUID()}.patch`);
143
138
  try {
144
139
  await Bun.write(tempPath, patch);
145
140
  const result = await runGitCommand(this.cwd, ["apply", "--cached", "--binary", tempPath]);
146
141
  this.ensureSuccess(result, "git apply --cached");
147
142
  } finally {
148
- await rm(tempPath, { force: true });
143
+ await fs.rm(tempPath, { force: true });
149
144
  }
150
145
  }
151
146
 
@@ -197,7 +192,7 @@ function extractFileHeader(diff: string): string {
197
192
 
198
193
  function joinPatch(parts: string[]): string {
199
194
  return parts
200
- .map((part) => (part.endsWith("\n") ? part : `${part}\n`))
195
+ .map(part => (part.endsWith("\n") ? part : `${part}\n`))
201
196
  .join("\n")
202
197
  .trimEnd()
203
198
  .concat("\n");
@@ -205,13 +200,13 @@ function joinPatch(parts: string[]): string {
205
200
 
206
201
  function selectHunks(file: FileHunks, selector: HunkSelection["hunks"]): FileHunks["hunks"] {
207
202
  if (selector.type === "indices") {
208
- const wanted = new Set(selector.indices.map((value) => Math.max(1, Math.floor(value))));
209
- return file.hunks.filter((hunk) => wanted.has(hunk.index + 1));
203
+ const wanted = new Set(selector.indices.map(value => Math.max(1, Math.floor(value))));
204
+ return file.hunks.filter(hunk => wanted.has(hunk.index + 1));
210
205
  }
211
206
  if (selector.type === "lines") {
212
207
  const start = Math.floor(selector.start);
213
208
  const end = Math.floor(selector.end);
214
- return file.hunks.filter((hunk) => hunk.newStart <= end && hunk.newStart + hunk.newLines - 1 >= start);
209
+ return file.hunks.filter(hunk => hunk.newStart <= end && hunk.newStart + hunk.newLines - 1 >= start);
215
210
  }
216
211
  return file.hunks;
217
212
  }
@@ -2,4 +2,4 @@
2
2
  * Entry points for the omp commit command.
3
3
  */
4
4
 
5
- export { runCommitCommand } from "@oh-my-pi/pi-coding-agent/commit/pipeline";
5
+ export { runCommitCommand } from "./pipeline";
@@ -1,10 +1,10 @@
1
1
  import type { Api, Model } from "@oh-my-pi/pi-ai";
2
- import { parseFileDiffs } from "@oh-my-pi/pi-coding-agent/commit/git/diff";
3
- import { runMapPhase } from "@oh-my-pi/pi-coding-agent/commit/map-reduce/map-phase";
4
- import { runReducePhase } from "@oh-my-pi/pi-coding-agent/commit/map-reduce/reduce-phase";
5
- import { estimateTokens } from "@oh-my-pi/pi-coding-agent/commit/map-reduce/utils";
6
- import type { ConventionalAnalysis } from "@oh-my-pi/pi-coding-agent/commit/types";
7
- import { isExcludedFile } from "@oh-my-pi/pi-coding-agent/commit/utils/exclusions";
2
+ import { parseFileDiffs } from "../../commit/git/diff";
3
+ import type { ConventionalAnalysis } from "../../commit/types";
4
+ import { isExcludedFile } from "../../commit/utils/exclusions";
5
+ import { runMapPhase } from "./map-phase";
6
+ import { runReducePhase } from "./reduce-phase";
7
+ import { estimateTokens } from "./utils";
8
8
 
9
9
  const MIN_FILES_FOR_MAP_REDUCE = 4;
10
10
  const MAX_FILE_TOKENS = 50_000;
@@ -34,10 +34,10 @@ export function shouldUseMapReduce(diff: string, settings?: MapReduceSettings):
34
34
  if (settings?.enabled === false) return false;
35
35
  const minFiles = settings?.minFiles ?? MIN_FILES_FOR_MAP_REDUCE;
36
36
  const maxFileTokens = settings?.maxFileTokens ?? MAX_FILE_TOKENS;
37
- const files = parseFileDiffs(diff).filter((file) => !isExcludedFile(file.filename));
37
+ const files = parseFileDiffs(diff).filter(file => !isExcludedFile(file.filename));
38
38
  const fileCount = files.length;
39
39
  if (fileCount >= minFiles) return true;
40
- return files.some((file) => estimateTokens(file.content) > maxFileTokens);
40
+ return files.some(file => estimateTokens(file.content) > maxFileTokens);
41
41
  }
42
42
 
43
43
  /**
@@ -45,7 +45,7 @@ export function shouldUseMapReduce(diff: string, settings?: MapReduceSettings):
45
45
  */
46
46
 
47
47
  export async function runMapReduceAnalysis(input: MapReduceInput): Promise<ConventionalAnalysis> {
48
- const fileDiffs = parseFileDiffs(input.diff).filter((file) => !isExcludedFile(file.filename));
48
+ const fileDiffs = parseFileDiffs(input.diff).filter(file => !isExcludedFile(file.filename));
49
49
  const observations = await runMapPhase({
50
50
  model: input.smolModel,
51
51
  apiKey: input.smolApiKey,
@@ -1,15 +1,11 @@
1
- import type { Api, AssistantMessage, Model } from "@oh-my-pi/pi-ai";
1
+ import type { Api, AssistantMessage, Message, Model } from "@oh-my-pi/pi-ai";
2
2
  import { completeSimple } from "@oh-my-pi/pi-ai";
3
- import { truncateToTokenLimit } from "@oh-my-pi/pi-coding-agent/commit/map-reduce/utils";
4
- import fileObserverSystemPrompt from "@oh-my-pi/pi-coding-agent/commit/prompts/file-observer-system.md" with {
5
- type: "text",
6
- };
7
- import fileObserverUserPrompt from "@oh-my-pi/pi-coding-agent/commit/prompts/file-observer-user.md" with {
8
- type: "text",
9
- };
10
- import type { FileDiff, FileObservation } from "@oh-my-pi/pi-coding-agent/commit/types";
11
- import { isExcludedFile } from "@oh-my-pi/pi-coding-agent/commit/utils/exclusions";
12
- import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
3
+ import fileObserverSystemPrompt from "../../commit/prompts/file-observer-system.md" with { type: "text" };
4
+ import fileObserverUserPrompt from "../../commit/prompts/file-observer-user.md" with { type: "text" };
5
+ import type { FileDiff, FileObservation } from "../../commit/types";
6
+ import { isExcludedFile } from "../../commit/utils/exclusions";
7
+ import { renderPromptTemplate } from "../../config/prompt-templates";
8
+ import { truncateToTokenLimit } from "./utils";
13
9
 
14
10
  const MAX_FILE_TOKENS = 50_000;
15
11
  const MAX_CONTEXT_FILES = 20;
@@ -32,14 +28,14 @@ export interface MapPhaseInput {
32
28
  }
33
29
 
34
30
  export async function runMapPhase({ model, apiKey, files, config }: MapPhaseInput): Promise<FileObservation[]> {
35
- const filtered = files.filter((file) => !isExcludedFile(file.filename));
31
+ const filtered = files.filter(file => !isExcludedFile(file.filename));
36
32
  const systemPrompt = renderPromptTemplate(fileObserverSystemPrompt);
37
33
  const maxFileTokens = config?.maxFileTokens ?? MAX_FILE_TOKENS;
38
34
  const maxConcurrency = config?.maxConcurrency ?? MAX_CONCURRENCY;
39
35
  const timeoutMs = config?.timeoutMs ?? MAP_PHASE_TIMEOUT_MS;
40
36
  const maxRetries = config?.maxRetries ?? MAX_RETRIES;
41
37
  const retryBackoffMs = config?.retryBackoffMs ?? RETRY_BACKOFF_MS;
42
- return runWithConcurrency(filtered, maxConcurrency, async (file) => {
38
+ return runWithConcurrency(filtered, maxConcurrency, async file => {
43
39
  if (file.isBinary) {
44
40
  return {
45
41
  file: file.filename,
@@ -56,24 +52,13 @@ export async function runMapPhase({ model, apiKey, files, config }: MapPhaseInpu
56
52
  diff: truncated,
57
53
  context_header: contextHeader,
58
54
  });
55
+ const request = {
56
+ systemPrompt,
57
+ messages: [{ role: "user", content: prompt, timestamp: Date.now() }] as Message[],
58
+ };
59
59
 
60
60
  const response = await withRetry(
61
- async () => {
62
- const controller = new AbortController();
63
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
64
- try {
65
- return await completeSimple(
66
- model,
67
- {
68
- systemPrompt: systemPrompt,
69
- messages: [{ role: "user", content: prompt, timestamp: Date.now() }],
70
- },
71
- { apiKey, maxTokens: 400, signal: controller.signal },
72
- );
73
- } finally {
74
- clearTimeout(timeout);
75
- }
76
- },
61
+ () => completeSimple(model, request, { apiKey, maxTokens: 400, signal: AbortSignal.timeout(timeoutMs) }),
77
62
  maxRetries,
78
63
  retryBackoffMs,
79
64
  );
@@ -90,8 +75,8 @@ export async function runMapPhase({ model, apiKey, files, config }: MapPhaseInpu
90
75
 
91
76
  function parseObservations(message: AssistantMessage): string[] {
92
77
  const text = message.content
93
- .filter((content) => content.type === "text")
94
- .map((content) => content.text)
78
+ .filter(content => content.type === "text")
79
+ .map(content => content.text)
95
80
  .join("")
96
81
  .trim();
97
82
 
@@ -99,9 +84,9 @@ function parseObservations(message: AssistantMessage): string[] {
99
84
 
100
85
  const lines = text
101
86
  .split("\n")
102
- .map((line) => line.trim())
87
+ .map(line => line.trim())
103
88
  .filter(Boolean)
104
- .map((line) => line.replace(/^[-*]\s+/, ""))
89
+ .map(line => line.replace(/^[-*]\s+/, ""))
105
90
  .filter(Boolean);
106
91
 
107
92
  return lines.slice(0, 5);
@@ -112,7 +97,7 @@ function generateContextHeader(files: FileDiff[], currentFile: string): string {
112
97
  return `(Large commit with ${files.length} total files)`;
113
98
  }
114
99
 
115
- const otherFiles = files.filter((file) => file.filename !== currentFile);
100
+ const otherFiles = files.filter(file => file.filename !== currentFile);
116
101
  if (otherFiles.length === 0) return "";
117
102
 
118
103
  const sorted = [...otherFiles].sort((a, b) => b.additions + b.deletions - (a.additions + a.deletions));
@@ -1,10 +1,10 @@
1
1
  import type { Api, AssistantMessage, Model, ToolCall } from "@oh-my-pi/pi-ai";
2
2
  import { completeSimple, validateToolCall } from "@oh-my-pi/pi-ai";
3
- import reduceSystemPrompt from "@oh-my-pi/pi-coding-agent/commit/prompts/reduce-system.md" with { type: "text" };
4
- import reduceUserPrompt from "@oh-my-pi/pi-coding-agent/commit/prompts/reduce-user.md" with { type: "text" };
5
- import type { ChangelogCategory, ConventionalAnalysis, FileObservation } from "@oh-my-pi/pi-coding-agent/commit/types";
6
- import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
7
3
  import { Type } from "@sinclair/typebox";
4
+ import reduceSystemPrompt from "../../commit/prompts/reduce-system.md" with { type: "text" };
5
+ import reduceUserPrompt from "../../commit/prompts/reduce-user.md" with { type: "text" };
6
+ import type { ChangelogCategory, ConventionalAnalysis, FileObservation } from "../../commit/types";
7
+ import { renderPromptTemplate } from "../../config/prompt-templates";
8
8
 
9
9
  const ReduceTool = {
10
10
  name: "create_conventional_analysis",
@@ -64,7 +64,7 @@ export async function runReducePhase({
64
64
  }: ReducePhaseInput): Promise<ConventionalAnalysis> {
65
65
  const prompt = renderPromptTemplate(reduceUserPrompt, {
66
66
  types_description: typesDescription,
67
- observations: observations.flatMap((obs) => obs.observations.map((line) => `- ${obs.file}: ${line}`)).join("\n"),
67
+ observations: observations.flatMap(obs => obs.observations.map(line => `- ${obs.file}: ${line}`)).join("\n"),
68
68
  stat,
69
69
  scope_candidates: scopeCandidates,
70
70
  });
@@ -123,7 +123,7 @@ function normalizeAnalysis(parsed: {
123
123
  return {
124
124
  type: parsed.type,
125
125
  scope: parsed.scope?.trim() || null,
126
- details: parsed.details.map((detail) => ({
126
+ details: parsed.details.map(detail => ({
127
127
  text: detail.text.trim(),
128
128
  changelogCategory: detail.user_visible ? detail.changelog_category : undefined,
129
129
  userVisible: detail.user_visible ?? false,
@@ -133,15 +133,13 @@ function normalizeAnalysis(parsed: {
133
133
  }
134
134
 
135
135
  function extractToolCall(message: AssistantMessage, name: string): ToolCall | undefined {
136
- return message.content.find((content) => content.type === "toolCall" && content.name === name) as
137
- | ToolCall
138
- | undefined;
136
+ return message.content.find(content => content.type === "toolCall" && content.name === name) as ToolCall | undefined;
139
137
  }
140
138
 
141
139
  function extractTextContent(message: AssistantMessage): string {
142
140
  return message.content
143
- .filter((content) => content.type === "text")
144
- .map((content) => content.text)
141
+ .filter(content => content.type === "text")
142
+ .map(content => content.text)
145
143
  .join("")
146
144
  .trim();
147
145
  }
@@ -1,9 +1,9 @@
1
- import type { ConventionalAnalysis } from "@oh-my-pi/pi-coding-agent/commit/types";
1
+ import type { ConventionalAnalysis } from "./types";
2
2
 
3
3
  export function formatCommitMessage(analysis: ConventionalAnalysis, summary: string): string {
4
4
  const scopePart = analysis.scope ? `(${analysis.scope})` : "";
5
5
  const header = `${analysis.type}${scopePart}: ${summary}`;
6
- const bodyLines = analysis.details.map((detail) => `- ${detail.text.trim()}`);
6
+ const bodyLines = analysis.details.map(detail => `- ${detail.text.trim()}`);
7
7
  if (bodyLines.length === 0) {
8
8
  return header;
9
9
  }
@@ -1,10 +1,6 @@
1
1
  import type { Api, Model } from "@oh-my-pi/pi-ai";
2
- import {
3
- parseModelPattern,
4
- parseModelString,
5
- SMOL_MODEL_PRIORITY,
6
- } from "@oh-my-pi/pi-coding-agent/config/model-resolver";
7
- import type { SettingsManager } from "@oh-my-pi/pi-coding-agent/config/settings-manager";
2
+ import { parseModelPattern, parseModelString, SMOL_MODEL_PRIORITY } from "../config/model-resolver";
3
+ import type { SettingsManager } from "../config/settings-manager";
8
4
 
9
5
  export async function resolvePrimaryModel(
10
6
  override: string | undefined,
@@ -69,7 +65,7 @@ function resolveModelFromSettings(settingsManager: SettingsManager, available: M
69
65
  function resolveModelFromString(value: string, available: Model<Api>[]): Model<Api> | undefined {
70
66
  const parsed = parseModelString(value);
71
67
  if (parsed) {
72
- return available.find((model) => model.provider === parsed.provider && model.id === parsed.id);
68
+ return available.find(model => model.provider === parsed.provider && model.id === parsed.id);
73
69
  }
74
70
  return parseModelPattern(value, available).model;
75
71
  }
@@ -1,28 +1,26 @@
1
- import { relative } from "node:path";
1
+ import * as path from "node:path";
2
2
  import type { Api, Model } from "@oh-my-pi/pi-ai";
3
- import { runAgenticCommit } from "@oh-my-pi/pi-coding-agent/commit/agentic";
3
+ import { logger } from "@oh-my-pi/pi-utils";
4
+ import { renderPromptTemplate } from "../config/prompt-templates";
5
+ import { SettingsManager } from "../config/settings-manager";
6
+ import { discoverAuthStorage, discoverModels } from "../sdk";
7
+ import { loadProjectContextFiles } from "../system-prompt";
8
+ import { runAgenticCommit } from "./agentic";
4
9
  import {
5
10
  extractScopeCandidates,
6
11
  generateConventionalAnalysis,
7
12
  generateSummary,
8
13
  validateAnalysis,
9
14
  validateSummary,
10
- } from "@oh-my-pi/pi-coding-agent/commit/analysis";
11
- import { runChangelogFlow } from "@oh-my-pi/pi-coding-agent/commit/changelog";
12
- import { ControlledGit } from "@oh-my-pi/pi-coding-agent/commit/git";
13
- import { runMapReduceAnalysis, shouldUseMapReduce } from "@oh-my-pi/pi-coding-agent/commit/map-reduce";
14
- import { formatCommitMessage } from "@oh-my-pi/pi-coding-agent/commit/message";
15
- import { resolvePrimaryModel, resolveSmolModel } from "@oh-my-pi/pi-coding-agent/commit/model-selection";
16
- import summaryRetryPrompt from "@oh-my-pi/pi-coding-agent/commit/prompts/summary-retry.md" with { type: "text" };
17
- import typesDescriptionPrompt from "@oh-my-pi/pi-coding-agent/commit/prompts/types-description.md" with {
18
- type: "text",
19
- };
20
- import type { CommitCommandArgs, ConventionalAnalysis } from "@oh-my-pi/pi-coding-agent/commit/types";
21
- import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
22
- import { SettingsManager } from "@oh-my-pi/pi-coding-agent/config/settings-manager";
23
- import { discoverAuthStorage, discoverModels } from "@oh-my-pi/pi-coding-agent/sdk";
24
- import { loadProjectContextFiles } from "@oh-my-pi/pi-coding-agent/system-prompt";
25
- import { logger } from "@oh-my-pi/pi-utils";
15
+ } from "./analysis";
16
+ import { runChangelogFlow } from "./changelog";
17
+ import { ControlledGit } from "./git";
18
+ import { runMapReduceAnalysis, shouldUseMapReduce } from "./map-reduce";
19
+ import { formatCommitMessage } from "./message";
20
+ import { resolvePrimaryModel, resolveSmolModel } from "./model-selection";
21
+ import summaryRetryPrompt from "./prompts/summary-retry.md" with { type: "text" };
22
+ import typesDescriptionPrompt from "./prompts/types-description.md" with { type: "text" };
23
+ import type { CommitCommandArgs, ConventionalAnalysis } from "./types";
26
24
 
27
25
  const SUMMARY_MAX_CHARS = 72;
28
26
  const RECENT_COMMITS_COUNT = 8;
@@ -43,7 +41,7 @@ async function runLegacyCommitCommand(args: CommitCommandArgs): Promise<void> {
43
41
  const settingsManager = await SettingsManager.create(cwd);
44
42
  const commitSettings = settingsManager.getCommitSettings();
45
43
  const authStorage = await discoverAuthStorage();
46
- const modelRegistry = await discoverModels(authStorage);
44
+ const modelRegistry = discoverModels(authStorage);
47
45
 
48
46
  const { model: primaryModel, apiKey: primaryApiKey } = await resolvePrimaryModel(
49
47
  args.model,
@@ -87,8 +85,8 @@ async function runLegacyCommitCommand(args: CommitCommandArgs): Promise<void> {
87
85
  const scopeCandidates = extractScopeCandidates(numstat).scopeCandidates;
88
86
  const recentCommits = await git.getRecentCommits(RECENT_COMMITS_COUNT);
89
87
  const contextFiles = await loadProjectContextFiles({ cwd });
90
- const formattedContextFiles = contextFiles.map((file) => ({
91
- path: relative(cwd, file.path),
88
+ const formattedContextFiles = contextFiles.map(file => ({
89
+ path: path.relative(cwd, file.path),
92
90
  content: file.content,
93
91
  }));
94
92
 
@@ -209,7 +207,7 @@ async function generateSummaryWithRetry(input: {
209
207
  apiKey: input.apiKey,
210
208
  commitType: input.analysis.type,
211
209
  scope: input.analysis.scope,
212
- details: input.analysis.details.map((detail) => detail.text),
210
+ details: input.analysis.details.map(detail => detail.text),
213
211
  stat: input.stat,
214
212
  maxChars: SUMMARY_MAX_CHARS,
215
213
  userContext: context,
@@ -31,12 +31,12 @@ const EXCLUDED_SUFFIXES = [".lock.yml", ".lock.yaml", "-lock.yml", "-lock.yaml"]
31
31
 
32
32
  export function isExcludedFile(path: string): boolean {
33
33
  const lower = path.toLowerCase();
34
- if (EXCLUDED_FILES.some((name) => lower.endsWith(name.toLowerCase()))) {
34
+ if (EXCLUDED_FILES.some(name => lower.endsWith(name.toLowerCase()))) {
35
35
  return true;
36
36
  }
37
- return EXCLUDED_SUFFIXES.some((suffix) => lower.endsWith(suffix));
37
+ return EXCLUDED_SUFFIXES.some(suffix => lower.endsWith(suffix));
38
38
  }
39
39
 
40
40
  export function filterExcludedFiles<T extends { filename: string }>(files: T[]): T[] {
41
- return files.filter((file) => !isExcludedFile(file.filename));
41
+ return files.filter(file => !isExcludedFile(file.filename));
42
42
  }
@@ -1,5 +1,5 @@
1
- import { existsSync } from "node:fs";
2
- import { mkdir, readFile, rm } from "node:fs/promises";
1
+ import * as fs from "node:fs/promises";
2
+ import { isEnoent } from "@oh-my-pi/pi-utils";
3
3
 
4
4
  export interface FileLockOptions {
5
5
  staleMs?: number;
@@ -29,7 +29,7 @@ async function writeLockInfo(lockPath: string): Promise<void> {
29
29
 
30
30
  async function readLockInfo(lockPath: string): Promise<LockInfo | null> {
31
31
  try {
32
- const content = await readFile(`${lockPath}/info`, "utf-8");
32
+ const content = await fs.readFile(`${lockPath}/info`, "utf-8");
33
33
  return JSON.parse(content) as LockInfo;
34
34
  } catch {
35
35
  return null;
@@ -58,7 +58,7 @@ async function isLockStale(lockPath: string, staleMs: number): Promise<boolean>
58
58
 
59
59
  async function tryAcquireLock(lockPath: string): Promise<boolean> {
60
60
  try {
61
- await mkdir(lockPath);
61
+ await fs.mkdir(lockPath);
62
62
  await writeLockInfo(lockPath);
63
63
  return true;
64
64
  } catch (error) {
@@ -71,12 +71,22 @@ async function tryAcquireLock(lockPath: string): Promise<boolean> {
71
71
 
72
72
  async function releaseLock(lockPath: string): Promise<void> {
73
73
  try {
74
- await rm(lockPath, { recursive: true });
74
+ await fs.rm(lockPath, { recursive: true });
75
75
  } catch {
76
76
  // Ignore errors on release
77
77
  }
78
78
  }
79
79
 
80
+ async function lockExists(lockPath: string): Promise<boolean> {
81
+ try {
82
+ await fs.stat(lockPath);
83
+ return true;
84
+ } catch (err) {
85
+ if (isEnoent(err)) return false;
86
+ throw err;
87
+ }
88
+ }
89
+
80
90
  async function acquireLock(filePath: string, options: FileLockOptions = {}): Promise<() => Promise<void>> {
81
91
  const opts = { ...DEFAULT_OPTIONS, ...options };
82
92
  const lockPath = getLockPath(filePath);
@@ -86,12 +96,12 @@ async function acquireLock(filePath: string, options: FileLockOptions = {}): Pro
86
96
  return () => releaseLock(lockPath);
87
97
  }
88
98
 
89
- if (existsSync(lockPath) && (await isLockStale(lockPath, opts.staleMs))) {
99
+ if ((await lockExists(lockPath)) && (await isLockStale(lockPath, opts.staleMs))) {
90
100
  await releaseLock(lockPath);
91
101
  continue;
92
102
  }
93
103
 
94
- await new Promise((resolve) => setTimeout(resolve, opts.retryDelayMs));
104
+ await new Promise(resolve => setTimeout(resolve, opts.retryDelayMs));
95
105
  }
96
106
 
97
107
  throw new Error(`Failed to acquire lock for ${filePath} after ${opts.retries} attempts`);