@pugi/cli 0.1.0-beta.9 → 0.1.0-beta.91

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 (411) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/LICENSE +1 -1
  3. package/assets/pugi-prozr2-mascot.ansi +9 -0
  4. package/bin/run.js +33 -1
  5. package/dist/commands/deploy.js +40 -40
  6. package/dist/commands/flatten.js +191 -0
  7. package/dist/commands/jobs-watch.js +201 -0
  8. package/dist/commands/jobs.js +42 -27
  9. package/dist/commands/smoke.js +133 -0
  10. package/dist/core/agent-progress/cleanup.js +134 -0
  11. package/dist/core/agent-progress/schema.js +144 -0
  12. package/dist/core/agent-progress/writer.js +101 -0
  13. package/dist/core/agents/adaptive-router.js +330 -0
  14. package/dist/core/agents/query-decomposer.js +297 -0
  15. package/dist/core/agents/registry.js +3 -3
  16. package/dist/core/approvals/shortcut-resolver.js +98 -0
  17. package/dist/core/artifact-chain/dispatcher.js +148 -0
  18. package/dist/core/artifact-chain/exporter.js +164 -0
  19. package/dist/core/artifact-chain/state.js +243 -0
  20. package/dist/core/artifact-chain/steps.js +169 -0
  21. package/dist/core/ask-user/question.js +92 -0
  22. package/dist/core/audit/audit-trail.js +275 -0
  23. package/dist/core/auth/ensure-authenticated.js +129 -0
  24. package/dist/core/auth/env-provider.js +238 -0
  25. package/dist/core/auto-open-browser.js +4 -4
  26. package/dist/core/auto-update/channels.js +122 -0
  27. package/dist/core/auto-update/checker.js +241 -0
  28. package/dist/core/auto-update/state.js +235 -0
  29. package/dist/core/bare-mode/index.js +107 -0
  30. package/dist/core/bash/redirect.js +281 -0
  31. package/dist/core/bash-classifier.js +436 -40
  32. package/dist/core/checkpoint/resumer.js +149 -0
  33. package/dist/core/checkpoint/rewinder.js +291 -0
  34. package/dist/core/checkpoints/shadow-git.js +670 -0
  35. package/dist/core/citations/parser.js +109 -0
  36. package/dist/core/classifier/yolo-classifier.js +88 -0
  37. package/dist/core/codegraph/decision-store.js +248 -0
  38. package/dist/core/codegraph/detect-repo.js +459 -0
  39. package/dist/core/codegraph/install.js +134 -0
  40. package/dist/core/codegraph/offer-hook.js +220 -0
  41. package/dist/core/compact/auto-trigger.js +96 -0
  42. package/dist/core/compact/buffer-rewriter.js +115 -0
  43. package/dist/core/compact/summarizer.js +208 -0
  44. package/dist/core/compact/token-counter.js +108 -0
  45. package/dist/core/consensus/anvil-fanout.js +25 -25
  46. package/dist/core/consensus/diff-capture.js +121 -12
  47. package/dist/core/consensus/rubric.js +21 -21
  48. package/dist/core/context/builder.js +6 -6
  49. package/dist/core/context/compaction-events.js +8 -8
  50. package/dist/core/context/compaction.js +31 -31
  51. package/dist/core/context/index.js +15 -8
  52. package/dist/core/context/invariants.js +51 -51
  53. package/dist/core/context/markdown-loader.js +28 -10
  54. package/dist/core/context/markdown-traverse.js +255 -0
  55. package/dist/core/context/pugiignore.js +41 -41
  56. package/dist/core/context/repo-skeleton.js +37 -37
  57. package/dist/core/context/tool-eviction.js +55 -0
  58. package/dist/core/context/watcher.js +32 -32
  59. package/dist/core/context/working-set.js +23 -23
  60. package/dist/core/coordinator/agent-tools.js +77 -0
  61. package/dist/core/coordinator/agent-toolset.js +65 -0
  62. package/dist/core/coordinator/fsm.js +73 -0
  63. package/dist/core/coordinator/mode-fsm.js +70 -0
  64. package/dist/core/cost/rate-card.js +129 -0
  65. package/dist/core/cost/tracker.js +221 -0
  66. package/dist/core/credentials.js +13 -13
  67. package/dist/core/cron/scheduler.js +138 -0
  68. package/dist/core/denial-tracking/index.js +8 -0
  69. package/dist/core/denial-tracking/state.js +264 -0
  70. package/dist/core/diagnostics/probe-runner.js +93 -0
  71. package/dist/core/diagnostics/probes/api.js +46 -0
  72. package/dist/core/diagnostics/probes/auth.js +93 -0
  73. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  74. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  75. package/dist/core/diagnostics/probes/config.js +72 -0
  76. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  77. package/dist/core/diagnostics/probes/disk.js +81 -0
  78. package/dist/core/diagnostics/probes/engine-live.js +46 -0
  79. package/dist/core/diagnostics/probes/git.js +65 -0
  80. package/dist/core/diagnostics/probes/hooks.js +118 -0
  81. package/dist/core/diagnostics/probes/mcp.js +75 -0
  82. package/dist/core/diagnostics/probes/node.js +59 -0
  83. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  84. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  85. package/dist/core/diagnostics/probes/sandbox.js +40 -0
  86. package/dist/core/diagnostics/probes/session.js +74 -0
  87. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  88. package/dist/core/diagnostics/probes/workspace.js +63 -0
  89. package/dist/core/diagnostics/types.js +70 -0
  90. package/dist/core/dispatch/cache-cleanup.js +197 -0
  91. package/dist/core/dispatch/cache-handoff.js +295 -0
  92. package/dist/core/edits/apply-patch-layer-e.js +189 -0
  93. package/dist/core/edits/dispatch.js +333 -7
  94. package/dist/core/edits/format-detector.js +260 -0
  95. package/dist/core/edits/format-matrix.js +26 -0
  96. package/dist/core/edits/fuzzy-ladder.js +650 -0
  97. package/dist/core/edits/index.js +5 -1
  98. package/dist/core/edits/journal.js +199 -0
  99. package/dist/core/edits/layer-a-apply.js +15 -15
  100. package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
  101. package/dist/core/edits/layer-b-apply.js +9 -9
  102. package/dist/core/edits/layer-c-apply.js +6 -6
  103. package/dist/core/edits/layer-d-ast.js +557 -14
  104. package/dist/core/edits/marker-parser.js +12 -12
  105. package/dist/core/edits/security-gate.js +27 -27
  106. package/dist/core/edits/verify-hook.js +273 -0
  107. package/dist/core/edits/worktree.js +29 -29
  108. package/dist/core/engine/anvil-client.js +214 -26
  109. package/dist/core/engine/auto-compact.js +179 -0
  110. package/dist/core/engine/budgets.js +186 -0
  111. package/dist/core/engine/context-prefix.js +155 -0
  112. package/dist/core/engine/index.js +1 -1
  113. package/dist/core/engine/intensity.js +158 -0
  114. package/dist/core/engine/intent.js +260 -0
  115. package/dist/core/engine/native-pugi.js +1295 -227
  116. package/dist/core/engine/prompts.js +129 -19
  117. package/dist/core/engine/strip-internal-fields.js +124 -0
  118. package/dist/core/engine/tool-bridge.js +1792 -59
  119. package/dist/core/evaluation/golden-dataset.js +293 -0
  120. package/dist/core/feedback/queue.js +177 -0
  121. package/dist/core/feedback/submitter.js +145 -0
  122. package/dist/core/file-cache.js +113 -1
  123. package/dist/core/flatten/flatten-repo.js +439 -0
  124. package/dist/core/format/osc8-link.js +28 -0
  125. package/dist/core/hook-chains.js +392 -0
  126. package/dist/core/hooks/citation-verify-hook.js +138 -0
  127. package/dist/core/hooks/citation-verify.js +112 -0
  128. package/dist/core/hooks/events.js +46 -0
  129. package/dist/core/hooks/index.js +15 -0
  130. package/dist/core/hooks/registry.js +216 -0
  131. package/dist/core/hooks/runner.js +236 -0
  132. package/dist/core/hooks/v2/event-emitter.js +115 -0
  133. package/dist/core/hooks/v2/executor.js +282 -0
  134. package/dist/core/hooks/v2/index.js +25 -0
  135. package/dist/core/hooks/v2/lifecycle.js +104 -0
  136. package/dist/core/hooks/v2/loader.js +216 -0
  137. package/dist/core/hooks/v2/matcher.js +125 -0
  138. package/dist/core/hooks/v2/trust.js +143 -0
  139. package/dist/core/hooks/v2/types.js +86 -0
  140. package/dist/core/hooks/worktree-events.js +158 -0
  141. package/dist/core/image/renderer.js +71 -0
  142. package/dist/core/init/detector.js +582 -0
  143. package/dist/core/init/template-renderer.js +242 -0
  144. package/dist/core/jobs/registry.js +18 -18
  145. package/dist/core/ledger/results-tsv.js +142 -0
  146. package/dist/core/log-discipline/stdout-redirect.js +51 -0
  147. package/dist/core/lsp/cache.js +105 -0
  148. package/dist/core/lsp/client.js +551 -41
  149. package/dist/core/lsp/language-detect.js +66 -0
  150. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  151. package/dist/core/lsp/server-detect.js +173 -0
  152. package/dist/core/lsp/symbol-cache.js +162 -0
  153. package/dist/core/lsp/symbol-tools.js +664 -0
  154. package/dist/core/mcp/client.js +97 -28
  155. package/dist/core/mcp/http-server.js +553 -0
  156. package/dist/core/mcp/orchestrator-tools.js +662 -0
  157. package/dist/core/mcp/permission.js +190 -0
  158. package/dist/core/mcp/registry.js +39 -17
  159. package/dist/core/mcp/server-tools.js +219 -0
  160. package/dist/core/mcp/server.js +397 -0
  161. package/dist/core/mcp/trust.js +10 -10
  162. package/dist/core/memory/dual-write.js +416 -0
  163. package/dist/core/memory/passive-extract.js +130 -0
  164. package/dist/core/memory/phase1-kinds.js +20 -0
  165. package/dist/core/memory/secret-scanner.js +304 -0
  166. package/dist/core/memory-sync/queue.js +170 -0
  167. package/dist/core/metrics/extract.js +113 -0
  168. package/dist/core/modes/roo-modes.js +68 -0
  169. package/dist/core/onboarding/ensure-initialized.js +133 -0
  170. package/dist/core/onboarding/marker.js +111 -0
  171. package/dist/core/onboarding/telemetry-state.js +108 -0
  172. package/dist/core/output-style/presets.js +176 -0
  173. package/dist/core/output-style/state.js +185 -0
  174. package/dist/core/path-security.js +287 -5
  175. package/dist/core/permission.js +82 -22
  176. package/dist/core/permissions/auto-classifier.js +124 -0
  177. package/dist/core/permissions/bash-parser.js +371 -0
  178. package/dist/core/permissions/circuit-breaker.js +83 -0
  179. package/dist/core/permissions/constrained-edit.js +91 -0
  180. package/dist/core/permissions/gate.js +278 -0
  181. package/dist/core/permissions/index.js +20 -0
  182. package/dist/core/permissions/mode.js +174 -0
  183. package/dist/core/permissions/network-egress.js +137 -0
  184. package/dist/core/permissions/state.js +241 -0
  185. package/dist/core/permissions/tool-class.js +93 -0
  186. package/dist/core/plan-mode/ui-state.js +51 -0
  187. package/dist/core/plans/plan-artifact.js +721 -0
  188. package/dist/core/policy-limits/etag-store.js +122 -0
  189. package/dist/core/prd-check/parser.js +215 -0
  190. package/dist/core/prd-check/reporter.js +127 -0
  191. package/dist/core/prd-check/session-review.js +557 -0
  192. package/dist/core/prd-check/verifiers.js +223 -0
  193. package/dist/core/prompt-cache/client-cache.js +99 -0
  194. package/dist/core/prompts/assembly.js +29 -0
  195. package/dist/core/prompts/registry.js +364 -0
  196. package/dist/core/pugi-md/cc-compat-rules.js +735 -0
  197. package/dist/core/pugi-md/context-injector.js +76 -0
  198. package/dist/core/pugi-md/walk-up.js +207 -0
  199. package/dist/core/python/uv-installer.js +270 -0
  200. package/dist/core/python/uv-resolver.js +83 -0
  201. package/dist/core/rate-limit/narrator.js +146 -0
  202. package/dist/core/recipes/cli-types.js +20 -0
  203. package/dist/core/recipes/loader.js +103 -0
  204. package/dist/core/recipes/runner.js +345 -0
  205. package/dist/core/recipes/schema.js +587 -0
  206. package/dist/core/release-notes/parser.js +241 -0
  207. package/dist/core/release-notes/state.js +116 -0
  208. package/dist/core/repl/ask.js +37 -37
  209. package/dist/core/repl/cancellation.js +26 -26
  210. package/dist/core/repl/cap-warning.js +4 -4
  211. package/dist/core/repl/clipboard-read.js +11 -11
  212. package/dist/core/repl/dispatch-fsm.js +12 -12
  213. package/dist/core/repl/history-search.js +15 -15
  214. package/dist/core/repl/history.js +28 -18
  215. package/dist/core/repl/kill-ring.js +5 -5
  216. package/dist/core/repl/model-pricing.js +135 -0
  217. package/dist/core/repl/privacy-banner.js +22 -22
  218. package/dist/core/repl/session.js +2148 -217
  219. package/dist/core/repl/slash-commands.js +501 -41
  220. package/dist/core/repl/store/index.js +1 -1
  221. package/dist/core/repl/store/jsonl-log.js +22 -22
  222. package/dist/core/repl/store/lockfile.js +10 -10
  223. package/dist/core/repl/store/session-store.js +136 -107
  224. package/dist/core/repl/store/types.js +15 -15
  225. package/dist/core/repl/store/uuid-v7.js +12 -12
  226. package/dist/core/repl/workspace-context.js +43 -21
  227. package/dist/core/repo-map/build.js +125 -0
  228. package/dist/core/repo-map/cache.js +185 -0
  229. package/dist/core/repo-map/extractor.js +254 -0
  230. package/dist/core/repo-map/formatter.js +145 -0
  231. package/dist/core/repo-map/page-rank.js +105 -0
  232. package/dist/core/repo-map/scanner.js +211 -0
  233. package/dist/core/retry-budget/budget.js +284 -0
  234. package/dist/core/retry-budget/index.js +5 -0
  235. package/dist/core/retry-budget/retry-cap.js +74 -0
  236. package/dist/core/routing/lead-worker.js +43 -0
  237. package/dist/core/routing/pre-flight-estimator.js +108 -0
  238. package/dist/core/runs/run-tree.js +103 -0
  239. package/dist/core/security/injection-scanner.js +367 -0
  240. package/dist/core/security/output-filter.js +418 -0
  241. package/dist/core/session/env-file.js +105 -0
  242. package/dist/core/session/section-budgets.js +140 -0
  243. package/dist/core/session.js +92 -0
  244. package/dist/core/settings.js +324 -5
  245. package/dist/core/share/formatter.js +271 -0
  246. package/dist/core/share/redactor.js +221 -0
  247. package/dist/core/share/uploader.js +267 -0
  248. package/dist/core/skills/defaults.js +30 -30
  249. package/dist/core/skills/loader.js +22 -22
  250. package/dist/core/skills/sources.js +27 -27
  251. package/dist/core/smoke/headless-driver.js +174 -0
  252. package/dist/core/smoke/orchestrator.js +194 -0
  253. package/dist/core/smoke/runner.js +238 -0
  254. package/dist/core/smoke/scenario-parser.js +316 -0
  255. package/dist/core/statusline.js +99 -0
  256. package/dist/core/subagents/dispatcher-real.js +600 -0
  257. package/dist/core/subagents/dispatcher.js +132 -43
  258. package/dist/core/subagents/index.js +19 -6
  259. package/dist/core/subagents/isolation-matrix.js +213 -0
  260. package/dist/core/subagents/spawn.js +19 -4
  261. package/dist/core/telemetry/emitter.js +229 -0
  262. package/dist/core/telemetry/queue.js +251 -0
  263. package/dist/core/theme/context.js +91 -0
  264. package/dist/core/theme/presets.js +228 -0
  265. package/dist/core/theme/state.js +181 -0
  266. package/dist/core/todos/invariant.js +10 -0
  267. package/dist/core/todos/state.js +177 -0
  268. package/dist/core/tool-schema/compressor.js +89 -0
  269. package/dist/core/transport/version-interceptor.js +166 -0
  270. package/dist/core/trust.js +2 -2
  271. package/dist/core/tui/thinking-block.js +64 -0
  272. package/dist/core/vim/keymap.js +288 -0
  273. package/dist/core/vim/state.js +92 -0
  274. package/dist/core/watch-markers/marker-watcher.js +133 -0
  275. package/dist/core/worktree/include-parser.js +249 -0
  276. package/dist/core/worktree-manager/cleanup.js +123 -0
  277. package/dist/core/worktree-manager/manager.js +303 -0
  278. package/dist/index.js +36 -0
  279. package/dist/runtime/bootstrap.js +190 -0
  280. package/dist/runtime/cli.js +4185 -549
  281. package/dist/runtime/commands/agents.js +31 -31
  282. package/dist/runtime/commands/budget.js +5 -5
  283. package/dist/runtime/commands/cancel.js +231 -0
  284. package/dist/runtime/commands/chain.js +489 -0
  285. package/dist/runtime/commands/codegraph-status.js +227 -0
  286. package/dist/runtime/commands/compact.js +297 -0
  287. package/dist/runtime/commands/config.js +73 -39
  288. package/dist/runtime/commands/cost.js +199 -0
  289. package/dist/runtime/commands/delegate.js +27 -4
  290. package/dist/runtime/commands/dispatch.js +126 -0
  291. package/dist/runtime/commands/doctor.js +579 -0
  292. package/dist/runtime/commands/feedback.js +184 -0
  293. package/dist/runtime/commands/hooks.js +187 -0
  294. package/dist/runtime/commands/init.js +254 -0
  295. package/dist/runtime/commands/lsp.js +200 -38
  296. package/dist/runtime/commands/mcp.js +879 -0
  297. package/dist/runtime/commands/memory.js +582 -0
  298. package/dist/runtime/commands/model.js +237 -0
  299. package/dist/runtime/commands/onboarding.js +275 -0
  300. package/dist/runtime/commands/patch.js +12 -12
  301. package/dist/runtime/commands/permissions.js +112 -0
  302. package/dist/runtime/commands/plan.js +143 -0
  303. package/dist/runtime/commands/prd-check.js +285 -0
  304. package/dist/runtime/commands/privacy.js +17 -17
  305. package/dist/runtime/commands/recipe.js +325 -0
  306. package/dist/runtime/commands/redo-blob-store.js +92 -0
  307. package/dist/runtime/commands/redo.js +361 -0
  308. package/dist/runtime/commands/release-notes.js +229 -0
  309. package/dist/runtime/commands/repo-map.js +95 -0
  310. package/dist/runtime/commands/report.js +299 -0
  311. package/dist/runtime/commands/resume.js +118 -0
  312. package/dist/runtime/commands/review-consensus.js +68 -53
  313. package/dist/runtime/commands/rewind.js +333 -0
  314. package/dist/runtime/commands/roster.js +14 -14
  315. package/dist/runtime/commands/sessions.js +163 -0
  316. package/dist/runtime/commands/share.js +316 -0
  317. package/dist/runtime/commands/skills.js +31 -31
  318. package/dist/runtime/commands/status.js +186 -0
  319. package/dist/runtime/commands/stickers.js +82 -0
  320. package/dist/runtime/commands/style.js +194 -0
  321. package/dist/runtime/commands/theme.js +196 -0
  322. package/dist/runtime/commands/undo.js +54 -22
  323. package/dist/runtime/commands/update.js +289 -0
  324. package/dist/runtime/commands/vim.js +140 -0
  325. package/dist/runtime/commands/worktree.js +8 -8
  326. package/dist/runtime/commands/worktrees.js +155 -0
  327. package/dist/runtime/headless-repl.js +195 -0
  328. package/dist/runtime/headless.js +543 -0
  329. package/dist/runtime/load-hooks-or-exit.js +71 -0
  330. package/dist/runtime/plan-decompose.js +22 -22
  331. package/dist/runtime/sigint-guard.js +272 -0
  332. package/dist/runtime/update-check.js +28 -28
  333. package/dist/runtime/version.js +65 -0
  334. package/dist/runtime/worktree-bootstrap.js +579 -0
  335. package/dist/skills/bundled/batch.js +617 -0
  336. package/dist/skills/bundled/index.js +45 -0
  337. package/dist/skills/bundled/loop.js +358 -0
  338. package/dist/skills/bundled/remember.js +383 -0
  339. package/dist/skills/bundled/simplify.js +289 -0
  340. package/dist/skills/bundled/skillify.js +373 -0
  341. package/dist/skills/bundled/stuck.js +558 -0
  342. package/dist/skills/bundled/verify.js +439 -0
  343. package/dist/testing/vcr.js +486 -0
  344. package/dist/tools/agent-tool.js +229 -0
  345. package/dist/tools/apply-patch.js +89 -28
  346. package/dist/tools/ask-user-question.js +337 -0
  347. package/dist/tools/ask-user.js +115 -0
  348. package/dist/tools/bash.js +624 -46
  349. package/dist/tools/brief.js +224 -0
  350. package/dist/tools/cron.js +433 -0
  351. package/dist/tools/enter-worktree.js +250 -0
  352. package/dist/tools/exit-worktree.js +147 -0
  353. package/dist/tools/file-tools.js +161 -44
  354. package/dist/tools/lsp-tools.js +377 -1
  355. package/dist/tools/mcp-tool.js +260 -0
  356. package/dist/tools/multi-edit.js +361 -0
  357. package/dist/tools/powershell.js +268 -0
  358. package/dist/tools/registry.js +99 -4
  359. package/dist/tools/skill-tool.js +96 -0
  360. package/dist/tools/sleep.js +99 -0
  361. package/dist/tools/synthetic-output.js +133 -0
  362. package/dist/tools/tasks.js +208 -0
  363. package/dist/tools/todo-write.js +184 -0
  364. package/dist/tools/verify-plan-execution.js +295 -0
  365. package/dist/tools/web-fetch-injection-scanner.js +207 -0
  366. package/dist/tools/web-fetch.js +195 -10
  367. package/dist/tools/web-search.js +458 -0
  368. package/dist/tui/agent-progress-card.js +111 -0
  369. package/dist/tui/agent-tree.js +11 -1
  370. package/dist/tui/ask-modal.js +14 -14
  371. package/dist/tui/ask-user-question-chips.js +315 -0
  372. package/dist/tui/ask-user-question-prompt.js +203 -0
  373. package/dist/tui/compact-banner.js +81 -0
  374. package/dist/tui/conversation-pane.js +85 -11
  375. package/dist/tui/cost-table.js +111 -0
  376. package/dist/tui/device-flow.js +2 -2
  377. package/dist/tui/doctor-table.js +46 -0
  378. package/dist/tui/feedback-prompt.js +156 -0
  379. package/dist/tui/input-box.js +247 -32
  380. package/dist/tui/login-picker.js +3 -3
  381. package/dist/tui/markdown-render.js +6 -6
  382. package/dist/tui/multi-file-diff-approval.js +375 -0
  383. package/dist/tui/onboarding-wizard.js +240 -0
  384. package/dist/tui/permissions-picker.js +86 -0
  385. package/dist/tui/render.js +36 -1
  386. package/dist/tui/repl-render.js +176 -25
  387. package/dist/tui/repl-splash-art.js +16 -16
  388. package/dist/tui/repl-splash-mascot.js +48 -24
  389. package/dist/tui/repl-splash.js +22 -22
  390. package/dist/tui/repl.js +125 -45
  391. package/dist/tui/slash-palette.js +6 -6
  392. package/dist/tui/splash.js +2 -2
  393. package/dist/tui/status-bar.js +109 -31
  394. package/dist/tui/status-table.js +7 -0
  395. package/dist/tui/stickers-art.js +136 -0
  396. package/dist/tui/style-table.js +28 -0
  397. package/dist/tui/theme-table.js +29 -0
  398. package/dist/tui/thinking-spinner.js +123 -0
  399. package/dist/tui/tool-stream-pane.js +53 -4
  400. package/dist/tui/update-banner.js +27 -2
  401. package/dist/tui/vim-input.js +267 -0
  402. package/dist/tui/welcome-banner.js +107 -0
  403. package/dist/tui/welcome-data.js +293 -0
  404. package/dist/tui/workspace-context.js +2 -2
  405. package/package.json +31 -16
  406. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  407. package/test/scenarios/compact-force.scenario.txt +12 -0
  408. package/test/scenarios/identity.scenario.txt +12 -0
  409. package/test/scenarios/persona-handoff.scenario.txt +12 -0
  410. package/test/scenarios/walkback.scenario.txt +12 -0
  411. package/dist/core/engine/compaction-hook.js +0 -154
