@pugi/cli 0.1.0-beta.10 → 0.1.0-beta.101

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 (464) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/LICENSE +1 -1
  3. package/README.md +55 -11
  4. package/assets/pugi-prozr2-mascot.ansi +9 -0
  5. package/bin/run.js +33 -1
  6. package/dist/commands/deploy.js +40 -40
  7. package/dist/commands/flatten.js +191 -0
  8. package/dist/commands/jobs-watch.js +201 -0
  9. package/dist/commands/jobs.js +42 -27
  10. package/dist/commands/retro.js +210 -0
  11. package/dist/commands/smoke.js +133 -0
  12. package/dist/core/agent-progress/cleanup.js +134 -0
  13. package/dist/core/agent-progress/schema.js +144 -0
  14. package/dist/core/agent-progress/writer.js +101 -0
  15. package/dist/core/agents/adaptive-router.js +330 -0
  16. package/dist/core/agents/query-decomposer.js +297 -0
  17. package/dist/core/agents/registry.js +3 -3
  18. package/dist/core/approvals/shortcut-resolver.js +98 -0
  19. package/dist/core/artifact-chain/dispatcher.js +148 -0
  20. package/dist/core/artifact-chain/exporter.js +164 -0
  21. package/dist/core/artifact-chain/state.js +243 -0
  22. package/dist/core/artifact-chain/steps.js +169 -0
  23. package/dist/core/ask-user/question.js +92 -0
  24. package/dist/core/audit/audit-trail.js +275 -0
  25. package/dist/core/auth/ensure-authenticated.js +129 -0
  26. package/dist/core/auth/env-provider.js +238 -0
  27. package/dist/core/auto-open-browser.js +4 -4
  28. package/dist/core/auto-update/channels.js +122 -0
  29. package/dist/core/auto-update/checker.js +241 -0
  30. package/dist/core/auto-update/state.js +235 -0
  31. package/dist/core/bare-mode/index.js +107 -0
  32. package/dist/core/bash/redirect.js +281 -0
  33. package/dist/core/bash-classifier.js +436 -40
  34. package/dist/core/checkpoint/resumer.js +149 -0
  35. package/dist/core/checkpoint/rewinder.js +291 -0
  36. package/dist/core/checkpoints/shadow-git.js +670 -0
  37. package/dist/core/citations/parser.js +109 -0
  38. package/dist/core/classifier/yolo-classifier.js +88 -0
  39. package/dist/core/codegraph/db.js +506 -0
  40. package/dist/core/codegraph/decision-store.js +248 -0
  41. package/dist/core/codegraph/detect-repo.js +459 -0
  42. package/dist/core/codegraph/install.js +134 -0
  43. package/dist/core/codegraph/offer-hook.js +220 -0
  44. package/dist/core/codegraph/parser.js +598 -0
  45. package/dist/core/codegraph/queries/go.scm +57 -0
  46. package/dist/core/codegraph/queries/javascript.scm +56 -0
  47. package/dist/core/codegraph/queries/python.scm +55 -0
  48. package/dist/core/codegraph/queries/rust.scm +63 -0
  49. package/dist/core/codegraph/queries/typescript.scm +91 -0
  50. package/dist/core/codegraph/reindex.js +218 -0
  51. package/dist/core/codegraph/resolve-edges.js +107 -0
  52. package/dist/core/codegraph/types.js +34 -0
  53. package/dist/core/codegraph/watcher.js +440 -0
  54. package/dist/core/compact/auto-trigger.js +96 -0
  55. package/dist/core/compact/buffer-rewriter.js +115 -0
  56. package/dist/core/compact/summarizer.js +208 -0
  57. package/dist/core/compact/token-counter.js +108 -0
  58. package/dist/core/consensus/anvil-fanout.js +25 -25
  59. package/dist/core/consensus/diff-capture.js +121 -12
  60. package/dist/core/consensus/rubric.js +21 -21
  61. package/dist/core/context/builder.js +6 -6
  62. package/dist/core/context/compaction-events.js +8 -8
  63. package/dist/core/context/compaction.js +31 -31
  64. package/dist/core/context/index.js +15 -8
  65. package/dist/core/context/invariants.js +51 -51
  66. package/dist/core/context/markdown-loader.js +28 -10
  67. package/dist/core/context/markdown-traverse.js +255 -0
  68. package/dist/core/context/pugiignore.js +41 -41
  69. package/dist/core/context/repo-skeleton.js +37 -37
  70. package/dist/core/context/tool-eviction.js +55 -0
  71. package/dist/core/context/watcher.js +32 -32
  72. package/dist/core/context/working-set.js +23 -23
  73. package/dist/core/coordinator/agent-tools.js +77 -0
  74. package/dist/core/coordinator/agent-toolset.js +65 -0
  75. package/dist/core/coordinator/fsm.js +73 -0
  76. package/dist/core/coordinator/mode-fsm.js +70 -0
  77. package/dist/core/cost/rate-card.js +129 -0
  78. package/dist/core/cost/tracker.js +221 -0
  79. package/dist/core/credentials.js +13 -13
  80. package/dist/core/cron/scheduler.js +138 -0
  81. package/dist/core/denial-tracking/index.js +8 -0
  82. package/dist/core/denial-tracking/state.js +264 -0
  83. package/dist/core/diagnostics/probe-runner.js +93 -0
  84. package/dist/core/diagnostics/probes/api.js +46 -0
  85. package/dist/core/diagnostics/probes/auth.js +93 -0
  86. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  87. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  88. package/dist/core/diagnostics/probes/config.js +72 -0
  89. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  90. package/dist/core/diagnostics/probes/disk.js +81 -0
  91. package/dist/core/diagnostics/probes/engine-live.js +46 -0
  92. package/dist/core/diagnostics/probes/git.js +65 -0
  93. package/dist/core/diagnostics/probes/hooks.js +118 -0
  94. package/dist/core/diagnostics/probes/mcp.js +75 -0
  95. package/dist/core/diagnostics/probes/node.js +59 -0
  96. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  97. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  98. package/dist/core/diagnostics/probes/sandbox.js +67 -0
  99. package/dist/core/diagnostics/probes/session.js +74 -0
  100. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  101. package/dist/core/diagnostics/probes/workspace.js +63 -0
  102. package/dist/core/diagnostics/types.js +70 -0
  103. package/dist/core/dispatch/cache-cleanup.js +197 -0
  104. package/dist/core/dispatch/cache-handoff.js +295 -0
  105. package/dist/core/edits/apply-patch-layer-e.js +189 -0
  106. package/dist/core/edits/dispatch.js +333 -7
  107. package/dist/core/edits/format-detector.js +260 -0
  108. package/dist/core/edits/format-matrix.js +26 -0
  109. package/dist/core/edits/fuzzy-ladder.js +650 -0
  110. package/dist/core/edits/index.js +5 -1
  111. package/dist/core/edits/journal.js +199 -0
  112. package/dist/core/edits/layer-a-apply.js +15 -15
  113. package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
  114. package/dist/core/edits/layer-b-apply.js +9 -9
  115. package/dist/core/edits/layer-c-apply.js +6 -6
  116. package/dist/core/edits/layer-d-ast.js +557 -14
  117. package/dist/core/edits/marker-parser.js +12 -12
  118. package/dist/core/edits/security-gate.js +27 -27
  119. package/dist/core/edits/verify-hook.js +273 -0
  120. package/dist/core/edits/worktree.js +29 -29
  121. package/dist/core/engine/anvil-client.js +214 -26
  122. package/dist/core/engine/auto-compact.js +247 -0
  123. package/dist/core/engine/budgets.js +220 -0
  124. package/dist/core/engine/compact-llm-summarizer.js +124 -0
  125. package/dist/core/engine/context-prefix.js +155 -0
  126. package/dist/core/engine/index.js +1 -1
  127. package/dist/core/engine/intensity.js +163 -0
  128. package/dist/core/engine/intent.js +260 -0
  129. package/dist/core/engine/native-pugi.js +1559 -227
  130. package/dist/core/engine/prompts.js +219 -19
  131. package/dist/core/engine/strip-internal-fields.js +124 -0
  132. package/dist/core/engine/tool-bridge.js +1887 -59
  133. package/dist/core/engine/verification-patterns.js +195 -0
  134. package/dist/core/eval/v1/ledger.js +83 -0
  135. package/dist/core/eval/v1/runner.js +280 -0
  136. package/dist/core/eval/v1/scoring.js +68 -0
  137. package/dist/core/eval/v1/task-loader.js +191 -0
  138. package/dist/core/eval/v1/types.js +14 -0
  139. package/dist/core/eval/v1/verifier.js +176 -0
  140. package/dist/core/eval/v1/yaml-parser.js +250 -0
  141. package/dist/core/evaluation/golden-dataset.js +293 -0
  142. package/dist/core/feedback/queue.js +177 -0
  143. package/dist/core/feedback/submitter.js +145 -0
  144. package/dist/core/file-cache.js +113 -1
  145. package/dist/core/flatten/flatten-repo.js +439 -0
  146. package/dist/core/format/osc8-link.js +28 -0
  147. package/dist/core/hook-chains.js +392 -0
  148. package/dist/core/hooks/citation-verify-hook.js +138 -0
  149. package/dist/core/hooks/citation-verify.js +112 -0
  150. package/dist/core/hooks/events.js +46 -0
  151. package/dist/core/hooks/index.js +15 -0
  152. package/dist/core/hooks/registry.js +216 -0
  153. package/dist/core/hooks/runner.js +236 -0
  154. package/dist/core/hooks/v2/event-emitter.js +115 -0
  155. package/dist/core/hooks/v2/executor.js +282 -0
  156. package/dist/core/hooks/v2/index.js +25 -0
  157. package/dist/core/hooks/v2/lifecycle.js +104 -0
  158. package/dist/core/hooks/v2/loader.js +216 -0
  159. package/dist/core/hooks/v2/matcher.js +125 -0
  160. package/dist/core/hooks/v2/trust.js +143 -0
  161. package/dist/core/hooks/v2/types.js +86 -0
  162. package/dist/core/hooks/worktree-events.js +158 -0
  163. package/dist/core/image/renderer.js +71 -0
  164. package/dist/core/init/detector.js +582 -0
  165. package/dist/core/init/template-renderer.js +242 -0
  166. package/dist/core/jobs/registry.js +18 -18
  167. package/dist/core/ledger/results-tsv.js +142 -0
  168. package/dist/core/log-discipline/stdout-redirect.js +51 -0
  169. package/dist/core/lsp/cache.js +105 -0
  170. package/dist/core/lsp/client.js +551 -41
  171. package/dist/core/lsp/language-detect.js +66 -0
  172. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  173. package/dist/core/lsp/server-detect.js +173 -0
  174. package/dist/core/lsp/symbol-cache.js +162 -0
  175. package/dist/core/lsp/symbol-tools.js +664 -0
  176. package/dist/core/mcp/client.js +97 -28
  177. package/dist/core/mcp/http-server.js +553 -0
  178. package/dist/core/mcp/orchestrator-config.js +192 -0
  179. package/dist/core/mcp/orchestrator-tools.js +806 -0
  180. package/dist/core/mcp/permission.js +190 -0
  181. package/dist/core/mcp/registry.js +39 -17
  182. package/dist/core/mcp/server-tools.js +219 -0
  183. package/dist/core/mcp/server.js +397 -0
  184. package/dist/core/mcp/trust.js +10 -10
  185. package/dist/core/memory/dual-write.js +416 -0
  186. package/dist/core/memory/passive-extract.js +130 -0
  187. package/dist/core/memory/phase1-kinds.js +20 -0
  188. package/dist/core/memory/secret-scanner.js +304 -0
  189. package/dist/core/memory-sync/queue.js +170 -0
  190. package/dist/core/metrics/extract.js +113 -0
  191. package/dist/core/modes/roo-modes.js +68 -0
  192. package/dist/core/notes/notes-paths.js +113 -0
  193. package/dist/core/notes/notes-recorder.js +140 -0
  194. package/dist/core/notes/notes-writer.js +53 -0
  195. package/dist/core/notes/renderers.js +0 -0
  196. package/dist/core/notes/slug.js +105 -0
  197. package/dist/core/onboarding/ensure-initialized.js +133 -0
  198. package/dist/core/onboarding/marker.js +111 -0
  199. package/dist/core/onboarding/telemetry-state.js +108 -0
  200. package/dist/core/output-style/presets.js +176 -0
  201. package/dist/core/output-style/state.js +185 -0
  202. package/dist/core/path-security.js +287 -5
  203. package/dist/core/permission.js +82 -22
  204. package/dist/core/permissions/auto-classifier.js +124 -0
  205. package/dist/core/permissions/bash-parser.js +371 -0
  206. package/dist/core/permissions/circuit-breaker.js +83 -0
  207. package/dist/core/permissions/constrained-edit.js +91 -0
  208. package/dist/core/permissions/gate.js +278 -0
  209. package/dist/core/permissions/index.js +20 -0
  210. package/dist/core/permissions/mode.js +174 -0
  211. package/dist/core/permissions/network-egress.js +137 -0
  212. package/dist/core/permissions/state.js +241 -0
  213. package/dist/core/permissions/tool-class.js +107 -0
  214. package/dist/core/plan-mode/ui-state.js +51 -0
  215. package/dist/core/plans/plan-artifact.js +721 -0
  216. package/dist/core/policy-limits/etag-store.js +122 -0
  217. package/dist/core/prd-check/parser.js +215 -0
  218. package/dist/core/prd-check/reporter.js +127 -0
  219. package/dist/core/prd-check/session-review.js +557 -0
  220. package/dist/core/prd-check/verifiers.js +223 -0
  221. package/dist/core/prompt-cache/client-cache.js +99 -0
  222. package/dist/core/prompts/assembly.js +29 -0
  223. package/dist/core/prompts/registry.js +364 -0
  224. package/dist/core/pugi-gitignore.js +52 -0
  225. package/dist/core/pugi-md/cc-compat-rules.js +735 -0
  226. package/dist/core/pugi-md/context-injector.js +76 -0
  227. package/dist/core/pugi-md/walk-up.js +207 -0
  228. package/dist/core/python/uv-installer.js +270 -0
  229. package/dist/core/python/uv-resolver.js +83 -0
  230. package/dist/core/rate-limit/narrator.js +146 -0
  231. package/dist/core/recipes/cli-types.js +20 -0
  232. package/dist/core/recipes/loader.js +103 -0
  233. package/dist/core/recipes/runner.js +345 -0
  234. package/dist/core/recipes/schema.js +587 -0
  235. package/dist/core/release-notes/parser.js +241 -0
  236. package/dist/core/release-notes/state.js +116 -0
  237. package/dist/core/repl/ask.js +37 -37
  238. package/dist/core/repl/cancellation.js +26 -26
  239. package/dist/core/repl/cap-warning.js +4 -4
  240. package/dist/core/repl/clipboard-read.js +11 -11
  241. package/dist/core/repl/dispatch-fsm.js +12 -12
  242. package/dist/core/repl/engine-bridge.js +303 -0
  243. package/dist/core/repl/history-search.js +15 -15
  244. package/dist/core/repl/history.js +28 -18
  245. package/dist/core/repl/kill-ring.js +5 -5
  246. package/dist/core/repl/model-pricing.js +135 -0
  247. package/dist/core/repl/privacy-banner.js +22 -22
  248. package/dist/core/repl/session.js +2690 -229
  249. package/dist/core/repl/slash-commands.js +540 -41
  250. package/dist/core/repl/store/index.js +1 -1
  251. package/dist/core/repl/store/jsonl-log.js +22 -22
  252. package/dist/core/repl/store/lockfile.js +10 -10
  253. package/dist/core/repl/store/session-store.js +136 -107
  254. package/dist/core/repl/store/types.js +15 -15
  255. package/dist/core/repl/store/uuid-v7.js +12 -12
  256. package/dist/core/repl/tool-route.js +382 -0
  257. package/dist/core/repl/workspace-context.js +43 -21
  258. package/dist/core/repo-map/build.js +125 -0
  259. package/dist/core/repo-map/cache.js +185 -0
  260. package/dist/core/repo-map/extractor.js +254 -0
  261. package/dist/core/repo-map/formatter.js +145 -0
  262. package/dist/core/repo-map/page-rank.js +105 -0
  263. package/dist/core/repo-map/scanner.js +211 -0
  264. package/dist/core/retro/git-collector.js +251 -0
  265. package/dist/core/retro/health-card.js +25 -0
  266. package/dist/core/retro/metrics.js +342 -0
  267. package/dist/core/retro/narrative.js +249 -0
  268. package/dist/core/retro/plane-collector.js +274 -0
  269. package/dist/core/retro/pr-issue-link.js +65 -0
  270. package/dist/core/retro/types.js +16 -0
  271. package/dist/core/retry-budget/budget.js +284 -0
  272. package/dist/core/retry-budget/index.js +5 -0
  273. package/dist/core/retry-budget/retry-cap.js +74 -0
  274. package/dist/core/routing/lead-worker.js +43 -0
  275. package/dist/core/routing/pre-flight-estimator.js +108 -0
  276. package/dist/core/runs/run-tree.js +103 -0
  277. package/dist/core/sandboxing/adapter.js +43 -0
  278. package/dist/core/sandboxing/bubblewrap.js +209 -0
  279. package/dist/core/sandboxing/index.js +78 -0
  280. package/dist/core/sandboxing/none.js +19 -0
  281. package/dist/core/sandboxing/policy.js +97 -0
  282. package/dist/core/sandboxing/seatbelt.js +231 -0
  283. package/dist/core/security/injection-scanner.js +367 -0
  284. package/dist/core/security/output-filter.js +418 -0
  285. package/dist/core/session/env-file.js +105 -0
  286. package/dist/core/session/section-budgets.js +140 -0
  287. package/dist/core/session.js +119 -0
  288. package/dist/core/settings.js +402 -5
  289. package/dist/core/share/formatter.js +271 -0
  290. package/dist/core/share/redactor.js +221 -0
  291. package/dist/core/share/uploader.js +267 -0
  292. package/dist/core/skills/defaults.js +30 -30
  293. package/dist/core/skills/loader.js +22 -22
  294. package/dist/core/skills/sources.js +27 -27
  295. package/dist/core/smoke/headless-driver.js +174 -0
  296. package/dist/core/smoke/orchestrator.js +194 -0
  297. package/dist/core/smoke/runner.js +238 -0
  298. package/dist/core/smoke/scenario-parser.js +316 -0
  299. package/dist/core/statusline.js +99 -0
  300. package/dist/core/subagents/dispatcher-real.js +600 -0
  301. package/dist/core/subagents/dispatcher.js +146 -52
  302. package/dist/core/subagents/index.js +19 -6
  303. package/dist/core/subagents/isolation-matrix.js +213 -0
  304. package/dist/core/subagents/spawn.js +19 -4
  305. package/dist/core/telemetry/emitter.js +229 -0
  306. package/dist/core/telemetry/queue.js +251 -0
  307. package/dist/core/theme/context.js +91 -0
  308. package/dist/core/theme/presets.js +228 -0
  309. package/dist/core/theme/state.js +181 -0
  310. package/dist/core/todos/invariant.js +10 -0
  311. package/dist/core/todos/state.js +177 -0
  312. package/dist/core/tool-schema/compressor.js +89 -0
  313. package/dist/core/transport/version-interceptor.js +166 -0
  314. package/dist/core/trust.js +2 -2
  315. package/dist/core/tui/thinking-block.js +64 -0
  316. package/dist/core/vim/keymap.js +288 -0
  317. package/dist/core/vim/state.js +92 -0
  318. package/dist/core/watch-markers/marker-watcher.js +133 -0
  319. package/dist/core/worktree/include-parser.js +249 -0
  320. package/dist/core/worktree-manager/cleanup.js +123 -0
  321. package/dist/core/worktree-manager/manager.js +303 -0
  322. package/dist/index.js +36 -0
  323. package/dist/runtime/bootstrap.js +190 -0
  324. package/dist/runtime/cli.js +4403 -561
  325. package/dist/runtime/commands/agents.js +31 -31
  326. package/dist/runtime/commands/budget.js +5 -5
  327. package/dist/runtime/commands/cancel.js +231 -0
  328. package/dist/runtime/commands/chain.js +489 -0
  329. package/dist/runtime/commands/codegraph-status.js +227 -0
  330. package/dist/runtime/commands/compact.js +297 -0
  331. package/dist/runtime/commands/config.js +74 -40
  332. package/dist/runtime/commands/cost.js +199 -0
  333. package/dist/runtime/commands/delegate.js +27 -4
  334. package/dist/runtime/commands/dispatch.js +126 -0
  335. package/dist/runtime/commands/doctor.js +579 -0
  336. package/dist/runtime/commands/eval-v1.js +266 -0
  337. package/dist/runtime/commands/feedback.js +184 -0
  338. package/dist/runtime/commands/hooks.js +187 -0
  339. package/dist/runtime/commands/index-cmd.js +459 -0
  340. package/dist/runtime/commands/init.js +254 -0
  341. package/dist/runtime/commands/lsp.js +200 -38
  342. package/dist/runtime/commands/mcp.js +935 -0
  343. package/dist/runtime/commands/memory.js +582 -0
  344. package/dist/runtime/commands/model.js +237 -0
  345. package/dist/runtime/commands/onboarding.js +275 -0
  346. package/dist/runtime/commands/patch.js +12 -12
  347. package/dist/runtime/commands/permissions.js +112 -0
  348. package/dist/runtime/commands/plan.js +143 -0
  349. package/dist/runtime/commands/prd-check.js +285 -0
  350. package/dist/runtime/commands/privacy.js +17 -17
  351. package/dist/runtime/commands/recipe.js +325 -0
  352. package/dist/runtime/commands/redo-blob-store.js +92 -0
  353. package/dist/runtime/commands/redo.js +361 -0
  354. package/dist/runtime/commands/release-notes.js +229 -0
  355. package/dist/runtime/commands/repo-map.js +95 -0
  356. package/dist/runtime/commands/report.js +299 -0
  357. package/dist/runtime/commands/resume.js +118 -0
  358. package/dist/runtime/commands/review-consensus.js +68 -53
  359. package/dist/runtime/commands/rewind.js +333 -0
  360. package/dist/runtime/commands/roster.js +14 -14
  361. package/dist/runtime/commands/servers-cli.js +182 -0
  362. package/dist/runtime/commands/servers.js +236 -0
  363. package/dist/runtime/commands/sessions.js +163 -0
  364. package/dist/runtime/commands/share.js +316 -0
  365. package/dist/runtime/commands/skills.js +31 -31
  366. package/dist/runtime/commands/status.js +186 -0
  367. package/dist/runtime/commands/stickers.js +82 -0
  368. package/dist/runtime/commands/style.js +194 -0
  369. package/dist/runtime/commands/theme.js +196 -0
  370. package/dist/runtime/commands/undo.js +54 -22
  371. package/dist/runtime/commands/update.js +289 -0
  372. package/dist/runtime/commands/vim.js +140 -0
  373. package/dist/runtime/commands/worktree.js +8 -8
  374. package/dist/runtime/commands/worktrees.js +155 -0
  375. package/dist/runtime/deprecation-warning.js +69 -0
  376. package/dist/runtime/engine-exit-code.js +50 -0
  377. package/dist/runtime/headless-repl.js +195 -0
  378. package/dist/runtime/headless.js +548 -0
  379. package/dist/runtime/load-hooks-or-exit.js +71 -0
  380. package/dist/runtime/plan-decompose.js +22 -22
  381. package/dist/runtime/sigint-guard.js +272 -0
  382. package/dist/runtime/stream-renderer.js +195 -0
  383. package/dist/runtime/update-check.js +28 -28
  384. package/dist/runtime/version.js +65 -0
  385. package/dist/runtime/worktree-bootstrap.js +579 -0
  386. package/dist/skills/bundled/batch.js +617 -0
  387. package/dist/skills/bundled/index.js +45 -0
  388. package/dist/skills/bundled/loop.js +358 -0
  389. package/dist/skills/bundled/remember.js +383 -0
  390. package/dist/skills/bundled/simplify.js +289 -0
  391. package/dist/skills/bundled/skillify.js +373 -0
  392. package/dist/skills/bundled/stuck.js +558 -0
  393. package/dist/skills/bundled/verify.js +439 -0
  394. package/dist/testing/vcr.js +486 -0
  395. package/dist/tools/agent-tool.js +229 -0
  396. package/dist/tools/apply-patch.js +89 -28
  397. package/dist/tools/ask-user-question.js +337 -0
  398. package/dist/tools/ask-user.js +115 -0
  399. package/dist/tools/bash.js +811 -49
  400. package/dist/tools/brief.js +224 -0
  401. package/dist/tools/cron.js +433 -0
  402. package/dist/tools/enter-worktree.js +250 -0
  403. package/dist/tools/exit-worktree.js +147 -0
  404. package/dist/tools/file-tools.js +161 -44
  405. package/dist/tools/http-request.js +336 -0
  406. package/dist/tools/lsp-tools.js +377 -1
  407. package/dist/tools/mcp-tool.js +260 -0
  408. package/dist/tools/multi-edit.js +361 -0
  409. package/dist/tools/powershell.js +268 -0
  410. package/dist/tools/registry.js +120 -5
  411. package/dist/tools/server-tools.js +892 -0
  412. package/dist/tools/skill-tool.js +96 -0
  413. package/dist/tools/sleep.js +99 -0
  414. package/dist/tools/synthetic-output.js +133 -0
  415. package/dist/tools/tasks.js +208 -0
  416. package/dist/tools/todo-write.js +184 -0
  417. package/dist/tools/verify-plan-execution.js +295 -0
  418. package/dist/tools/web-fetch-injection-scanner.js +207 -0
  419. package/dist/tools/web-fetch.js +195 -10
  420. package/dist/tools/web-search.js +458 -0
  421. package/dist/tui/agent-progress-card.js +111 -0
  422. package/dist/tui/agent-tree.js +22 -1
  423. package/dist/tui/ask-modal.js +14 -14
  424. package/dist/tui/ask-user-question-chips.js +315 -0
  425. package/dist/tui/ask-user-question-prompt.js +203 -0
  426. package/dist/tui/compact-banner.js +81 -0
  427. package/dist/tui/conversation-pane.js +85 -11
  428. package/dist/tui/cost-table.js +111 -0
  429. package/dist/tui/device-flow.js +2 -2
  430. package/dist/tui/doctor-table.js +46 -0
  431. package/dist/tui/feedback-prompt.js +156 -0
  432. package/dist/tui/input-box.js +247 -32
  433. package/dist/tui/login-picker.js +3 -3
  434. package/dist/tui/markdown-render.js +6 -6
  435. package/dist/tui/multi-file-diff-approval.js +375 -0
  436. package/dist/tui/onboarding-wizard.js +240 -0
  437. package/dist/tui/permissions-picker.js +86 -0
  438. package/dist/tui/render.js +36 -1
  439. package/dist/tui/repl-render.js +239 -25
  440. package/dist/tui/repl-splash-art.js +16 -16
  441. package/dist/tui/repl-splash-mascot.js +48 -24
  442. package/dist/tui/repl-splash.js +22 -22
  443. package/dist/tui/repl.js +125 -45
  444. package/dist/tui/slash-palette.js +6 -6
  445. package/dist/tui/splash.js +2 -2
  446. package/dist/tui/status-bar.js +109 -31
  447. package/dist/tui/status-table.js +7 -0
  448. package/dist/tui/stickers-art.js +136 -0
  449. package/dist/tui/style-table.js +28 -0
  450. package/dist/tui/theme-table.js +29 -0
  451. package/dist/tui/thinking-spinner.js +123 -0
  452. package/dist/tui/tool-stream-pane.js +53 -4
  453. package/dist/tui/update-banner.js +27 -2
  454. package/dist/tui/vim-input.js +267 -0
  455. package/dist/tui/welcome-banner.js +107 -0
  456. package/dist/tui/welcome-data.js +293 -0
  457. package/dist/tui/workspace-context.js +2 -2
  458. package/package.json +29 -6
  459. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  460. package/test/scenarios/compact-force.scenario.txt +12 -0
  461. package/test/scenarios/identity.scenario.txt +11 -0
  462. package/test/scenarios/persona-handoff.scenario.txt +12 -0
  463. package/test/scenarios/walkback.scenario.txt +12 -0
  464. package/dist/core/engine/compaction-hook.js +0 -154