@@ -0,0 +1,304 @@
1
+ /**
2
+ * Persona-memory secret scanner (backlog, hardening).
3
+ *
4
+ * Defense against API keys / credentials accidentally landing in shared
5
+ * persona memory. A naive operator typing `pugi memory write fact "Use
6
+ * API key sk-ant-..."` would silently persist that secret to the
7
+ * `the platform database.persona_memory` table — visible to every persona, every
8
+ * recall query, every dual-write sink. This module is the chokepoint
9
+ * that refuses such writes.
10
+ *
11
+ * # Scope
12
+ *
13
+ * Scoped to the persona-memory write surface (`pugi memory write` and
14
+ * the `pugi remember` curator's `defaultPersist`). NOT a global secret
15
+ * scanner — the tarball pre-publish gate
16
+ * (`apps/pugi-cli/scripts/secret-scanner.mjs`) covers npm publish, and
17
+ * gitleaks covers git history. The two scanners share the pattern
18
+ * vocabulary but live at different boundaries; do NOT consolidate
19
+ * without re-validating each call site.
20
+ *
21
+ * # Patterns shipped
22
+ *
23
+ * 1. Anthropic key sk-ant-… high
24
+ * 2. Generic OpenAI-style sk-… high
25
+ * 3. GitHub PAT / installation tokens (ghp_/ghs_/gho_/ghu_)
26
+ * high
27
+ * 4. AWS access key id AKIA… high
28
+ * 5. Plane API token plane_api_… high [pugi-leak-ok]
29
+ * 6. npm token npm_… high
30
+ * 7. Slack token xox[bpoars]-… high
31
+ * 8. Stripe secret key sk_(live|test)_… high
32
+ * 9. JWT-shaped token eyJ…\.…\.… medium
33
+ * 10. Generic Bearer header Bearer <opaque> medium
34
+ * 11. Private-key PEM header -----BEGIN … PRIVATE… high
35
+ * 12. Pugi internal Anvil key anvil_… / sk_anvil_… high
36
+ *
37
+ * Each rule names its pattern; matches are surfaced by `pattern` name
38
+ * so the operator can map the rejection back to a credential type.
39
+ *
40
+ * # Confidence levels
41
+ *
42
+ * `high` — the regex is prefix-anchored to a vendor-issued shape that
43
+ * is rare in normal English text (e.g. `sk-ant-`, `AKIA`, `ghp_`). A
44
+ * single match is enough to reject.
45
+ *
46
+ * `medium` — the regex is structural (JWT, Bearer, hex blob). False
47
+ * positives ARE possible. Default behaviour rejects on medium matches
48
+ * too (security-first), but operators can downgrade to high-only via
49
+ * the `PUGI_MEMORY_SECRET_STRICT=0` opt-out — see `scanForSecrets`'s
50
+ * `strict` option.
51
+ *
52
+ * # Opt-outs
53
+ *
54
+ * - `PUGI_MEMORY_SECRET_SCAN_DISABLE=1` bypass entirely (tests).
55
+ * - `PUGI_MEMORY_SECRET_STRICT=0` ignore medium-confidence
56
+ * matches (still rejects
57
+ * on high).
58
+ * - `--allow-redacted` flag (handled by caller) opts into auto-redact
59
+ * instead of reject. The scanner exposes `redactSecrets` for that
60
+ * caller — see `runtime/commands/memory.ts`.
61
+ *
62
+ * # independent implementation provenance
63
+ *
64
+ * Inspired by the the upstream tool teamMemorySync.secretScanner pattern
65
+ * (intel from leak-research memos). independent implementation TypeScript
66
+ * implementation — no upstream code reused. Pattern vocabulary was
67
+ * cross-referenced against the existing
68
+ * `apps/pugi-cli/scripts/secret-scanner.mjs` tarball gate so a single
69
+ * vendor (AWS, GitHub, etc.) is handled the same way in both surfaces.
70
+ */
71
+ /**
72
+ * Pattern registry. Each regex is anchored with a non-capturing
73
+ * boundary and uses bounded quantifiers — no `.*` and no nested
74
+ * alternations that would invite catastrophic backtracking.
75
+ *
76
+ * NOTE: regexes are constructed with the `g` flag here because
77
+ * `String.prototype.matchAll` requires it. The scanner clones each
78
+ * regex before iterating so concurrent calls do not share `lastIndex`.
79
+ */
80
+ const SECRET_RULES = [
81
+ {
82
+ pattern: 'anthropic-api-key',
83
+ // sk-ant- followed by 40+ url-safe chars. Anthropic keys are
84
+ // typically 95+ chars; production keys can run 200+ chars. The
85
+ // greedy `{40,}` plus a negative lookahead terminates cleanly on
86
+ // any length without an upper bound that would silently miss
87
+ // longer keys (Claude reviewer P1).
88
+ regex: /\bsk-ant-[A-Za-z0-9_-]{40,}(?![A-Za-z0-9_-])/g,
89
+ confidence: 'high',
90
+ },
91
+ {
92
+ pattern: 'openai-project-key',
93
+ // OpenAI project keys (`sk-proj-…`) include hyphens/underscores
94
+ // in the tail, so the legacy alphanumeric class would silently
95
+ // miss them. Anchored to the literal `sk-proj-` prefix and
96
+ // terminated via lookahead so length never caps detection.
97
+ regex: /\bsk-proj-[A-Za-z0-9_-]{40,}(?![A-Za-z0-9_-])/g,
98
+ confidence: 'high',
99
+ },
100
+ {
101
+ pattern: 'openai-sk-key',
102
+ // OpenAI legacy "sk-" + 32+ alphanumeric. We anchor with a word
103
+ // boundary so prose like "the SK-Mafia" can't collide. The lower
104
+ // bound is 20 to align with the spec; OpenAI in practice issues
105
+ // 48-char keys. Lookahead-terminated so length cannot cap us out.
106
+ regex: /\bsk-[A-Za-z0-9]{20,}(?![A-Za-z0-9])/g,
107
+ confidence: 'high',
108
+ },
109
+ {
110
+ pattern: 'github-token',
111
+ // ghp_ / ghs_ / gho_ / ghu_ + exactly 36 base62 chars (vendor
112
+ // doc'd shape).
113
+ regex: /\bgh[psou]_[A-Za-z0-9]{36}\b/g,
114
+ confidence: 'high',
115
+ },
116
+ {
117
+ pattern: 'aws-access-key-id',
118
+ // 16 uppercase alphanumerics after `AKIA` — vendor format. Word
119
+ // boundary keeps `MAKIAYZX...` (unlikely but possible) out.
120
+ regex: /\bAKIA[0-9A-Z]{16}\b/g,
121
+ confidence: 'high',
122
+ },
123
+ {
124
+ pattern: 'plane-api-token',
125
+ // Plane (project management) personal API tokens. Bounded to 20+
126
+ // url-safe chars after the prefix.
127
+ regex: /\bplane_api_[A-Za-z0-9]{20,}(?![A-Za-z0-9])/g, // [pugi-leak-ok]
128
+ confidence: 'high',
129
+ },
130
+ {
131
+ pattern: 'npm-token',
132
+ // npm_ + 36 base62 chars (vendor format).
133
+ regex: /\bnpm_[A-Za-z0-9]{36}\b/g,
134
+ confidence: 'high',
135
+ },
136
+ {
137
+ pattern: 'slack-token',
138
+ // xoxb / xoxp / xoxo / xoxa / xoxr / xoxs. Slack tokens are
139
+ // hyphen-segmented; we bound the trailing segment length to dodge
140
+ // `.*`-style backtracking.
141
+ regex: /\bxox[bpoars]-[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g,
142
+ confidence: 'high',
143
+ },
144
+ {
145
+ pattern: 'stripe-key',
146
+ // sk_live_ / sk_test_ + 24+ base62 chars.
147
+ regex: /\bsk_(?:live|test)_[A-Za-z0-9]{24,}(?![A-Za-z0-9])/g,
148
+ confidence: 'high',
149
+ },
150
+ {
151
+ pattern: 'private-key-pem',
152
+ // PEM block headers — RSA / OPENSSH / EC / DSA / PGP. Anchored
153
+ // alternation, no nested quantifiers.
154
+ regex: /-----BEGIN (?:RSA|OPENSSH|EC|DSA|PGP) PRIVATE KEY-----/g,
155
+ confidence: 'high',
156
+ },
157
+ {
158
+ pattern: 'anvil-api-key',
159
+ // Pugi-internal Anvil keys — both the legacy and scoped shapes.
160
+ regex: /\b(?:sk_)?anvil_[A-Za-z0-9_-]{16,}(?![A-Za-z0-9_-])/g,
161
+ confidence: 'high',
162
+ },
163
+ {
164
+ pattern: 'jwt-token',
165
+ // Three base64url segments separated by `.`. The 20-char lower
166
+ // bound per segment dodges the "u.s.a" trap; real JWTs have
167
+ // headers like `eyJhbGciOi…` which are well above this floor.
168
+ regex: /\beyJ[A-Za-z0-9_-]{18,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\b/g,
169
+ confidence: 'medium',
170
+ },
171
+ {
172
+ pattern: 'bearer-token',
173
+ // Authorization-header shape with an opaque 20+ char tail.
174
+ // Case-insensitive `Bearer` keyword. Bounded tail to avoid
175
+ // matching whole prose paragraphs.
176
+ regex: /\b[Bb]earer\s+[A-Za-z0-9_.~+/=-]{20,200}\b/g,
177
+ confidence: 'medium',
178
+ },
179
+ ];
180
+ /** Resolve the strict-mode setting (env-first, opt-out via "0" / "false"). */
181
+ function resolveStrict(opt) {
182
+ if (typeof opt === 'boolean')
183
+ return opt;
184
+ const raw = process.env.PUGI_MEMORY_SECRET_STRICT;
185
+ if (raw === undefined)
186
+ return true;
187
+ const lowered = raw.trim().toLowerCase();
188
+ return !(lowered === '0' || lowered === 'false' || lowered === 'no');
189
+ }
190
+ /** Predicate — is the scanner currently disabled via env? */
191
+ export function isScanDisabled() {
192
+ const raw = process.env.PUGI_MEMORY_SECRET_SCAN_DISABLE;
193
+ if (raw === undefined)
194
+ return false;
195
+ const lowered = raw.trim().toLowerCase();
196
+ return lowered === '1' || lowered === 'true' || lowered === 'yes';
197
+ }
198
+ /** Compute 1-based line number for a byte offset. */
199
+ function lineNumberFor(text, offset) {
200
+ let line = 1;
201
+ for (let i = 0; i < offset && i < text.length; i += 1) {
202
+ if (text.charCodeAt(i) === 10)
203
+ line += 1;
204
+ }
205
+ return line;
206
+ }
207
+ /**
208
+ * Scan `text` for secret patterns. Returns one `SecretMatch` per hit,
209
+ * in occurrence order. Empty array means clean.
210
+ *
211
+ * The function is pure — it does NOT read env outside of resolving the
212
+ * strict-mode default for `options.strict`. Callers that need a fully
213
+ * deterministic invocation should pass `options.strict` explicitly.
214
+ */
215
+ export function scanForSecrets(text, options) {
216
+ if (text.length === 0)
217
+ return [];
218
+ const strict = resolveStrict(options?.strict);
219
+ const out = [];
220
+ for (const rule of SECRET_RULES) {
221
+ if (!strict && rule.confidence === 'medium')
222
+ continue;
223
+ // Clone the regex per-rule so concurrent scans (e.g. parallel
224
+ // tests) don't share `lastIndex`. This also defends against
225
+ // accidental mutation if a future rule forgets the `g` flag.
226
+ const rx = new RegExp(rule.regex.source, rule.regex.flags);
227
+ for (const m of text.matchAll(rx)) {
228
+ if (m.index === undefined)
229
+ continue;
230
+ out.push({
231
+ pattern: rule.pattern,
232
+ match: m[0],
233
+ offset: m.index,
234
+ lineNumber: lineNumberFor(text, m.index),
235
+ confidence: rule.confidence,
236
+ });
237
+ }
238
+ }
239
+ // Sort by offset so callers see a left-to-right walk of the input
240
+ // even when multiple rules fired in different orders.
241
+ out.sort((a, b) => a.offset - b.offset);
242
+ return out;
243
+ }
244
+ /**
245
+ * Replace every detected secret with a `[SECRET:<pattern>]` placeholder
246
+ * and return both the redacted text and the original match list. The
247
+ * pattern name is included in the placeholder so the operator can see
248
+ * what category was scrubbed.
249
+ *
250
+ * Redaction respects the same strict-mode resolution as `scanForSecrets`.
251
+ */
252
+ export function redactSecrets(text, options) {
253
+ const matches = scanForSecrets(text, options);
254
+ if (matches.length === 0)
255
+ return { redacted: text, matches };
256
+ // Walk right-to-left so earlier offsets stay valid as we splice.
257
+ let redacted = text;
258
+ for (let i = matches.length - 1; i >= 0; i -= 1) {
259
+ const m = matches[i];
260
+ if (m === undefined)
261
+ continue;
262
+ const before = redacted.slice(0, m.offset);
263
+ const after = redacted.slice(m.offset + m.match.length);
264
+ redacted = `${before}[SECRET:${m.pattern}]${after}`;
265
+ }
266
+ return { redacted, matches };
267
+ }
268
+ /**
269
+ * Hard guard for memory write entry points. Throws a
270
+ * `MemorySecretGuardError` on any non-empty match set (after the env
271
+ * disable check). Caller side handles the `--allow-redacted` flow by
272
+ * calling `redactSecrets` directly.
273
+ *
274
+ * Returns the (unchanged) input string when the scan is clean — lets
275
+ * the call site stay one-liner: `content = assertNoSecrets(content)`.
276
+ */
277
+ export function assertNoSecrets(text, options) {
278
+ if (isScanDisabled())
279
+ return text;
280
+ const matches = scanForSecrets(text, options);
281
+ if (matches.length === 0)
282
+ return text;
283
+ throw new MemorySecretGuardError(matches);
284
+ }
285
+ /**
286
+ * Thrown when memory content would persist a credential. The error
287
+ * carries the structured match list so the CLI can render an
288
+ * actionable message (pattern names + line numbers) without
289
+ * re-leaking the secrets back into the operator's terminal.
290
+ */
291
+ export class MemorySecretGuardError extends Error {
292
+ matches;
293
+ constructor(matches) {
294
+ const names = Array.from(new Set(matches.map((m) => m.pattern))).join(', ');
295
+ super(`pugi memory: refused to persist content containing secret(s) — pattern(s): ${names}. ` +
296
+ `Set PUGI_MEMORY_SECRET_SCAN_DISABLE=1 to bypass, or rewrite the content. ` +
297
+ `Use \`pugi memory write --allow-redacted\` to auto-scrub.`);
298
+ this.name = 'MemorySecretGuardError';
299
+ this.matches = matches;
300
+ }
301
+ }
302
+ /** Stable export for tests + integration call sites. */
303
+ export const SECRET_PATTERN_NAMES = SECRET_RULES.map((r) => r.pattern);
304
+ //# sourceMappingURL=secret-scanner.js.map
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Pugi memory sync queue .
3
+ *
4
+ * Local pending-write queue for `pugi memory` commands when the
5
+ * operator is offline or the admin-api is unreachable. Each pending
6
+ * mutation lands on disk as one JSONL line; `pugi memory sync` reads
7
+ * the queue, fires them to the admin-api in order, and rewrites the
8
+ * file with only the entries that still failed.
9
+ *
10
+ * Storage:
11
+ *
12
+ * ~/.pugi/memory-queue.jsonl (mode 0600)
13
+ *
14
+ * Each line is a fully-typed `PendingMemoryOperation` envelope. The
15
+ * envelope is forward-compatible: an older CLI reading a JSONL file
16
+ * written by a newer CLI silently skips lines whose `op` field is
17
+ * not in its known set (so a partial-rollback scenario doesn't crash
18
+ * the queue).
19
+ *
20
+ * Design intent:
21
+ * - Append-only on disk for the hot path (`pugi memory write` /
22
+ * `pugi memory forget` queue when offline). Rewrites only on
23
+ * successful sync.
24
+ * - One file per operator (PUGI_HOME-aware). Queue is local to the
25
+ * machine — no cross-host coordination. Multi-device sync is
26
+ * deferred to Phase 6 (server-side outbox).
27
+ * - No fsync / atomic rename ceremony in v1 — best effort. The
28
+ * queue is a convenience surface, not a durability primitive;
29
+ * the source of truth is the admin-api row.
30
+ */
31
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync, } from 'node:fs';
32
+ import { homedir } from 'node:os';
33
+ import { dirname, resolve } from 'node:path';
34
+ import { z } from 'zod';
35
+ import { assertNoSecrets } from '../memory/secret-scanner.js';
36
+ /** Six canonical kinds — must mirror `apps/admin-api/src/persona-memory/persona-memory.types.ts`. */
37
+ export const PERSONA_MEMORY_KINDS = [
38
+ 'pattern',
39
+ 'preference',
40
+ 'architecture',
41
+ 'bug',
42
+ 'workflow',
43
+ 'fact',
44
+ ];
45
+ const writeOpSchema = z.object({
46
+ op: z.literal('write'),
47
+ enqueuedAt: z.string().datetime(),
48
+ personaSlug: z.string().min(1).max(64),
49
+ kind: z.enum(PERSONA_MEMORY_KINDS),
50
+ content: z.string().min(1).max(4000),
51
+ forgetAfter: z.string().datetime().nullable().optional(),
52
+ });
53
+ const forgetOpSchema = z.object({
54
+ op: z.literal('forget'),
55
+ enqueuedAt: z.string().datetime(),
56
+ id: z.string().min(1),
57
+ });
58
+ const pendingMemoryOpSchema = z.discriminatedUnion('op', [
59
+ writeOpSchema,
60
+ forgetOpSchema,
61
+ ]);
62
+ /** Default storage path. Override via `PUGI_HOME` for tests / multi-account. */
63
+ export function defaultQueuePath() {
64
+ const root = process.env.PUGI_HOME ?? resolve(homedir(), '.pugi');
65
+ return resolve(root, 'memory-queue.jsonl');
66
+ }
67
+ /**
68
+ * Append one pending operation to the queue file. Creates the parent
69
+ * directory + file with mode 0600 if missing. Pure-disk, no network.
70
+ *
71
+ * Returns the count of pending ops after the append (1-based) so the
72
+ * CLI command can render "queued (3 pending) — run `pugi memory sync`".
73
+ */
74
+ export function enqueueMemoryOp(op, pathOverride) {
75
+ const fullOp = {
76
+ ...op,
77
+ enqueuedAt: new Date().toISOString(),
78
+ };
79
+ pendingMemoryOpSchema.parse(fullOp);
80
+ // Backlog (P1 security hardening): refuse to enqueue any write
81
+ // whose `content` matches a known secret shape (sk-ant-, ghp_, AKIA…
82
+ // etc). `assertNoSecrets` throws `MemorySecretGuardError` carrying
83
+ // the matched pattern list, which the CLI catches and renders
84
+ // without re-leaking the value back to the operator. The opt-out
85
+ // env (`PUGI_MEMORY_SECRET_SCAN_DISABLE=1`) is honoured inside
86
+ // `assertNoSecrets` so tests + emergency overrides have a knob.
87
+ // `forget` ops carry only an id, so they bypass the scan.
88
+ if (fullOp.op === 'write') {
89
+ assertNoSecrets(fullOp.content);
90
+ }
91
+ const queuePath = pathOverride ?? defaultQueuePath();
92
+ ensureQueueFile(queuePath);
93
+ const existing = readFileSync(queuePath, 'utf-8');
94
+ const line = `${JSON.stringify(fullOp)}\n`;
95
+ writeFileSync(queuePath, `${existing}${line}`, { encoding: 'utf-8', mode: 0o600 });
96
+ // Best-effort chmod (in case the file existed already at the wrong mode).
97
+ try {
98
+ chmodSync(queuePath, 0o600);
99
+ }
100
+ catch {
101
+ // ignore — the file was just written above, mode might be platform-dependent.
102
+ }
103
+ return countPending(queuePath);
104
+ }
105
+ /** Read the queue file and return parsed entries. Skips unknown / malformed lines. */
106
+ export function readMemoryQueue(pathOverride) {
107
+ const queuePath = pathOverride ?? defaultQueuePath();
108
+ if (!existsSync(queuePath))
109
+ return [];
110
+ const raw = readFileSync(queuePath, 'utf-8');
111
+ const out = [];
112
+ for (const line of raw.split(/\r?\n/)) {
113
+ const trimmed = line.trim();
114
+ if (!trimmed)
115
+ continue;
116
+ try {
117
+ const parsed = pendingMemoryOpSchema.parse(JSON.parse(trimmed));
118
+ out.push(parsed);
119
+ }
120
+ catch {
121
+ // forward-compat: a future op kind we don't recognise should not
122
+ // crash the queue; just drop the line during this read.
123
+ continue;
124
+ }
125
+ }
126
+ return out;
127
+ }
128
+ /** Rewrite the queue file with `remaining` entries only. Empty array clears the file. */
129
+ export function rewriteMemoryQueue(remaining, pathOverride) {
130
+ const queuePath = pathOverride ?? defaultQueuePath();
131
+ ensureQueueFile(queuePath);
132
+ if (remaining.length === 0) {
133
+ writeFileSync(queuePath, '', { encoding: 'utf-8', mode: 0o600 });
134
+ return;
135
+ }
136
+ const body = remaining.map((op) => JSON.stringify(op)).join('\n') + '\n';
137
+ writeFileSync(queuePath, body, { encoding: 'utf-8', mode: 0o600 });
138
+ }
139
+ /** Count pending ops without re-parsing every line individually for the typed shape. */
140
+ export function countPending(pathOverride) {
141
+ const queuePath = pathOverride ?? defaultQueuePath();
142
+ if (!existsSync(queuePath))
143
+ return 0;
144
+ const raw = readFileSync(queuePath, 'utf-8');
145
+ let n = 0;
146
+ for (const line of raw.split(/\r?\n/)) {
147
+ if (line.trim().length > 0)
148
+ n++;
149
+ }
150
+ return n;
151
+ }
152
+ /** Quick predicate — was anything ever queued? */
153
+ export function hasPendingOps(pathOverride) {
154
+ return countPending(pathOverride) > 0;
155
+ }
156
+ function ensureQueueFile(queuePath) {
157
+ const dir = dirname(queuePath);
158
+ if (!existsSync(dir))
159
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
160
+ if (!existsSync(queuePath)) {
161
+ writeFileSync(queuePath, '', { encoding: 'utf-8', mode: 0o600 });
162
+ try {
163
+ chmodSync(queuePath, 0o600);
164
+ }
165
+ catch {
166
+ // ignore
167
+ }
168
+ }
169
+ }
170
+ //# sourceMappingURL=queue.js.map
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Metric extraction primitive (task P1).
3
+ *
4
+ * Scans a log buffer line by line for named regex patterns and emits one
5
+ * `ExtractedMetric` per match. Used by the pugi_eval harness to lift
6
+ * karpathy-style flat log lines (e.g. `e2e: total=51 pass=45 fail=6
7
+ * dur=170.48s`) into structured measurements without committing to a
8
+ * full log-parser dependency.
9
+ *
10
+ * Contract:
11
+ * - Every supplied pattern MUST contain a numeric capture group 1.
12
+ * Patterns lacking a capture group throw a clear error rather than
13
+ * silently emitting NaN — a missing group is a programming bug, not
14
+ * a data condition.
15
+ * - Lines that don't match are skipped, not reported. Callers that
16
+ * need per-line trace coverage should layer their own scanner.
17
+ * - Each match emits a fresh ISO-8601 timestamp via `new Date()`. The
18
+ * timestamp reflects extraction time, not the moment the log line
19
+ * was produced — log producers that need true event time should
20
+ * embed it themselves and pattern it out.
21
+ * - Regex `lastIndex` state is not relied upon: we test each line as
22
+ * a self-contained string. Patterns may be sticky or global without
23
+ * affecting correctness.
24
+ */
25
+ /**
26
+ * Scan `logText` for each named pattern and emit one metric per match.
27
+ *
28
+ * Output order: outer loop over the patterns map insertion order, inner
29
+ * loop over lines in source order. So all `pugi_score` hits arrive
30
+ * before all `tokens_in` hits when both patterns fire on the same
31
+ * buffer. Tests rely on this stable ordering; do not switch to a
32
+ * line-major loop without updating the spec.
33
+ */
34
+ export function extractMetrics(logText, patterns) {
35
+ const lines = logText.split(/\r?\n/);
36
+ const out = [];
37
+ for (const [name, pattern] of Object.entries(patterns)) {
38
+ assertHasCaptureGroup(name, pattern);
39
+ for (let i = 0; i < lines.length; i += 1) {
40
+ const line = lines[i] ?? '';
41
+ const match = line.match(pattern);
42
+ if (!match)
43
+ continue;
44
+ const raw = match[1];
45
+ if (raw === undefined)
46
+ continue;
47
+ const value = parseFloat(raw);
48
+ if (!Number.isFinite(value))
49
+ continue;
50
+ out.push({
51
+ name,
52
+ value,
53
+ ts: new Date().toISOString(),
54
+ line: i + 1,
55
+ });
56
+ }
57
+ }
58
+ return out;
59
+ }
60
+ /**
61
+ * Common pugi_eval log patterns. Case-insensitive on the metric key so
62
+ * `Pugi_Score`, `PUGI_SCORE`, and `pugi_score` all match — karpathy-style
63
+ * harness output varies. The numeric capture group is always group 1.
64
+ *
65
+ * Separator class `[:\s=]+` accepts `key: value`, `key=value`, and
66
+ * `key value` shapes. Numeric class `[0-9.]+` is liberal enough for
67
+ * scientific-notation-free floats; exotic encodings (1e6, NaN, hex)
68
+ * are not in scope — pugi_eval emits decimal-only.
69
+ */
70
+ export const DEFAULT_PATTERNS = {
71
+ pugi_score: /pugi_score[:\s=]+([0-9.]+)/i,
72
+ tokens_in: /tokens.in[:\s=]+([0-9]+)/i,
73
+ tokens_out: /tokens.out[:\s=]+([0-9]+)/i,
74
+ wall_clock_sec: /wall.clock.*?([0-9.]+)\s*s(?:ec)?/i,
75
+ dur: /dur[:\s=]+([0-9.]+)/i,
76
+ pass: /pass[:\s=]+([0-9]+)/i,
77
+ fail: /fail[:\s=]+([0-9]+)/i,
78
+ };
79
+ /**
80
+ * Validate that `pattern` has at least one capture group by probing the
81
+ * regex source. Throws a clear `Error` rather than letting `match[1]`
82
+ * silently return `undefined` at every line — a missing group is a
83
+ * programmer error and we want it loud on first call.
84
+ *
85
+ * Detection is heuristic but conservative: counts `(` characters that
86
+ * are not preceded by `\` and not opening a `(?:` non-capturing group
87
+ * or `(?=` / `(?!` lookaround. Good enough for the patterns we ship;
88
+ * patterns that fool the heuristic will still be caught by the runtime
89
+ * `parseFloat(undefined) → NaN` skip — they just won't emit metrics.
90
+ */
91
+ function assertHasCaptureGroup(name, pattern) {
92
+ const src = pattern.source;
93
+ let depth = 0;
94
+ for (let i = 0; i < src.length; i += 1) {
95
+ const ch = src[i];
96
+ if (ch === '\\') {
97
+ i += 1;
98
+ continue;
99
+ }
100
+ if (ch !== '(')
101
+ continue;
102
+ const next = src[i + 1];
103
+ const next2 = src[i + 2];
104
+ if (next === '?' && (next2 === ':' || next2 === '=' || next2 === '!' || next2 === '<')) {
105
+ continue;
106
+ }
107
+ depth += 1;
108
+ }
109
+ if (depth === 0) {
110
+ throw new Error(`extractMetrics: pattern "${name}" has no capture group; add (...) around the numeric value`);
111
+ }
112
+ }
113
+ //# sourceMappingURL=extract.js.map
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @file Defines the operational modes for the Roo agent, controlling tool access and behavior.
3
+ */
4
+ export const MODES = {
5
+ architect: {
6
+ name: 'architect',
7
+ description: 'Researches and proposes solutions, but does not implement them.',
8
+ allowed_groups: ['read', 'web', 'memory'],
9
+ system_prompt_addendum: 'You are an architect. Read + propose. Do not edit.',
10
+ },
11
+ code: {
12
+ name: 'code',
13
+ description: 'Implements code changes as specified.',
14
+ allowed_groups: ['read', 'edit', 'bash', 'web', 'mcp', 'memory'],
15
+ system_prompt_addendum: 'Implement как specified.',
16
+ },
17
+ ask: {
18
+ name: 'ask',
19
+ description: 'Answers questions about the codebase without making changes.',
20
+ allowed_groups: ['read', 'web', 'memory'],
21
+ system_prompt_addendum: 'Answer questions. Do not modify state.',
22
+ },
23
+ debug: {
24
+ name: 'debug',
25
+ description: 'Investigates issues and can run commands, but requires approval for edits.',
26
+ allowed_groups: ['read', 'bash', 'web', 'memory'],
27
+ system_prompt_addendum: 'Investigate root cause. Edit only after explicit user approval.',
28
+ },
29
+ orchestrator: {
30
+ name: 'orchestrator',
31
+ description: 'Coordinates sub-agents to accomplish complex tasks.',
32
+ allowed_groups: ['read', 'web', 'agent_spawn', 'memory'],
33
+ system_prompt_addendum: 'Coordinate sub-agents. Delegate edits.',
34
+ },
35
+ };
36
+ /**
37
+ * Checks if a specific tool is allowed in a given agent mode.
38
+ *
39
+ * @param mode The current mode of the agent.
40
+ * @param tool The name of the tool being checked.
41
+ * @param group The tool group the tool belongs to.
42
+ * @returns True if the tool is allowed, false otherwise.
43
+ */
44
+ export function isToolAllowed(mode, tool, group) {
45
+ const modeDef = MODES[mode];
46
+ if (!modeDef) {
47
+ return false;
48
+ }
49
+ const groupAllowed = modeDef.allowed_groups.includes(group);
50
+ if (!groupAllowed) {
51
+ return false;
52
+ }
53
+ const toolDenied = modeDef.denied_tools?.includes(tool) ?? false;
54
+ if (toolDenied) {
55
+ return false;
56
+ }
57
+ return true;
58
+ }
59
+ /**
60
+ * Retrieves the system prompt addendum for a given mode.
61
+ *
62
+ * @param mode The current mode of the agent.
63
+ * @returns The system prompt addendum string, or an empty string if none is defined.
64
+ */
65
+ export function getModeAddendum(mode) {
66
+ return MODES[mode]?.system_prompt_addendum ?? '';
67
+ }
68
+ //# sourceMappingURL=roo-modes.js.map