@@ -0,0 +1,109 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import * as path from 'node:path';
3
+ const EXT_GROUP = 'ts|tsx|js|jsx|md|json|sh|py|yaml|yml';
4
+ const BRACKET_RE = new RegExp(String.raw `\[([^\]\s]+\.(?:` + EXT_GROUP + String.raw `)):(\d+)(?:-(\d+))?\]`, 'g');
5
+ const PAREN_RE = new RegExp(String.raw `\(([^()\s]+\.(?:` + EXT_GROUP + String.raw `)):(\d+)(?:-(\d+))?\)`, 'g');
6
+ function collect(text, re, out) {
7
+ re.lastIndex = 0;
8
+ let match;
9
+ while ((match = re.exec(text)) !== null) {
10
+ const raw = match[0];
11
+ const filePath = match[1];
12
+ const lineStr = match[2];
13
+ const endStr = match[3];
14
+ if (filePath === undefined || lineStr === undefined)
15
+ continue;
16
+ const line = Number.parseInt(lineStr, 10);
17
+ if (!Number.isFinite(line))
18
+ continue;
19
+ const citation = {
20
+ raw,
21
+ path: filePath,
22
+ line,
23
+ startOffset: match.index,
24
+ endOffset: match.index + raw.length,
25
+ };
26
+ if (endStr !== undefined) {
27
+ const endLine = Number.parseInt(endStr, 10);
28
+ if (Number.isFinite(endLine))
29
+ citation.endLine = endLine;
30
+ }
31
+ out.push(citation);
32
+ }
33
+ }
34
+ export function parseCitations(text) {
35
+ const out = [];
36
+ collect(text, BRACKET_RE, out);
37
+ collect(text, PAREN_RE, out);
38
+ out.sort((a, b) => a.startOffset - b.startOffset);
39
+ return out;
40
+ }
41
+ function isTraversal(rawPath) {
42
+ if (path.isAbsolute(rawPath))
43
+ return true;
44
+ const segments = rawPath.split(/[/\\]+/);
45
+ return segments.includes('..');
46
+ }
47
+ function countLines(content) {
48
+ if (content.length === 0)
49
+ return 0;
50
+ const matches = content.match(/\n/g);
51
+ const nl = matches === null ? 0 : matches.length;
52
+ return content.endsWith('\n') ? nl : nl + 1;
53
+ }
54
+ export async function validateCitations(citations, workspaceRoot) {
55
+ const rootReal = await fs.realpath(workspaceRoot);
56
+ const results = [];
57
+ for (const citation of citations) {
58
+ if (isTraversal(citation.path)) {
59
+ results.push({ citation, valid: false, reason: 'path-traversal' });
60
+ continue;
61
+ }
62
+ const resolved = path.resolve(rootReal, citation.path);
63
+ let realResolved;
64
+ try {
65
+ realResolved = await fs.realpath(resolved);
66
+ }
67
+ catch {
68
+ results.push({ citation, valid: false, reason: 'file-missing' });
69
+ continue;
70
+ }
71
+ const rel = path.relative(rootReal, realResolved);
72
+ if (rel.startsWith('..') || path.isAbsolute(rel)) {
73
+ results.push({ citation, valid: false, reason: 'outside-workspace' });
74
+ continue;
75
+ }
76
+ let stat;
77
+ try {
78
+ stat = await fs.stat(realResolved);
79
+ }
80
+ catch {
81
+ results.push({ citation, valid: false, reason: 'file-missing' });
82
+ continue;
83
+ }
84
+ if (!stat.isFile()) {
85
+ results.push({ citation, valid: false, reason: 'file-missing' });
86
+ continue;
87
+ }
88
+ let content;
89
+ try {
90
+ content = await fs.readFile(realResolved, 'utf8');
91
+ }
92
+ catch {
93
+ results.push({ citation, valid: false, reason: 'file-missing' });
94
+ continue;
95
+ }
96
+ const totalLines = countLines(content);
97
+ const lastLine = citation.endLine ?? citation.line;
98
+ const rangeInvalid = citation.line < 1 ||
99
+ lastLine < citation.line ||
100
+ lastLine > totalLines;
101
+ if (rangeInvalid) {
102
+ results.push({ citation, valid: false, reason: 'line-out-of-range' });
103
+ continue;
104
+ }
105
+ results.push({ citation, valid: true });
106
+ }
107
+ return results;
108
+ }
109
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1,88 @@
1
+ export const DEFAULT_RULES = [
2
+ { name: 'rm-recursive-force', pattern: /\brm\s+(-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r|-rf|-fr)\b/, kind: 'destructive', baseConfidence: 0.95 },
3
+ { name: 'sudo-prefix', pattern: /^\s*sudo\b/, kind: 'destructive', baseConfidence: 0.85 },
4
+ { name: 'git-force-push', pattern: /\bgit\s+push\b.*--force\b|\bgit\s+push\b.*\s-f\b/, kind: 'destructive', baseConfidence: 0.92 },
5
+ { name: 'drop-table', pattern: /\bDROP\s+(TABLE|DATABASE|SCHEMA)\b/i, kind: 'destructive', baseConfidence: 0.95 },
6
+ { name: 'mkfs', pattern: /\bmkfs(\.\w+)?\b/, kind: 'destructive', baseConfidence: 0.95 },
7
+ { name: 'dd-of-device', pattern: /\bdd\b.*\bof=\/dev\//, kind: 'destructive', baseConfidence: 0.95 },
8
+ { name: 'chmod-recursive-permissive', pattern: /\bchmod\s+-R\s+777\b/, kind: 'risky', baseConfidence: 0.8 },
9
+ { name: 'curl-pipe-shell', pattern: /\bcurl\b[^|]*\|\s*(sudo\s+)?(ba)?sh\b/, kind: 'risky', baseConfidence: 0.85 },
10
+ { name: 'ls-listing', pattern: /^\s*ls(\s|$)/, kind: 'safe', baseConfidence: 0.9 },
11
+ { name: 'cd-navigate', pattern: /^\s*cd(\s|$)/, kind: 'safe', baseConfidence: 0.9 },
12
+ { name: 'cat-read', pattern: /^\s*cat\s+/, kind: 'safe', baseConfidence: 0.88 },
13
+ { name: 'echo-print', pattern: /^\s*echo\s+/, kind: 'safe', baseConfidence: 0.9 },
14
+ { name: 'pwd-print', pattern: /^\s*pwd\s*$/, kind: 'safe', baseConfidence: 0.95 },
15
+ { name: 'git-status', pattern: /^\s*git\s+(status|log|diff|branch|show)\b/, kind: 'safe', baseConfidence: 0.9 },
16
+ ];
17
+ const DEFAULT_AMBIGUITY_THRESHOLD = 0.7;
18
+ function parseLlmResponse(raw) {
19
+ let parsed;
20
+ try {
21
+ parsed = JSON.parse(raw);
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ if (!parsed || typeof parsed !== 'object')
27
+ return null;
28
+ const kind = parsed.kind;
29
+ const confidence = parsed.confidence;
30
+ if (kind !== 'safe' && kind !== 'risky' && kind !== 'destructive' && kind !== 'unknown') {
31
+ return null;
32
+ }
33
+ if (typeof confidence !== 'number' || !Number.isFinite(confidence)) {
34
+ return null;
35
+ }
36
+ const clamped = Math.max(0, Math.min(1, confidence));
37
+ return { kind, confidence: clamped };
38
+ }
39
+ function matchRules(input, rules) {
40
+ for (const rule of rules) {
41
+ if (rule.pattern.test(input)) {
42
+ return { rule };
43
+ }
44
+ }
45
+ return null;
46
+ }
47
+ export async function classifyInput(input, options = {}) {
48
+ const rules = options.rules ?? DEFAULT_RULES;
49
+ const threshold = options.ambiguityThreshold ?? DEFAULT_AMBIGUITY_THRESHOLD;
50
+ const transport = options.transport;
51
+ const match = matchRules(input, rules);
52
+ if (match && match.rule.baseConfidence >= threshold) {
53
+ return {
54
+ kind: match.rule.kind,
55
+ confidence: match.rule.baseConfidence,
56
+ stage: 'regex',
57
+ matchedRule: match.rule.name,
58
+ };
59
+ }
60
+ if (!transport) {
61
+ if (match) {
62
+ return {
63
+ kind: match.rule.kind,
64
+ confidence: match.rule.baseConfidence,
65
+ stage: 'regex',
66
+ matchedRule: match.rule.name,
67
+ };
68
+ }
69
+ return { kind: 'unknown', confidence: 0, stage: 'fallback' };
70
+ }
71
+ let raw;
72
+ try {
73
+ raw = await transport(input);
74
+ }
75
+ catch {
76
+ return { kind: 'unknown', confidence: 0, stage: 'fallback' };
77
+ }
78
+ const parsed = parseLlmResponse(raw);
79
+ if (!parsed) {
80
+ return { kind: 'unknown', confidence: 0, stage: 'fallback' };
81
+ }
82
+ return {
83
+ kind: parsed.kind,
84
+ confidence: parsed.confidence,
85
+ stage: 'llm',
86
+ };
87
+ }
88
+ //# sourceMappingURL=yolo-classifier.js.map
@@ -0,0 +1,506 @@
1
+ /**
2
+ * Pugi local symbol index - SQLite + FTS5 wrapper.
3
+ *
4
+ * Persists the symbol / edge / file tables that the parser (PR L1) and
5
+ * watcher (PR L2) write into, plus the FTS5 mirror that powers
6
+ * `code_search`. The DB file lives at `<workspaceRoot>/.pugi/codegraph.db`
7
+ * and is regenerable - losing it costs one re-parse, never customer data.
8
+ *
9
+ * Driver choice - `node:sqlite`, NOT `better-sqlite3`:
10
+ *
11
+ * - The session-store at `apps/pugi-cli/src/core/repl/store/session-store.ts`
12
+ * (the only prior SQLite consumer inside @pugi/cli) deliberately uses
13
+ * the built-in `node:sqlite` module instead of the better-sqlite3
14
+ * npm package. Reason documented there at lines 20-36: better-sqlite3
15
+ * needs a prebuilt native binary per Node ABI × platform, and missing
16
+ * a wheel triggers a node-gyp compile that fails on customer machines
17
+ * that lack build-essential. Pugi CLI ships via `npm i -g @pugi/cli`
18
+ * to operators who almost certainly do not have a C++ toolchain.
19
+ * - `engines.node >= 22.5.0` already pins the minimum runtime the
20
+ * `node:sqlite` module is available on (stable subset since 22.5).
21
+ * The CLI refuses to install on Node 20.x instead of crash-on-import.
22
+ * - The task spec mentions better-sqlite3 by name; we deviate here so
23
+ * the symbol index inherits the same zero-native-build property the
24
+ * session store already proved out in production. Surfaced in the
25
+ * PR body so the reviewer can re-litigate.
26
+ *
27
+ * The module is structured as standalone functions, not a class, so the
28
+ * MCP tool layer (PR L3) can compose them without instantiating a stateful
29
+ * object. Connection lifetime is the caller's responsibility - open at the
30
+ * start of an operation, close at the end, never share across processes.
31
+ */
32
+ import { DatabaseSync } from 'node:sqlite';
33
+ import { existsSync, mkdirSync } from 'node:fs';
34
+ import { dirname, resolve } from 'node:path';
35
+ import { INDEX_SCHEMA_VERSION, } from './types.js';
36
+ /**
37
+ * Default search limit applied when `SearchOptions.limit` is absent.
38
+ * Mirrors the session-store convention (50 list / 20 search). 50 chosen
39
+ * here because callers expect at-least-one full screen of matches.
40
+ */
41
+ const DEFAULT_SEARCH_LIMIT = 50;
42
+ /** Hard cap on search limit. Protects response payload size. */
43
+ const MAX_SEARCH_LIMIT = 500;
44
+ /** Hard cap on trace depth. Five hops is well past usable. */
45
+ const MAX_TRACE_DEPTH = 5;
46
+ /** Hard cap on trace node count. Stops runaway BFS on densely-connected graphs. */
47
+ const MAX_TRACE_NODES = 200;
48
+ /** Hard cap on disambiguation candidates in DefinitionResult. */
49
+ const MAX_DEFINITION_CANDIDATES = 25;
50
+ /**
51
+ * Initial schema - applied once at first `openIndex`. Future migrations
52
+ * append a new branch in `applyMigration` and bump `INDEX_SCHEMA_VERSION`.
53
+ *
54
+ * FTS5 contentless mirror pattern (`content='symbols'`,
55
+ * `content_rowid='id'`) keeps storage flat - the FTS5 virtual table
56
+ * stores only the inverted index, not duplicated columns. Triggers below
57
+ * keep it in sync with INSERT / UPDATE / DELETE on `symbols`.
58
+ *
59
+ * `kind` columns carry CHECK constraints that mirror the closed
60
+ * SymbolKind / EdgeKind unions in types.ts. Diverging the runtime check
61
+ * from the TS type without a schema migration WILL silently insert
62
+ * unknown values; the CHECK constraint is the safety belt.
63
+ */
64
+ const SCHEMA_V1_SQL = `
65
+ CREATE TABLE files (
66
+ path TEXT PRIMARY KEY,
67
+ sha256 TEXT NOT NULL,
68
+ last_indexed_at TEXT NOT NULL,
69
+ symbol_count INTEGER NOT NULL DEFAULT 0
70
+ );
71
+
72
+ CREATE TABLE symbols (
73
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
74
+ name TEXT NOT NULL,
75
+ kind TEXT NOT NULL CHECK (kind IN ('function','class','method','interface','type','variable','import')),
76
+ file TEXT NOT NULL,
77
+ line INTEGER NOT NULL,
78
+ col INTEGER NOT NULL,
79
+ scope TEXT NOT NULL DEFAULT '',
80
+ signature TEXT
81
+ );
82
+ CREATE INDEX idx_symbols_name ON symbols(name);
83
+ CREATE INDEX idx_symbols_file ON symbols(file);
84
+ CREATE INDEX idx_symbols_kind ON symbols(kind);
85
+
86
+ CREATE TABLE edges (
87
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
88
+ from_symbol_id INTEGER NOT NULL,
89
+ to_symbol_id INTEGER NOT NULL,
90
+ kind TEXT NOT NULL CHECK (kind IN ('calls','extends','implements','imports','references')),
91
+ FOREIGN KEY (from_symbol_id) REFERENCES symbols(id) ON DELETE CASCADE,
92
+ FOREIGN KEY (to_symbol_id) REFERENCES symbols(id) ON DELETE CASCADE
93
+ );
94
+ CREATE INDEX idx_edges_from ON edges(from_symbol_id);
95
+ CREATE INDEX idx_edges_to ON edges(to_symbol_id);
96
+ CREATE INDEX idx_edges_kind ON edges(kind);
97
+
98
+ CREATE VIRTUAL TABLE symbols_fts USING fts5(
99
+ name,
100
+ scope,
101
+ signature,
102
+ content='symbols',
103
+ content_rowid='id',
104
+ tokenize='unicode61 remove_diacritics 1'
105
+ );
106
+
107
+ CREATE TRIGGER symbols_ai AFTER INSERT ON symbols BEGIN
108
+ INSERT INTO symbols_fts(rowid, name, scope, signature)
109
+ VALUES (new.id, new.name, new.scope, COALESCE(new.signature, ''));
110
+ END;
111
+
112
+ CREATE TRIGGER symbols_ad AFTER DELETE ON symbols BEGIN
113
+ INSERT INTO symbols_fts(symbols_fts, rowid, name, scope, signature)
114
+ VALUES ('delete', old.id, old.name, old.scope, COALESCE(old.signature, ''));
115
+ END;
116
+
117
+ CREATE TRIGGER symbols_au AFTER UPDATE ON symbols BEGIN
118
+ INSERT INTO symbols_fts(symbols_fts, rowid, name, scope, signature)
119
+ VALUES ('delete', old.id, old.name, old.scope, COALESCE(old.signature, ''));
120
+ INSERT INTO symbols_fts(rowid, name, scope, signature)
121
+ VALUES (new.id, new.name, new.scope, COALESCE(new.signature, ''));
122
+ END;
123
+
124
+ CREATE TABLE _migrations (
125
+ version INTEGER PRIMARY KEY,
126
+ applied_at TEXT NOT NULL
127
+ );
128
+ `;
129
+ /**
130
+ * Resolve `<workspaceRoot>/.pugi/codegraph.db`. Exported so the doctor +
131
+ * status surfaces can print the location without duplicating the path
132
+ * computation. Does NOT create the parent directory; that is the
133
+ * caller's responsibility (openIndex handles it).
134
+ */
135
+ export function resolveIndexPath(workspaceRoot) {
136
+ return resolve(workspaceRoot, '.pugi', 'codegraph.db');
137
+ }
138
+ /**
139
+ * Open or create the index DB at `<workspaceRoot>/.pugi/codegraph.db`.
140
+ *
141
+ * Idempotent: a second call against the same workspace returns a fresh
142
+ * connection but the schema (already applied) is untouched. The caller
143
+ * is responsible for `closeIndex` - leaking handles is a chokidar PR L2
144
+ * concern (the watcher holds the connection for the process lifetime).
145
+ *
146
+ * Throws on:
147
+ * - workspaceRoot does not exist
148
+ * - `.pugi/` parent cannot be created (EACCES)
149
+ * - SQLite cannot open the file
150
+ * - migration SQL fails
151
+ */
152
+ export function openIndex(workspaceRoot) {
153
+ const abs = resolve(workspaceRoot);
154
+ if (!existsSync(abs)) {
155
+ throw new Error(`workspaceRoot does not exist: ${abs}`);
156
+ }
157
+ const dbPath = resolveIndexPath(abs);
158
+ const parent = dirname(dbPath);
159
+ if (!existsSync(parent)) {
160
+ mkdirSync(parent, { recursive: true, mode: 0o700 });
161
+ }
162
+ const conn = new DatabaseSync(dbPath);
163
+ // PRAGMA foreign_keys is OFF by default in SQLite; turn it on so the
164
+ // ON DELETE CASCADE in the edges table actually fires when a symbol
165
+ // row is removed.
166
+ conn.exec('PRAGMA foreign_keys = ON;');
167
+ // WAL mode for read-during-write concurrency. The watcher (PR L2)
168
+ // re-parses files in batches; queries from the MCP tool layer must
169
+ // not block on the write transaction.
170
+ conn.exec('PRAGMA journal_mode = WAL;');
171
+ const version = ensureSchema(conn);
172
+ return { conn, workspaceRoot: abs, dbPath, version };
173
+ }
174
+ /**
175
+ * Close the connection. Safe to call multiple times - node:sqlite's
176
+ * `close()` is idempotent.
177
+ */
178
+ export function closeIndex(db) {
179
+ db.conn.close();
180
+ }
181
+ /**
182
+ * Apply migrations from the current applied version up to
183
+ * INDEX_SCHEMA_VERSION. Returns the version actually applied. The
184
+ * `_migrations` table itself is created lazily inside the v1 schema -
185
+ * on a fresh DB we detect that by probing for the `symbols` table.
186
+ */
187
+ function ensureSchema(conn) {
188
+ const hasMigrationsTable = tableExists(conn, '_migrations');
189
+ if (!hasMigrationsTable) {
190
+ // Fresh DB - apply v1 in one transaction so a mid-script error
191
+ // never leaves us with half the schema.
192
+ conn.exec('BEGIN;');
193
+ try {
194
+ conn.exec(SCHEMA_V1_SQL);
195
+ conn
196
+ .prepare('INSERT INTO _migrations(version, applied_at) VALUES (?, ?)')
197
+ .run(1, new Date().toISOString());
198
+ conn.exec('COMMIT;');
199
+ }
200
+ catch (err) {
201
+ conn.exec('ROLLBACK;');
202
+ throw err;
203
+ }
204
+ return 1;
205
+ }
206
+ const row = conn
207
+ .prepare('SELECT MAX(version) AS version FROM _migrations')
208
+ .get();
209
+ const current = row?.version ?? 0;
210
+ // Future migrations: loop from current+1 to INDEX_SCHEMA_VERSION and
211
+ // call applyMigration(n). v1 is the only version this PR ships.
212
+ if (current < INDEX_SCHEMA_VERSION) {
213
+ for (let v = current + 1; v <= INDEX_SCHEMA_VERSION; v += 1) {
214
+ applyMigration(conn, v);
215
+ }
216
+ }
217
+ return INDEX_SCHEMA_VERSION;
218
+ }
219
+ /**
220
+ * Apply one numbered migration. v1 is handled inline in `ensureSchema`
221
+ * (fresh DB branch). This function is the extension point for v2+.
222
+ */
223
+ function applyMigration(_conn, version) {
224
+ // Reserved for PR L2+ migrations.
225
+ throw new Error(`migration ${version} not implemented in this PR`);
226
+ }
227
+ function tableExists(conn, name) {
228
+ const row = conn
229
+ .prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name = ?`)
230
+ .get(name);
231
+ return row !== undefined;
232
+ }
233
+ /**
234
+ * Insert a batch of symbols. Runs in one transaction so a partial
235
+ * failure rolls back cleanly; the FTS5 mirror is updated via triggers
236
+ * declared in v1. Returns the assigned row ids in the same order as
237
+ * the input array - callers (parser.ts) use these ids to build the
238
+ * subsequent edge list.
239
+ *
240
+ * Idempotency: this layer does NOT dedupe. Callers (PR L1 parser) are
241
+ * responsible for deleting prior rows of the same file before re-inserting.
242
+ * The schema's `idx_symbols_file` index makes the `DELETE FROM symbols
243
+ * WHERE file = ?` pattern fast.
244
+ */
245
+ export function insertSymbols(db, symbols) {
246
+ if (symbols.length === 0)
247
+ return [];
248
+ const stmt = db.conn.prepare(`INSERT INTO symbols(name, kind, file, line, col, scope, signature)
249
+ VALUES (?, ?, ?, ?, ?, ?, ?)
250
+ RETURNING id`);
251
+ const ids = [];
252
+ db.conn.exec('BEGIN;');
253
+ try {
254
+ for (const s of symbols) {
255
+ const row = stmt.get(s.name, s.kind, s.file, s.line, s.column, s.scope, s.signature ?? null);
256
+ if (!row)
257
+ throw new Error('insertSymbols: RETURNING id missing');
258
+ ids.push(row.id);
259
+ }
260
+ db.conn.exec('COMMIT;');
261
+ }
262
+ catch (err) {
263
+ db.conn.exec('ROLLBACK;');
264
+ throw err;
265
+ }
266
+ return ids;
267
+ }
268
+ /**
269
+ * Insert a batch of edges. Single transaction; orphan edges (endpoints
270
+ * missing from `symbols`) raise a FOREIGN KEY constraint failure that
271
+ * rolls back the whole batch - callers should pre-filter on the
272
+ * caller-side ID map produced by `insertSymbols`.
273
+ */
274
+ export function insertEdges(db, edges) {
275
+ if (edges.length === 0)
276
+ return;
277
+ const stmt = db.conn.prepare(`INSERT INTO edges(from_symbol_id, to_symbol_id, kind) VALUES (?, ?, ?)`);
278
+ db.conn.exec('BEGIN;');
279
+ try {
280
+ for (const e of edges) {
281
+ stmt.run(e.fromSymbolId, e.toSymbolId, e.kind);
282
+ }
283
+ db.conn.exec('COMMIT;');
284
+ }
285
+ catch (err) {
286
+ db.conn.exec('ROLLBACK;');
287
+ throw err;
288
+ }
289
+ }
290
+ /**
291
+ * Upsert a file fingerprint row. The watcher (PR L2) calls this after
292
+ * re-parsing a file so subsequent runs can skip unchanged content.
293
+ */
294
+ export function upsertFile(db, file) {
295
+ db.conn
296
+ .prepare(`INSERT INTO files(path, sha256, last_indexed_at, symbol_count)
297
+ VALUES (?, ?, ?, ?)
298
+ ON CONFLICT(path) DO UPDATE SET
299
+ sha256 = excluded.sha256,
300
+ last_indexed_at = excluded.last_indexed_at,
301
+ symbol_count = excluded.symbol_count`)
302
+ .run(file.path, file.sha256, file.lastIndexedAt, file.symbolCount);
303
+ }
304
+ /**
305
+ * FTS5 search on the contentless mirror. Returns symbols ranked by
306
+ * BM25 (lower is better). The query string is passed through to FTS5
307
+ * verbatim - callers wanting prefix match append `*`, callers wanting
308
+ * a phrase quote it. Invalid FTS5 syntax raises a SqliteError which
309
+ * we surface as 'invalid_query'.
310
+ *
311
+ * `kindFilter` is applied via a secondary JOIN - FTS5 itself does not
312
+ * know about the `kind` column.
313
+ */
314
+ export function searchSymbols(db, query, options = {}) {
315
+ if (query.trim().length === 0)
316
+ return [];
317
+ const limit = clamp(options.limit ?? DEFAULT_SEARCH_LIMIT, 1, MAX_SEARCH_LIMIT);
318
+ const kindFilter = options.kindFilter ?? null;
319
+ // Build the kind filter dynamically if present; FTS5 + an indexed
320
+ // column lookup composes naturally via the rowid join.
321
+ const kindClause = kindFilter && kindFilter.length > 0
322
+ ? ` AND s.kind IN (${kindFilter.map(() => '?').join(',')})`
323
+ : '';
324
+ const sql = `SELECT s.id, s.name, s.kind, s.file, s.line, s.col, s.scope, s.signature, bm25(symbols_fts) AS rank
325
+ FROM symbols_fts
326
+ JOIN symbols s ON s.id = symbols_fts.rowid
327
+ WHERE symbols_fts MATCH ?${kindClause}
328
+ ORDER BY rank
329
+ LIMIT ?`;
330
+ // node:sqlite's StatementSync.all() accepts a variadic of SQLiteAnonymousBoundValue
331
+ // (string | number | bigint | null | Buffer | Uint8Array). Our params are all
332
+ // string | number - cast through `unknown` to satisfy TS's variadic spread.
333
+ const sqliteParams = [query];
334
+ if (kindFilter && kindFilter.length > 0) {
335
+ sqliteParams.push(...kindFilter);
336
+ }
337
+ sqliteParams.push(limit);
338
+ const rows = db.conn.prepare(sql).all(...sqliteParams);
339
+ return rows.map((r) => ({
340
+ symbol: rowToSymbol(r),
341
+ rank: r.rank,
342
+ }));
343
+ }
344
+ /**
345
+ * Exact-name definition lookup. Returns the canonical definition
346
+ * (lowest line number wins when multiple match; arbitrary but stable)
347
+ * plus up to `MAX_DEFINITION_CANDIDATES` disambiguations so the agent
348
+ * can pick a different one without a second query.
349
+ */
350
+ export function findDefinition(db, name) {
351
+ const rows = db.conn
352
+ .prepare(`SELECT id, name, kind, file, line, col, scope, signature
353
+ FROM symbols
354
+ WHERE name = ?
355
+ ORDER BY
356
+ CASE kind
357
+ WHEN 'function' THEN 1
358
+ WHEN 'class' THEN 2
359
+ WHEN 'method' THEN 3
360
+ WHEN 'interface' THEN 4
361
+ WHEN 'type' THEN 5
362
+ WHEN 'variable' THEN 6
363
+ WHEN 'import' THEN 7
364
+ END,
365
+ line ASC
366
+ LIMIT ?`)
367
+ .all(name, MAX_DEFINITION_CANDIDATES + 1);
368
+ if (rows.length === 0) {
369
+ return { kind: 'not_found', name };
370
+ }
371
+ const [first, ...rest] = rows;
372
+ if (!first)
373
+ return { kind: 'not_found', name };
374
+ return {
375
+ kind: 'found',
376
+ definition: rowToSymbol(first),
377
+ disambiguations: rest.slice(0, MAX_DEFINITION_CANDIDATES).map(rowToSymbol),
378
+ };
379
+ }
380
+ /**
381
+ * Reverse-edge query: every symbol whose outbound edge lands on
382
+ * `symbolId`. Filters to `calls` / `extends` / `implements` /
383
+ * `references` - `imports` is excluded because import edges fire on
384
+ * every transitive use, drowning the result set. Callers wanting
385
+ * imports specifically can query the `edges` table directly via the
386
+ * exposed `db.conn` handle (PR L3 wires a dedicated tool).
387
+ */
388
+ export function findCallers(db, symbolId) {
389
+ const rows = db.conn
390
+ .prepare(`SELECT s.id, s.name, s.kind, s.file, s.line, s.col, s.scope, s.signature,
391
+ e.id AS edge_id, e.from_symbol_id, e.to_symbol_id, e.kind AS edge_kind
392
+ FROM edges e
393
+ JOIN symbols s ON s.id = e.from_symbol_id
394
+ WHERE e.to_symbol_id = ?
395
+ AND e.kind IN ('calls','extends','implements','references')
396
+ ORDER BY s.file ASC, s.line ASC`)
397
+ .all(symbolId);
398
+ return rows.map((r) => ({
399
+ caller: rowToSymbol(r),
400
+ edge: {
401
+ fromSymbolId: r.from_symbol_id,
402
+ toSymbolId: r.to_symbol_id,
403
+ kind: r.edge_kind,
404
+ },
405
+ }));
406
+ }
407
+ /**
408
+ * Bounded BFS outward from `rootSymbolId` over `calls` edges. Real
409
+ * implementation in PR L3 (the MCP tool layer composes this with the
410
+ * agent loop); here we ship a minimal version that returns the root
411
+ * symbol with zero outbound edges + a depth marker of 0 so the schema
412
+ * roundtrips. Caps documented at module scope.
413
+ */
414
+ export function traceSymbol(db, rootSymbolId, depth) {
415
+ const clampedDepth = clamp(depth, 1, MAX_TRACE_DEPTH);
416
+ const root = db.conn
417
+ .prepare(`SELECT id, name, kind, file, line, col, scope, signature
418
+ FROM symbols WHERE id = ?`)
419
+ .get(rootSymbolId);
420
+ if (!root) {
421
+ return {
422
+ rootSymbolId,
423
+ nodes: [],
424
+ edges: [],
425
+ depthReached: 0,
426
+ truncated: false,
427
+ };
428
+ }
429
+ // Real BFS implementation lands in PR L3. The scaffold returns the
430
+ // root only so callers can wire the response envelope without waiting
431
+ // for the parser.
432
+ return {
433
+ rootSymbolId,
434
+ nodes: [rowToSymbol(root)],
435
+ edges: [],
436
+ depthReached: 0,
437
+ truncated: false,
438
+ // clampedDepth is recorded but unused until PR L3; surface for the
439
+ // future MCP tool body. Reading it here also keeps TS happy about
440
+ // the unused-arg lint.
441
+ ...{ requestedDepth: clampedDepth },
442
+ };
443
+ }
444
+ /**
445
+ * Look up an indexed file by workspace-relative path. Returns null
446
+ * when the watcher has not seen this file yet. The watcher (PR L2)
447
+ * uses this to skip re-parsing unchanged files via `sha256` compare.
448
+ */
449
+ export function findIndexedFile(db, path) {
450
+ const row = db.conn
451
+ .prepare(`SELECT path, sha256, last_indexed_at AS lastIndexedAt, symbol_count AS symbolCount
452
+ FROM files WHERE path = ?`)
453
+ .get(path);
454
+ return row ?? null;
455
+ }
456
+ /**
457
+ * Count of indexed symbols across all files. Surfaced by `pugi index`
458
+ * status output + `/codegraph-status` once PR L3 wires it. Constant-time
459
+ * via SQLite's COUNT estimate.
460
+ */
461
+ export function countSymbols(db) {
462
+ const row = db.conn
463
+ .prepare(`SELECT COUNT(*) AS count FROM symbols`)
464
+ .get();
465
+ return row?.count ?? 0;
466
+ }
467
+ /**
468
+ * Delete every symbol + edge belonging to one file. The watcher calls
469
+ * this before re-inserting freshly-parsed rows so the index reflects
470
+ * the current state of disk. Edges to other-file symbols disappear via
471
+ * the FOREIGN KEY CASCADE.
472
+ */
473
+ export function deleteFile(db, path) {
474
+ db.conn.exec('BEGIN;');
475
+ try {
476
+ db.conn.prepare('DELETE FROM symbols WHERE file = ?').run(path);
477
+ db.conn.prepare('DELETE FROM files WHERE path = ?').run(path);
478
+ db.conn.exec('COMMIT;');
479
+ }
480
+ catch (err) {
481
+ db.conn.exec('ROLLBACK;');
482
+ throw err;
483
+ }
484
+ }
485
+ function rowToSymbol(r) {
486
+ const out = {
487
+ id: r.id,
488
+ name: r.name,
489
+ kind: r.kind,
490
+ file: r.file,
491
+ line: r.line,
492
+ column: r.col,
493
+ scope: r.scope,
494
+ };
495
+ if (r.signature !== null)
496
+ out.signature = r.signature;
497
+ return out;
498
+ }
499
+ function clamp(n, lo, hi) {
500
+ if (n < lo)
501
+ return lo;
502
+ if (n > hi)
503
+ return hi;
504
+ return n;
505
+ }
506
+ //# sourceMappingURL=db.js.map