@pugi/cli 0.1.0-beta.98 → 1.0.0-alpha.1

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 (448) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +11 -191
  3. package/bin/pugi +8 -0
  4. package/package.json +15 -71
  5. package/postinstall.mjs +31 -0
  6. package/CHANGELOG.md +0 -132
  7. package/THIRD_PARTY_NOTICES.md +0 -40
  8. package/assets/pugi-mascot.ansi +0 -16
  9. package/assets/pugi-prozr2-mascot.ansi +0 -9
  10. package/bin/run.js +0 -34
  11. package/dist/commands/deploy.js +0 -439
  12. package/dist/commands/flatten.js +0 -191
  13. package/dist/commands/jobs-watch.js +0 -201
  14. package/dist/commands/jobs.js +0 -260
  15. package/dist/commands/retro.js +0 -210
  16. package/dist/commands/smoke.js +0 -133
  17. package/dist/core/agent-progress/cleanup.js +0 -134
  18. package/dist/core/agent-progress/schema.js +0 -144
  19. package/dist/core/agent-progress/writer.js +0 -101
  20. package/dist/core/agents/adaptive-router.js +0 -330
  21. package/dist/core/agents/loader.js +0 -104
  22. package/dist/core/agents/query-decomposer.js +0 -297
  23. package/dist/core/agents/registry.js +0 -69
  24. package/dist/core/approvals/shortcut-resolver.js +0 -98
  25. package/dist/core/artifact-chain/dispatcher.js +0 -148
  26. package/dist/core/artifact-chain/exporter.js +0 -164
  27. package/dist/core/artifact-chain/state.js +0 -243
  28. package/dist/core/artifact-chain/steps.js +0 -169
  29. package/dist/core/ask-user/question.js +0 -92
  30. package/dist/core/audit/audit-trail.js +0 -275
  31. package/dist/core/auth/ensure-authenticated.js +0 -129
  32. package/dist/core/auth/env-provider.js +0 -238
  33. package/dist/core/auto-open-browser.js +0 -128
  34. package/dist/core/auto-update/channels.js +0 -122
  35. package/dist/core/auto-update/checker.js +0 -241
  36. package/dist/core/auto-update/state.js +0 -235
  37. package/dist/core/bare-mode/index.js +0 -107
  38. package/dist/core/bash/redirect.js +0 -281
  39. package/dist/core/bash-classifier.js +0 -1397
  40. package/dist/core/checkpoint/resumer.js +0 -149
  41. package/dist/core/checkpoint/rewinder.js +0 -291
  42. package/dist/core/checkpoints/shadow-git.js +0 -670
  43. package/dist/core/citations/parser.js +0 -109
  44. package/dist/core/classifier/yolo-classifier.js +0 -88
  45. package/dist/core/clipboard.js +0 -70
  46. package/dist/core/codegraph/decision-store.js +0 -248
  47. package/dist/core/codegraph/detect-repo.js +0 -459
  48. package/dist/core/codegraph/install.js +0 -134
  49. package/dist/core/codegraph/offer-hook.js +0 -220
  50. package/dist/core/compact/auto-trigger.js +0 -96
  51. package/dist/core/compact/buffer-rewriter.js +0 -115
  52. package/dist/core/compact/summarizer.js +0 -208
  53. package/dist/core/compact/token-counter.js +0 -108
  54. package/dist/core/consensus/anvil-fanout.js +0 -276
  55. package/dist/core/consensus/diff-capture.js +0 -491
  56. package/dist/core/consensus/rubric.js +0 -233
  57. package/dist/core/context/builder.js +0 -114
  58. package/dist/core/context/compaction-events.js +0 -99
  59. package/dist/core/context/compaction.js +0 -602
  60. package/dist/core/context/index.js +0 -28
  61. package/dist/core/context/invariants.js +0 -250
  62. package/dist/core/context/markdown-loader.js +0 -288
  63. package/dist/core/context/markdown-traverse.js +0 -255
  64. package/dist/core/context/pugiignore.js +0 -316
  65. package/dist/core/context/repo-skeleton.js +0 -533
  66. package/dist/core/context/tool-eviction.js +0 -55
  67. package/dist/core/context/watcher.js +0 -342
  68. package/dist/core/context/working-set.js +0 -165
  69. package/dist/core/coordinator/agent-tools.js +0 -77
  70. package/dist/core/coordinator/agent-toolset.js +0 -65
  71. package/dist/core/coordinator/fsm.js +0 -73
  72. package/dist/core/coordinator/mode-fsm.js +0 -70
  73. package/dist/core/cost/rate-card.js +0 -129
  74. package/dist/core/cost/tracker.js +0 -221
  75. package/dist/core/credentials.js +0 -355
  76. package/dist/core/cron/scheduler.js +0 -138
  77. package/dist/core/denial-tracking/index.js +0 -8
  78. package/dist/core/denial-tracking/state.js +0 -264
  79. package/dist/core/diagnostics/probe-runner.js +0 -93
  80. package/dist/core/diagnostics/probes/api.js +0 -46
  81. package/dist/core/diagnostics/probes/auth.js +0 -93
  82. package/dist/core/diagnostics/probes/bare-mode.js +0 -42
  83. package/dist/core/diagnostics/probes/cli-version.js +0 -127
  84. package/dist/core/diagnostics/probes/config.js +0 -72
  85. package/dist/core/diagnostics/probes/denial-tracking.js +0 -57
  86. package/dist/core/diagnostics/probes/disk.js +0 -81
  87. package/dist/core/diagnostics/probes/engine-live.js +0 -46
  88. package/dist/core/diagnostics/probes/git.js +0 -65
  89. package/dist/core/diagnostics/probes/hooks.js +0 -118
  90. package/dist/core/diagnostics/probes/mcp.js +0 -75
  91. package/dist/core/diagnostics/probes/node.js +0 -59
  92. package/dist/core/diagnostics/probes/pnpm.js +0 -36
  93. package/dist/core/diagnostics/probes/pugi-md.js +0 -89
  94. package/dist/core/diagnostics/probes/sandbox.js +0 -72
  95. package/dist/core/diagnostics/probes/session.js +0 -74
  96. package/dist/core/diagnostics/probes/status-snapshot.js +0 -488
  97. package/dist/core/diagnostics/probes/workspace.js +0 -63
  98. package/dist/core/diagnostics/types.js +0 -70
  99. package/dist/core/dispatch/cache-cleanup.js +0 -197
  100. package/dist/core/dispatch/cache-handoff.js +0 -295
  101. package/dist/core/edits/apply-patch-layer-e.js +0 -189
  102. package/dist/core/edits/dispatch.js +0 -511
  103. package/dist/core/edits/format-detector.js +0 -260
  104. package/dist/core/edits/format-matrix.js +0 -26
  105. package/dist/core/edits/fuzzy-ladder.js +0 -650
  106. package/dist/core/edits/index.js +0 -19
  107. package/dist/core/edits/journal.js +0 -199
  108. package/dist/core/edits/layer-a-apply.js +0 -217
  109. package/dist/core/edits/layer-a-fuzzy-apply.js +0 -198
  110. package/dist/core/edits/layer-b-apply.js +0 -211
  111. package/dist/core/edits/layer-c-apply.js +0 -160
  112. package/dist/core/edits/layer-d-ast.js +0 -572
  113. package/dist/core/edits/marker-parser.js +0 -401
  114. package/dist/core/edits/security-gate.js +0 -223
  115. package/dist/core/edits/verify-hook.js +0 -273
  116. package/dist/core/edits/worktree.js +0 -322
  117. package/dist/core/engine/adapter-runner.js +0 -8
  118. package/dist/core/engine/anvil-client.js +0 -344
  119. package/dist/core/engine/auto-compact.js +0 -179
  120. package/dist/core/engine/budgets.js +0 -192
  121. package/dist/core/engine/context-prefix.js +0 -155
  122. package/dist/core/engine/index.js +0 -12
  123. package/dist/core/engine/intensity.js +0 -163
  124. package/dist/core/engine/intent.js +0 -260
  125. package/dist/core/engine/native-pugi.js +0 -1616
  126. package/dist/core/engine/noop.js +0 -27
  127. package/dist/core/engine/prompts.js +0 -236
  128. package/dist/core/engine/strip-internal-fields.js +0 -124
  129. package/dist/core/engine/tool-bridge.js +0 -2173
  130. package/dist/core/engine/verification-patterns.js +0 -195
  131. package/dist/core/evaluation/golden-dataset.js +0 -293
  132. package/dist/core/feedback/queue.js +0 -177
  133. package/dist/core/feedback/submitter.js +0 -145
  134. package/dist/core/file-cache.js +0 -141
  135. package/dist/core/flatten/flatten-repo.js +0 -439
  136. package/dist/core/format/osc8-link.js +0 -28
  137. package/dist/core/hook-chains.js +0 -392
  138. package/dist/core/hooks/citation-verify-hook.js +0 -138
  139. package/dist/core/hooks/citation-verify.js +0 -112
  140. package/dist/core/hooks/events.js +0 -46
  141. package/dist/core/hooks/index.js +0 -15
  142. package/dist/core/hooks/registry.js +0 -216
  143. package/dist/core/hooks/runner.js +0 -236
  144. package/dist/core/hooks/v2/event-emitter.js +0 -115
  145. package/dist/core/hooks/v2/executor.js +0 -282
  146. package/dist/core/hooks/v2/index.js +0 -25
  147. package/dist/core/hooks/v2/lifecycle.js +0 -104
  148. package/dist/core/hooks/v2/loader.js +0 -216
  149. package/dist/core/hooks/v2/matcher.js +0 -125
  150. package/dist/core/hooks/v2/trust.js +0 -143
  151. package/dist/core/hooks/v2/types.js +0 -86
  152. package/dist/core/hooks/worktree-events.js +0 -158
  153. package/dist/core/hooks.js +0 -415
  154. package/dist/core/image/renderer.js +0 -71
  155. package/dist/core/index-store.js +0 -260
  156. package/dist/core/init/detector.js +0 -582
  157. package/dist/core/init/template-renderer.js +0 -242
  158. package/dist/core/jobs/registry.js +0 -462
  159. package/dist/core/ledger/results-tsv.js +0 -142
  160. package/dist/core/log-discipline/stdout-redirect.js +0 -51
  161. package/dist/core/lsp/cache.js +0 -105
  162. package/dist/core/lsp/client.js +0 -1229
  163. package/dist/core/lsp/language-detect.js +0 -66
  164. package/dist/core/lsp/post-edit-diagnostics.js +0 -171
  165. package/dist/core/lsp/server-detect.js +0 -173
  166. package/dist/core/lsp/symbol-cache.js +0 -162
  167. package/dist/core/lsp/symbol-tools.js +0 -664
  168. package/dist/core/mcp/client.js +0 -385
  169. package/dist/core/mcp/http-server.js +0 -553
  170. package/dist/core/mcp/orchestrator-config.js +0 -192
  171. package/dist/core/mcp/orchestrator-tools.js +0 -806
  172. package/dist/core/mcp/permission.js +0 -190
  173. package/dist/core/mcp/registry.js +0 -193
  174. package/dist/core/mcp/server-tools.js +0 -219
  175. package/dist/core/mcp/server.js +0 -397
  176. package/dist/core/mcp/trust.js +0 -91
  177. package/dist/core/memory/dual-write.js +0 -416
  178. package/dist/core/memory/passive-extract.js +0 -130
  179. package/dist/core/memory/phase1-kinds.js +0 -20
  180. package/dist/core/memory/secret-scanner.js +0 -304
  181. package/dist/core/memory-sync/queue.js +0 -170
  182. package/dist/core/metrics/extract.js +0 -113
  183. package/dist/core/modes/roo-modes.js +0 -68
  184. package/dist/core/onboarding/ensure-initialized.js +0 -133
  185. package/dist/core/onboarding/marker.js +0 -111
  186. package/dist/core/onboarding/telemetry-state.js +0 -108
  187. package/dist/core/output-style/presets.js +0 -176
  188. package/dist/core/output-style/state.js +0 -185
  189. package/dist/core/path-security.js +0 -345
  190. package/dist/core/permission.js +0 -369
  191. package/dist/core/permissions/auto-classifier.js +0 -124
  192. package/dist/core/permissions/bash-parser.js +0 -371
  193. package/dist/core/permissions/circuit-breaker.js +0 -83
  194. package/dist/core/permissions/constrained-edit.js +0 -91
  195. package/dist/core/permissions/gate.js +0 -278
  196. package/dist/core/permissions/index.js +0 -20
  197. package/dist/core/permissions/mode.js +0 -174
  198. package/dist/core/permissions/network-egress.js +0 -137
  199. package/dist/core/permissions/state.js +0 -241
  200. package/dist/core/permissions/tool-class.js +0 -107
  201. package/dist/core/plan-mode/ui-state.js +0 -51
  202. package/dist/core/plans/plan-artifact.js +0 -721
  203. package/dist/core/policy-limits/etag-store.js +0 -122
  204. package/dist/core/prd-check/parser.js +0 -215
  205. package/dist/core/prd-check/reporter.js +0 -127
  206. package/dist/core/prd-check/session-review.js +0 -557
  207. package/dist/core/prd-check/verifiers.js +0 -223
  208. package/dist/core/prompt-cache/client-cache.js +0 -99
  209. package/dist/core/prompts/assembly.js +0 -29
  210. package/dist/core/prompts/registry.js +0 -364
  211. package/dist/core/pugi-gitignore.js +0 -52
  212. package/dist/core/pugi-md/cc-compat-rules.js +0 -735
  213. package/dist/core/pugi-md/context-injector.js +0 -76
  214. package/dist/core/pugi-md/walk-up.js +0 -207
  215. package/dist/core/python/uv-installer.js +0 -270
  216. package/dist/core/python/uv-resolver.js +0 -83
  217. package/dist/core/rate-limit/narrator.js +0 -146
  218. package/dist/core/recipes/cli-types.js +0 -20
  219. package/dist/core/recipes/loader.js +0 -103
  220. package/dist/core/recipes/runner.js +0 -345
  221. package/dist/core/recipes/schema.js +0 -587
  222. package/dist/core/release-notes/parser.js +0 -241
  223. package/dist/core/release-notes/state.js +0 -116
  224. package/dist/core/repl/ask.js +0 -512
  225. package/dist/core/repl/cancellation.js +0 -98
  226. package/dist/core/repl/cap-warning.js +0 -91
  227. package/dist/core/repl/clipboard-read.js +0 -174
  228. package/dist/core/repl/dispatch-fsm.js +0 -220
  229. package/dist/core/repl/engine-bridge.js +0 -303
  230. package/dist/core/repl/history-search.js +0 -175
  231. package/dist/core/repl/history.js +0 -182
  232. package/dist/core/repl/kill-ring.js +0 -138
  233. package/dist/core/repl/model-pricing.js +0 -135
  234. package/dist/core/repl/privacy-banner.js +0 -71
  235. package/dist/core/repl/session.js +0 -4962
  236. package/dist/core/repl/slash-commands.js +0 -747
  237. package/dist/core/repl/store/index.js +0 -12
  238. package/dist/core/repl/store/jsonl-log.js +0 -321
  239. package/dist/core/repl/store/lockfile.js +0 -155
  240. package/dist/core/repl/store/session-store.js +0 -821
  241. package/dist/core/repl/store/types.js +0 -44
  242. package/dist/core/repl/store/uuid-v7.js +0 -68
  243. package/dist/core/repl/tool-route.js +0 -382
  244. package/dist/core/repl/workspace-context.js +0 -206
  245. package/dist/core/repo-map/build.js +0 -125
  246. package/dist/core/repo-map/cache.js +0 -185
  247. package/dist/core/repo-map/extractor.js +0 -254
  248. package/dist/core/repo-map/formatter.js +0 -145
  249. package/dist/core/repo-map/page-rank.js +0 -105
  250. package/dist/core/repo-map/scanner.js +0 -211
  251. package/dist/core/retro/git-collector.js +0 -251
  252. package/dist/core/retro/health-card.js +0 -25
  253. package/dist/core/retro/metrics.js +0 -342
  254. package/dist/core/retro/narrative.js +0 -249
  255. package/dist/core/retro/plane-collector.js +0 -274
  256. package/dist/core/retro/pr-issue-link.js +0 -65
  257. package/dist/core/retro/types.js +0 -16
  258. package/dist/core/retry-budget/budget.js +0 -284
  259. package/dist/core/retry-budget/index.js +0 -5
  260. package/dist/core/retry-budget/retry-cap.js +0 -74
  261. package/dist/core/routing/lead-worker.js +0 -43
  262. package/dist/core/routing/pre-flight-estimator.js +0 -108
  263. package/dist/core/runs/run-tree.js +0 -103
  264. package/dist/core/sandboxing/adapter.js +0 -29
  265. package/dist/core/sandboxing/index.js +0 -49
  266. package/dist/core/sandboxing/none.js +0 -19
  267. package/dist/core/sandboxing/seatbelt.js +0 -183
  268. package/dist/core/security/injection-scanner.js +0 -367
  269. package/dist/core/security/output-filter.js +0 -418
  270. package/dist/core/session/env-file.js +0 -105
  271. package/dist/core/session/section-budgets.js +0 -140
  272. package/dist/core/session.js +0 -377
  273. package/dist/core/settings.js +0 -400
  274. package/dist/core/share/formatter.js +0 -271
  275. package/dist/core/share/redactor.js +0 -221
  276. package/dist/core/share/uploader.js +0 -267
  277. package/dist/core/skills/defaults.js +0 -457
  278. package/dist/core/skills/loader.js +0 -454
  279. package/dist/core/skills/sources.js +0 -480
  280. package/dist/core/skills/trust.js +0 -172
  281. package/dist/core/smoke/headless-driver.js +0 -174
  282. package/dist/core/smoke/orchestrator.js +0 -194
  283. package/dist/core/smoke/runner.js +0 -238
  284. package/dist/core/smoke/scenario-parser.js +0 -316
  285. package/dist/core/statusline.js +0 -99
  286. package/dist/core/subagents/dispatcher-real.js +0 -600
  287. package/dist/core/subagents/dispatcher.js +0 -352
  288. package/dist/core/subagents/index.js +0 -39
  289. package/dist/core/subagents/isolation-matrix.js +0 -213
  290. package/dist/core/subagents/spawn.js +0 -101
  291. package/dist/core/telemetry/emitter.js +0 -229
  292. package/dist/core/telemetry/queue.js +0 -251
  293. package/dist/core/theme/context.js +0 -91
  294. package/dist/core/theme/presets.js +0 -228
  295. package/dist/core/theme/state.js +0 -181
  296. package/dist/core/todos/invariant.js +0 -10
  297. package/dist/core/todos/state.js +0 -177
  298. package/dist/core/tool-schema/compressor.js +0 -89
  299. package/dist/core/transport/version-interceptor.js +0 -166
  300. package/dist/core/trust.js +0 -109
  301. package/dist/core/tui/thinking-block.js +0 -64
  302. package/dist/core/vim/keymap.js +0 -288
  303. package/dist/core/vim/state.js +0 -92
  304. package/dist/core/watch-markers/marker-watcher.js +0 -133
  305. package/dist/core/worktree/include-parser.js +0 -249
  306. package/dist/core/worktree-manager/cleanup.js +0 -123
  307. package/dist/core/worktree-manager/manager.js +0 -303
  308. package/dist/index.js +0 -44
  309. package/dist/runtime/bootstrap.js +0 -190
  310. package/dist/runtime/cli.js +0 -8121
  311. package/dist/runtime/commands/agents.js +0 -385
  312. package/dist/runtime/commands/budget.js +0 -192
  313. package/dist/runtime/commands/cancel.js +0 -231
  314. package/dist/runtime/commands/chain.js +0 -489
  315. package/dist/runtime/commands/codegraph-status.js +0 -227
  316. package/dist/runtime/commands/compact.js +0 -297
  317. package/dist/runtime/commands/config.js +0 -595
  318. package/dist/runtime/commands/cost.js +0 -199
  319. package/dist/runtime/commands/delegate.js +0 -312
  320. package/dist/runtime/commands/dispatch.js +0 -126
  321. package/dist/runtime/commands/doctor.js +0 -579
  322. package/dist/runtime/commands/feedback.js +0 -184
  323. package/dist/runtime/commands/hooks.js +0 -187
  324. package/dist/runtime/commands/init.js +0 -254
  325. package/dist/runtime/commands/lsp.js +0 -368
  326. package/dist/runtime/commands/mcp.js +0 -935
  327. package/dist/runtime/commands/memory.js +0 -582
  328. package/dist/runtime/commands/model.js +0 -237
  329. package/dist/runtime/commands/onboarding.js +0 -275
  330. package/dist/runtime/commands/patch.js +0 -128
  331. package/dist/runtime/commands/permissions.js +0 -112
  332. package/dist/runtime/commands/plan.js +0 -143
  333. package/dist/runtime/commands/prd-check.js +0 -285
  334. package/dist/runtime/commands/privacy.js +0 -107
  335. package/dist/runtime/commands/recipe.js +0 -325
  336. package/dist/runtime/commands/redo-blob-store.js +0 -92
  337. package/dist/runtime/commands/redo.js +0 -361
  338. package/dist/runtime/commands/release-notes.js +0 -229
  339. package/dist/runtime/commands/repo-map.js +0 -95
  340. package/dist/runtime/commands/report.js +0 -299
  341. package/dist/runtime/commands/resume.js +0 -118
  342. package/dist/runtime/commands/review-consensus.js +0 -414
  343. package/dist/runtime/commands/rewind.js +0 -333
  344. package/dist/runtime/commands/roster.js +0 -117
  345. package/dist/runtime/commands/sessions.js +0 -163
  346. package/dist/runtime/commands/share.js +0 -316
  347. package/dist/runtime/commands/skills.js +0 -401
  348. package/dist/runtime/commands/status.js +0 -186
  349. package/dist/runtime/commands/stickers.js +0 -82
  350. package/dist/runtime/commands/style.js +0 -194
  351. package/dist/runtime/commands/theme.js +0 -196
  352. package/dist/runtime/commands/undo.js +0 -361
  353. package/dist/runtime/commands/update.js +0 -289
  354. package/dist/runtime/commands/vim.js +0 -140
  355. package/dist/runtime/commands/worktree.js +0 -177
  356. package/dist/runtime/commands/worktrees.js +0 -155
  357. package/dist/runtime/deprecation-warning.js +0 -69
  358. package/dist/runtime/engine-exit-code.js +0 -50
  359. package/dist/runtime/headless-repl.js +0 -195
  360. package/dist/runtime/headless.js +0 -548
  361. package/dist/runtime/load-hooks-or-exit.js +0 -71
  362. package/dist/runtime/plan-decompose.js +0 -531
  363. package/dist/runtime/sigint-guard.js +0 -272
  364. package/dist/runtime/stream-renderer.js +0 -195
  365. package/dist/runtime/update-check.js +0 -294
  366. package/dist/runtime/version.js +0 -65
  367. package/dist/runtime/worktree-bootstrap.js +0 -579
  368. package/dist/skills/bundled/batch.js +0 -617
  369. package/dist/skills/bundled/index.js +0 -45
  370. package/dist/skills/bundled/loop.js +0 -358
  371. package/dist/skills/bundled/remember.js +0 -383
  372. package/dist/skills/bundled/simplify.js +0 -289
  373. package/dist/skills/bundled/skillify.js +0 -373
  374. package/dist/skills/bundled/stuck.js +0 -558
  375. package/dist/skills/bundled/verify.js +0 -439
  376. package/dist/testing/vcr.js +0 -486
  377. package/dist/tools/agent-tool.js +0 -229
  378. package/dist/tools/apply-patch.js +0 -556
  379. package/dist/tools/ask-user-question.js +0 -337
  380. package/dist/tools/ask-user.js +0 -115
  381. package/dist/tools/bash.js +0 -1238
  382. package/dist/tools/brief.js +0 -224
  383. package/dist/tools/cron.js +0 -433
  384. package/dist/tools/enter-worktree.js +0 -250
  385. package/dist/tools/exit-worktree.js +0 -147
  386. package/dist/tools/file-tools.js +0 -553
  387. package/dist/tools/http-request.js +0 -336
  388. package/dist/tools/lsp-tools.js +0 -565
  389. package/dist/tools/mcp-tool.js +0 -260
  390. package/dist/tools/multi-edit.js +0 -361
  391. package/dist/tools/powershell.js +0 -268
  392. package/dist/tools/registry.js +0 -166
  393. package/dist/tools/server-tools.js +0 -892
  394. package/dist/tools/skill-tool.js +0 -96
  395. package/dist/tools/sleep.js +0 -99
  396. package/dist/tools/synthetic-output.js +0 -133
  397. package/dist/tools/tasks.js +0 -208
  398. package/dist/tools/todo-write.js +0 -184
  399. package/dist/tools/verify-plan-execution.js +0 -295
  400. package/dist/tools/web-fetch-injection-scanner.js +0 -207
  401. package/dist/tools/web-fetch.js +0 -720
  402. package/dist/tools/web-search.js +0 -458
  403. package/dist/tui/agent-progress-card.js +0 -111
  404. package/dist/tui/agent-tree-pane.js +0 -9
  405. package/dist/tui/agent-tree.js +0 -87
  406. package/dist/tui/ask-cli.js +0 -52
  407. package/dist/tui/ask-modal.js +0 -211
  408. package/dist/tui/ask-user-question-chips.js +0 -315
  409. package/dist/tui/ask-user-question-prompt.js +0 -203
  410. package/dist/tui/compact-banner.js +0 -81
  411. package/dist/tui/conversation-pane.js +0 -164
  412. package/dist/tui/cost-table.js +0 -111
  413. package/dist/tui/device-flow.js +0 -142
  414. package/dist/tui/doctor-table.js +0 -46
  415. package/dist/tui/feedback-prompt.js +0 -156
  416. package/dist/tui/input-box.js +0 -732
  417. package/dist/tui/login-picker.js +0 -69
  418. package/dist/tui/markdown-render.js +0 -266
  419. package/dist/tui/multi-file-diff-approval.js +0 -375
  420. package/dist/tui/onboarding-wizard.js +0 -240
  421. package/dist/tui/permissions-picker.js +0 -86
  422. package/dist/tui/render.js +0 -160
  423. package/dist/tui/repl-render.js +0 -770
  424. package/dist/tui/repl-splash-art.js +0 -64
  425. package/dist/tui/repl-splash-mascot.js +0 -154
  426. package/dist/tui/repl-splash.js +0 -117
  427. package/dist/tui/repl.js +0 -378
  428. package/dist/tui/slash-palette.js +0 -106
  429. package/dist/tui/splash-data.js +0 -61
  430. package/dist/tui/splash.js +0 -31
  431. package/dist/tui/status-bar.js +0 -209
  432. package/dist/tui/status-table.js +0 -7
  433. package/dist/tui/stickers-art.js +0 -136
  434. package/dist/tui/style-table.js +0 -28
  435. package/dist/tui/theme-table.js +0 -29
  436. package/dist/tui/thinking-spinner.js +0 -123
  437. package/dist/tui/tool-stream-pane.js +0 -140
  438. package/dist/tui/update-banner.js +0 -33
  439. package/dist/tui/vim-input.js +0 -267
  440. package/dist/tui/welcome-banner.js +0 -107
  441. package/dist/tui/welcome-data.js +0 -293
  442. package/dist/tui/workspace-context.js +0 -105
  443. package/docs/examples/codegraph.mcp.json +0 -10
  444. package/test/scenarios/codegen-create-file.scenario.txt +0 -13
  445. package/test/scenarios/compact-force.scenario.txt +0 -12
  446. package/test/scenarios/identity.scenario.txt +0 -11
  447. package/test/scenarios/persona-handoff.scenario.txt +0 -12
  448. package/test/scenarios/walkback.scenario.txt +0 -12
@@ -1,458 +0,0 @@
1
- /**
2
- * web_search tool — β1b T4 .
3
- *
4
- * One-shot web search via the Anvil-proxied Brave Search API. Mirrors
5
- * the gate + SSRF + sanitize + rate-limit posture of
6
- * `tools/web-fetch.ts`. Returns the top-N {title, url, snippet}
7
- * results wrapped in an `<untrusted-search-NONCE>` sentinel so the
8
- * downstream prompt treats every snippet as data, never as
9
- * instructions.
10
- *
11
- * Why an Anvil proxy and not a direct Brave API call:
12
- * - The Brave Search subscription key is server-side. Shipping it in
13
- * a customer CLI bundle would burn the rotation budget the first
14
- * time the binary lands on a phishing repo.
15
- * - The Anvil proxy already terminates the customer's `PUGI_API_KEY`
16
- * for tenancy + per-tier rate-limiting + audit logging. Routing
17
- * search through the same boundary keeps the security model
18
- * uniform with `pugi engine` and `pugi sync`.
19
- * - For local dev / offline testing the env var
20
- * `PUGI_SEARCH_PROXY_URL` overrides the default proxy endpoint;
21
- * unit tests inject `_setSearchFetchForTests()` so no network is
22
- * touched.
23
- *
24
- * Gate: the tool refuses unless one of:
25
- * 1. `settings.web.search.enabled === true` (persistent opt-in)
26
- * 2. `allowSearch === true` (CLI flag `--allow-search`, single dispatch)
27
- *
28
- * SSRF: the proxy URL itself runs through `validateHostnameForFetch`
29
- * from `web-fetch.ts` so a configured `PUGI_SEARCH_PROXY_URL=
30
- * http://169.254.169.254/...` cannot ride to cloud metadata. We also
31
- * apply the β1b #62 DNS-rebinding guard via the shared
32
- * `pinnedAddressAgent` helper (web-fetch.ts) — the same SSRF window
33
- * that fetch closes, search closes.
34
- *
35
- * Rate limit: in-memory session bucket — 5 requests per rolling 60 s
36
- * keyed by `sessionId`. Burst above the cap returns
37
- * `{ ok: false, error: 'rate_limited' }` immediately, no network
38
- * dispatch. Per-session because a noisy session should not starve a
39
- * quieter one in the same workstation process.
40
- *
41
- * Response size cap: hard 1 MiB ceiling on the proxy body. Anything
42
- * larger drops the entire response with `oversized_response` — the
43
- * proxy already normalizes Brave's payload to a small JSON shape, so
44
- * 1 MiB is generous for the typical 10-result payload (~5 KiB).
45
- *
46
- * Sanitization: snippet text is stripped of HTML tags before being
47
- * surfaced to the model. Brave returns raw HTML in `description` for
48
- * highlight markup; we keep the visible text and drop every `<...>`
49
- * span via a conservative regex. Cross-checked by the `script/style`
50
- * spec which asserts a hostile `<script>alert(1)</script>` snippet
51
- * lands as the inner text only.
52
- *
53
- * Brand voice: brief / dispatch / ship / sentinel only — brandbook §08.
54
- */
55
- import { request, Agent } from 'undici';
56
- import { randomBytes } from 'node:crypto';
57
- import { validateHostnameForFetch, validateIpLiteralForFetch } from './web-fetch.js';
58
- let activeFetch = null;
59
- /**
60
- * Test seam — pass `null` to restore the production undici dispatcher.
61
- * Mirrors the `_setLookupForTests` pattern from `web-fetch.ts`.
62
- */
63
- export function _setSearchFetchForTests(fn) {
64
- activeFetch = fn;
65
- }
66
- const SEARCH_TIMEOUT_MS = 10_000;
67
- const MAX_RESPONSE_BYTES = 1 * 1024 * 1024; // 1 MiB hard cap
68
- const MAX_RESULTS = 10;
69
- const DEFAULT_RESULTS = 10;
70
- const MAX_QUERY_LEN = 256;
71
- const RATE_LIMIT_WINDOW_MS = 60_000;
72
- const RATE_LIMIT_MAX = 5;
73
- const USER_AGENT = 'pugi-cli/0.1 (+https://pugi.dev)';
74
- const DEFAULT_PROXY_URL = 'https://api.pugi.io/api/pugi/web-search';
75
- export function isWebSearchEnabled(ctx) {
76
- if (ctx.allowSearch === true)
77
- return true;
78
- return ctx.settings.web?.search?.enabled === true;
79
- }
80
- const rateBuckets = new Map();
81
- /**
82
- * β1b r1: Map sweep cap so a long-running engine session that recycles
83
- * sessionIds (or a misconfigured caller that passes per-call random
84
- * ids) cannot grow `rateBuckets` unbounded. 1024 is well above any
85
- * realistic concurrent-session count for a single CLI process; when
86
- * we cross the cap we sweep expired buckets first, and if the Map is
87
- * still over the cap we evict the oldest-touched bucket. Pure LRU
88
- * would need another timestamp; oldest-by-insertion (Map iteration
89
- * order) is close enough for a defense-in-depth memory bound.
90
- */
91
- const RATE_BUCKETS_CAP = 1024;
92
- /**
93
- * Returns true when the dispatch is allowed and records the call;
94
- * returns false when the per-session bucket is at cap. Pure
95
- * in-process; CLI dispatch is short-lived so a Map is sufficient.
96
- *
97
- * Exported for spec reset between test cases.
98
- */
99
- export function _checkRateLimit(sessionId, now = Date.now()) {
100
- if (!sessionId)
101
- return true;
102
- let bucket = rateBuckets.get(sessionId);
103
- if (!bucket) {
104
- // β1b r1: sweep before allocating a new bucket so we never grow
105
- // past the cap. Sweep is O(n) but only runs when we are about to
106
- // breach the cap, so per-call cost stays effectively constant.
107
- if (rateBuckets.size >= RATE_BUCKETS_CAP) {
108
- sweepRateBuckets(now);
109
- }
110
- bucket = { timestamps: [] };
111
- rateBuckets.set(sessionId, bucket);
112
- }
113
- const cutoff = now - RATE_LIMIT_WINDOW_MS;
114
- bucket.timestamps = bucket.timestamps.filter((ts) => ts > cutoff);
115
- if (bucket.timestamps.length >= RATE_LIMIT_MAX)
116
- return false;
117
- bucket.timestamps.push(now);
118
- return true;
119
- }
120
- /**
121
- * Drop expired buckets (all timestamps older than the rate-limit
122
- * window) and, if still over the cap, evict the oldest-inserted
123
- * buckets until under cap. Memory-bound only — never affects rate
124
- * limit correctness for live sessions.
125
- */
126
- function sweepRateBuckets(now) {
127
- const cutoff = now - RATE_LIMIT_WINDOW_MS;
128
- for (const [id, b] of rateBuckets) {
129
- if (b.timestamps.every((ts) => ts <= cutoff)) {
130
- rateBuckets.delete(id);
131
- }
132
- }
133
- while (rateBuckets.size >= RATE_BUCKETS_CAP) {
134
- const oldest = rateBuckets.keys().next();
135
- if (oldest.done)
136
- break;
137
- rateBuckets.delete(oldest.value);
138
- }
139
- }
140
- /** Test seam — clear rate buckets between specs. */
141
- export function _resetRateLimitForTests() {
142
- rateBuckets.clear();
143
- }
144
- /* ----------------------- sanitization ---------------------- */
145
- /**
146
- * Strip HTML tags + collapse whitespace. Brave's `description` carries
147
- * highlight markup (`<strong>`); we keep the inner text only so the
148
- * model never sees raw HTML it could mistake for a directive. The
149
- * regex is conservative: `<[^>]*>` removes opening + closing tags +
150
- * self-closing tags + comments + processing instructions. Script
151
- * BODIES are removed via a second pass that drops the entire
152
- * `<script>...</script>` and `<style>...</style>` block (case-
153
- * insensitive) so even a server-rendered snippet cannot leak a
154
- * `console.log` line into the model context.
155
- */
156
- export function sanitizeSnippet(input) {
157
- if (!input)
158
- return '';
159
- // Drop full script + style blocks (with their bodies). The
160
- // non-greedy `.*?` handles "..." with embedded newlines via the `s`
161
- // flag.
162
- const noScripts = input
163
- .replace(/<script\b[^>]*>.*?<\/script>/gis, '')
164
- .replace(/<style\b[^>]*>.*?<\/style>/gis, '');
165
- // Drop any remaining tag.
166
- const noTags = noScripts.replace(/<[^>]*>/g, '');
167
- // Collapse whitespace runs. Brave already trims but defensive.
168
- return noTags.replace(/\s+/g, ' ').trim();
169
- }
170
- /**
171
- * HTML-escape for sentinel body — mirrors `escapeForSentinelBody` from
172
- * web-fetch.ts. Kept local to avoid coupling the modules tighter than
173
- * the SSRF helpers already do.
174
- */
175
- function escapeForSentinelBody(input) {
176
- return input
177
- .replace(/&/g, '&amp;')
178
- .replace(/</g, '&lt;')
179
- .replace(/>/g, '&gt;')
180
- .replace(/"/g, '&quot;')
181
- .replace(/'/g, '&#39;');
182
- }
183
- /* ----------------------- dispatcher ---------------------- */
184
- /**
185
- * Resolve the search proxy URL. `PUGI_SEARCH_PROXY_URL` env override
186
- * lets local dev point at a fixture server; production hits
187
- * `api.pugi.io`.
188
- */
189
- function resolveProxyUrl() {
190
- const env = process.env.PUGI_SEARCH_PROXY_URL;
191
- if (typeof env === 'string' && env.length > 0)
192
- return env;
193
- return DEFAULT_PROXY_URL;
194
- }
195
- /**
196
- * Production fetch via undici with the β1b #62 DNS-rebinding guard
197
- * applied. We resolve the proxy hostname, validate against the SSRF
198
- * blocklist, then dispatch via an Agent pinned to the resolved IP so
199
- * a hostile DNS server cannot answer a different address on the
200
- * connect(2) than on the lookup.
201
- */
202
- async function productionFetch(url, init) {
203
- // β1b #62 — pinned-address Dispatcher to close the lookup→connect
204
- // race. The Agent's `connect.lookup` always returns the address we
205
- // already validated, so DNS cannot flip between the SSRF guard and
206
- // the socket creation. Agent is per-call (small object) so process
207
- // teardown does not need to keep a long-lived dispatcher around.
208
- const parsed = new URL(url);
209
- const hostname = parsed.hostname.replace(/^\[|\]$/g, '');
210
- const dnsGuard = await validateHostnameForFetch(hostname);
211
- if (dnsGuard) {
212
- throw new Error(`SSRF refused: ${dnsGuard}`);
213
- }
214
- const { lookup: dnsLookup } = await import('node:dns/promises');
215
- const answers = await dnsLookup(hostname, { all: true, verbatim: true });
216
- if (answers.length === 0) {
217
- throw new Error(`DNS returned no answers for ${hostname}`);
218
- }
219
- // Pin the first answer of THIS lookup, then re-validate against the
220
- // SSRF blocklist. β1b r1: closes the DNS rebinding window where a
221
- // hostile resolver returns public IPs to `validateHostnameForFetch`
222
- // above and private IPs here. The lookup that feeds the pin is the
223
- // lookup whose answer the connect(2) will actually use; without a
224
- // re-check on this literal the original guard's IP set can diverge
225
- // from the IP we lock into `connect.lookup`.
226
- const pinned = answers[0];
227
- if (!pinned) {
228
- throw new Error(`DNS returned no answers for ${hostname}`);
229
- }
230
- const ipCheck = validateIpLiteralForFetch(pinned.address, pinned.family);
231
- if (ipCheck !== null) {
232
- throw new Error(`SSRF refused: ssrf_pinned_address_blocked: ${ipCheck}`);
233
- }
234
- const agent = new Agent({
235
- connect: {
236
- // Force the connect path to use the pre-resolved address. undici
237
- // accepts the standard Node `dns.LookupFunction` shape here.
238
- lookup: (_h, _opts, cb) => {
239
- cb(null, pinned.address, pinned.family);
240
- },
241
- },
242
- });
243
- try {
244
- return await request(url, { ...init, dispatcher: agent });
245
- }
246
- finally {
247
- await agent.close().catch(() => {
248
- /* best-effort */
249
- });
250
- }
251
- }
252
- async function readBodyWithCap(body, controller) {
253
- const chunks = [];
254
- let total = 0;
255
- try {
256
- for await (const chunk of body) {
257
- const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
258
- total += buf.length;
259
- if (total > MAX_RESPONSE_BYTES) {
260
- controller.abort();
261
- try {
262
- if (typeof body.destroy === 'function')
263
- body.destroy();
264
- }
265
- catch {
266
- /* ignore — already aborted */
267
- }
268
- return { ok: false, error: `oversized_response: > ${MAX_RESPONSE_BYTES} bytes` };
269
- }
270
- chunks.push(buf);
271
- }
272
- }
273
- catch (error) {
274
- const msg = error instanceof Error ? error.message : String(error);
275
- return { ok: false, error: `body_read_failed: ${msg}` };
276
- }
277
- return { ok: true, buffer: Buffer.concat(chunks) };
278
- }
279
- /**
280
- * Validate + clamp the optional `count` arg. Clamp into [1, MAX_RESULTS]
281
- * and default to DEFAULT_RESULTS when missing. The bridge already
282
- * validates the type — this enforces the bounds.
283
- */
284
- function resolveCount(raw) {
285
- if (raw === undefined)
286
- return DEFAULT_RESULTS;
287
- if (raw < 1)
288
- return 1;
289
- if (raw > MAX_RESULTS)
290
- return MAX_RESULTS;
291
- return Math.floor(raw);
292
- }
293
- /**
294
- * Sanitize the operator query before it crosses the proxy boundary.
295
- * Trim, hard-cap at MAX_QUERY_LEN, reject empty after trim. The proxy
296
- * also validates, but a CLI-side check keeps the model's mistake
297
- * visible to the operator (the failed call shows up in
298
- * `.pugi/events.jsonl`) rather than blamed on a 400 from the proxy.
299
- */
300
- function sanitizeQuery(raw) {
301
- const trimmed = raw.trim();
302
- if (trimmed.length === 0) {
303
- throw new Error('empty_query');
304
- }
305
- if (trimmed.length > MAX_QUERY_LEN) {
306
- return trimmed.slice(0, MAX_QUERY_LEN);
307
- }
308
- return trimmed;
309
- }
310
- export async function webSearchTool(input, ctx) {
311
- if (!isWebSearchEnabled(ctx)) {
312
- return {
313
- ok: false,
314
- error: 'web_search disabled. Enable with --allow-search or set web.search.enabled=true in .pugi/settings.json.',
315
- };
316
- }
317
- let query;
318
- try {
319
- query = sanitizeQuery(input.query);
320
- }
321
- catch (error) {
322
- return { ok: false, error: error instanceof Error ? error.message : String(error) };
323
- }
324
- if (!_checkRateLimit(ctx.sessionId)) {
325
- return {
326
- ok: false,
327
- error: `rate_limited: max ${RATE_LIMIT_MAX} searches per ${RATE_LIMIT_WINDOW_MS / 1000}s per session`,
328
- };
329
- }
330
- const count = resolveCount(input.count);
331
- const proxyUrl = resolveProxyUrl();
332
- // Validate the proxy URL parses + scheme + SSRF. Even though the
333
- // env var is operator-controlled, a careless export of
334
- // `http://localhost:8080` would otherwise let an unrelated local
335
- // server intercept the call. Default URL is `api.pugi.io` and
336
- // resolves clean.
337
- let parsedProxy;
338
- try {
339
- parsedProxy = new URL(proxyUrl);
340
- }
341
- catch {
342
- return { ok: false, error: `invalid_proxy_url: ${proxyUrl}` };
343
- }
344
- if (parsedProxy.protocol !== 'http:' && parsedProxy.protocol !== 'https:') {
345
- return { ok: false, error: `unsupported_proxy_scheme: ${parsedProxy.protocol}` };
346
- }
347
- const proxyHost = parsedProxy.hostname.replace(/^\[|\]$/g, '');
348
- // SSRF guard applies to the proxy hostname even when activeFetch is
349
- // a test stub — keeps the spec contract honest. Tests that exercise
350
- // the rate limiter use a stub that bypasses productionFetch but the
351
- // guard still runs at the entry above test stubs.
352
- const ssrfErr = await validateHostnameForFetch(proxyHost);
353
- if (ssrfErr) {
354
- return { ok: false, error: `SSRF refused: ${ssrfErr}` };
355
- }
356
- const controller = new AbortController();
357
- const timer = setTimeout(() => controller.abort(), SEARCH_TIMEOUT_MS);
358
- // β1b: lifted PUGI_API_KEY from the runtime credential store would
359
- // belong here for production — for the proxy call we forward whatever
360
- // PUGI_API_KEY the operator has exported. Anonymous proxy access is
361
- // permitted; the proxy treats it as the free-tier search quota.
362
- const apiKey = process.env.PUGI_API_KEY ?? process.env.PUGI_LOGIN_TOKEN;
363
- const headers = {
364
- 'user-agent': USER_AGENT,
365
- 'content-type': 'application/json',
366
- accept: 'application/json',
367
- };
368
- if (typeof apiKey === 'string' && apiKey.length > 0) {
369
- headers.authorization = `Bearer ${apiKey}`;
370
- }
371
- let response;
372
- try {
373
- const fetcher = activeFetch ?? (async (u, i) => (await productionFetch(u, i)));
374
- response = (await fetcher(proxyUrl, {
375
- method: 'POST',
376
- headers,
377
- body: JSON.stringify({ query, count }),
378
- signal: controller.signal,
379
- }));
380
- }
381
- catch (error) {
382
- clearTimeout(timer);
383
- const message = error instanceof Error ? error.message : String(error);
384
- return { ok: false, error: `search_failed: ${message}` };
385
- }
386
- finally {
387
- clearTimeout(timer);
388
- }
389
- if (response.statusCode < 200 || response.statusCode >= 300) {
390
- // Drain the body so the socket can be reused / released.
391
- try {
392
- if (typeof response.body.destroy === 'function')
393
- response.body.destroy();
394
- }
395
- catch {
396
- /* ignore */
397
- }
398
- return { ok: false, error: `proxy_http_${response.statusCode}` };
399
- }
400
- const bodyResult = await readBodyWithCap(response.body, controller);
401
- if (!bodyResult.ok)
402
- return bodyResult;
403
- let parsed;
404
- try {
405
- parsed = JSON.parse(bodyResult.buffer.toString('utf8'));
406
- }
407
- catch (error) {
408
- const msg = error instanceof Error ? error.message : String(error);
409
- return { ok: false, error: `proxy_malformed_json: ${msg}` };
410
- }
411
- // Expect the proxy to normalize Brave's payload to `{results: [{title, url, snippet}]}`.
412
- // When the proxy is missing the `results` array (or it is not an array)
413
- // we surface a clean error instead of returning empty.
414
- const obj = parsed;
415
- if (!obj || typeof obj !== 'object') {
416
- return { ok: false, error: 'proxy_malformed_response: not an object' };
417
- }
418
- const rawResults = obj.results;
419
- if (!Array.isArray(rawResults)) {
420
- return { ok: false, error: 'proxy_malformed_response: missing results array' };
421
- }
422
- const results = [];
423
- for (const r of rawResults.slice(0, count)) {
424
- if (!r || typeof r !== 'object')
425
- continue;
426
- const row = r;
427
- const title = typeof row.title === 'string' ? sanitizeSnippet(row.title) : '';
428
- const url = typeof row.url === 'string' ? row.url : '';
429
- const snippet = typeof row.snippet === 'string' ? sanitizeSnippet(row.snippet) : '';
430
- if (!url)
431
- continue;
432
- results.push({ title, url, snippet });
433
- }
434
- // Per-call nonce for the sentinel. Mirrors `web-fetch.ts` so the
435
- // downstream prompt's nonce-matching escape logic works uniformly.
436
- const nonce = randomBytes(8).toString('hex');
437
- const renderedLines = [`<untrusted-search-${nonce}>`, `Query: ${escapeForSentinelBody(query)}`, ''];
438
- for (let i = 0; i < results.length; i += 1) {
439
- const r = results[i];
440
- if (!r)
441
- continue;
442
- renderedLines.push(`${i + 1}. ${escapeForSentinelBody(r.title || r.url)}`);
443
- renderedLines.push(` ${escapeForSentinelBody(r.url)}`);
444
- if (r.snippet)
445
- renderedLines.push(` ${escapeForSentinelBody(r.snippet)}`);
446
- renderedLines.push('');
447
- }
448
- renderedLines.push(`</untrusted-search-${nonce}>`);
449
- const content_md = renderedLines.join('\n').trimEnd();
450
- return {
451
- ok: true,
452
- query,
453
- results,
454
- content_md,
455
- fetched_at: new Date().toISOString(),
456
- };
457
- }
458
- //# sourceMappingURL=web-search.js.map
@@ -1,111 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Text } from 'ink';
3
- /** Width of the progress bar in display cells. Tuned to fit comfortably
4
- * inside an 80-col terminal alongside the percent label. */
5
- export const PROGRESS_BAR_WIDTH = 24;
6
- /** Max milestone rows the card renders before collapsing to the footer
7
- * summary. Matches the CC `/compact` cutoff. */
8
- export const MAX_VISIBLE_MILESTONES = 5;
9
- const STATUS_GLYPH = {
10
- done: '◼',
11
- active: '▸',
12
- pending: '◻',
13
- };
14
- const STATUS_COLOR = {
15
- done: 'green',
16
- active: 'yellow',
17
- pending: 'gray',
18
- };
19
- const HEADER_DOT_COLOR = {
20
- running: 'cyan',
21
- completed: 'green',
22
- failed: 'red',
23
- };
24
- /**
25
- * Build the unicode progress bar. Exported для тесты — guarantees the
26
- * filled/empty counts match the percent under all rounding edges.
27
- */
28
- export function renderProgressBarCells(percent, width = PROGRESS_BAR_WIDTH) {
29
- const safePercent = Math.max(0, Math.min(100, percent));
30
- const cells = Math.round((safePercent / 100) * width);
31
- const clamped = Math.max(0, Math.min(width, cells));
32
- return {
33
- filled: '▰'.repeat(clamped),
34
- empty: '▱'.repeat(width - clamped),
35
- cells: clamped,
36
- };
37
- }
38
- /**
39
- * Format milliseconds as the `Hh Mm Ss` / `Mm Ss` / `Ss` label.
40
- * Mirrors the rule used by status-bar elapsed slot.
41
- */
42
- export function formatElapsed(ms) {
43
- const total = Math.max(0, Math.floor(ms / 1000));
44
- const h = Math.floor(total / 3600);
45
- const m = Math.floor((total % 3600) / 60);
46
- const s = total % 60;
47
- if (h > 0)
48
- return `${h}h ${m}m ${s}s`;
49
- if (m > 0)
50
- return `${m}m ${s}s`;
51
- return `${s}s`;
52
- }
53
- /**
54
- * Format a raw token count as `21.7k` / `3.4M` / `812`. Mirrors the
55
- * formatter in `core/repl/model-pricing.ts` so both surfaces stay
56
- * visually consistent without coupling.
57
- */
58
- export function formatTokenCount(n) {
59
- if (n === undefined)
60
- return undefined;
61
- if (n < 1_000)
62
- return `${n}`;
63
- if (n < 1_000_000) {
64
- const k = n / 1_000;
65
- return `${k >= 10 ? k.toFixed(1).replace(/\.0$/, '') : k.toFixed(1)}k`;
66
- }
67
- const m = n / 1_000_000;
68
- return `${m >= 10 ? m.toFixed(1).replace(/\.0$/, '') : m.toFixed(1)}M`;
69
- }
70
- /**
71
- * Compute the "… +N pending, M completed" footer counts. When the
72
- * agent supplied rollups they win; otherwise we derive from the
73
- * milestone array.
74
- */
75
- export function computeFooterCounts(milestones, visibleCount, rollup) {
76
- const pending = rollup.pendingCount
77
- ?? milestones.filter((m) => m.status === 'pending').length;
78
- const completed = rollup.completedCount
79
- ?? milestones.filter((m) => m.status === 'done').length;
80
- const hidden = Math.max(0, milestones.length - visibleCount);
81
- return { pending, completed, hidden };
82
- }
83
- function MilestoneRow({ milestone }) {
84
- const glyph = STATUS_GLYPH[milestone.status];
85
- const color = STATUS_COLOR[milestone.status];
86
- // Truncate to 64 chars so a verbose label can't wrap and break the
87
- // grid layout in the watcher.
88
- const label = milestone.label.length > 64
89
- ? `${milestone.label.slice(0, 63)}…`
90
- : milestone.label;
91
- return (_jsxs(Box, { children: [_jsx(Text, { children: ' ' }), _jsx(Text, { color: color, children: glyph }), _jsx(Text, { children: " " }), _jsx(Text, { color: color === 'gray' ? 'gray' : undefined, dimColor: milestone.status === 'pending', children: label })] }));
92
- }
93
- export function AgentProgressCard({ progress, nowEpochMs, }) {
94
- // Re-derive elapsed from the wall clock when the parent supplied it;
95
- // this is what makes the card tick once a second without the writer
96
- // re-emitting JSON every tick.
97
- const elapsed = nowEpochMs !== undefined
98
- ? Math.max(progress.elapsedMs, nowEpochMs - Date.parse(progress.startedAt))
99
- : progress.elapsedMs;
100
- const bar = renderProgressBarCells(progress.percentComplete);
101
- const percentLabel = `${Math.round(Math.max(0, Math.min(100, progress.percentComplete)))}%`;
102
- const tokensLabel = formatTokenCount(progress.tokensUsed);
103
- const dotColor = HEADER_DOT_COLOR[progress.status];
104
- const visibleMilestones = progress.milestones.slice(0, MAX_VISIBLE_MILESTONES);
105
- const footer = computeFooterCounts(progress.milestones, visibleMilestones.length, { pendingCount: progress.pendingCount, completedCount: progress.completedCount });
106
- // CC compact pattern: header has a leading `· ` glyph + the task label.
107
- // We append `…` only while running (matches CC's "Compacting…" verb form).
108
- const headerVerb = progress.status === 'running' ? '…' : '';
109
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 0, marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: dotColor, children: '· ' }), _jsx(Text, { bold: true, children: progress.agentType }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [progress.task, headerVerb] }), _jsxs(Text, { dimColor: true, children: [' (', formatElapsed(elapsed), tokensLabel ? ` · ↑ ${tokensLabel} tokens` : '', ')'] })] }), _jsxs(Box, { children: [_jsx(Text, { children: ' ' }), _jsx(Text, { color: "cyan", children: bar.filled }), _jsx(Text, { dimColor: true, children: bar.empty }), _jsxs(Text, { children: [' ', percentLabel] })] }), progress.stepDescription ? (_jsxs(Box, { children: [_jsx(Text, { children: ' ' }), _jsxs(Text, { dimColor: true, children: ["step ", progress.currentStep, "/", progress.totalSteps, ": ", progress.stepDescription] })] })) : null, visibleMilestones.length > 0 ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { children: ' ' }), _jsx(Text, { dimColor: true, children: "\u23BF" })] }), visibleMilestones.map((m, i) => (_jsx(MilestoneRow, { milestone: m }, `${m.label}-${i}`))), footer.hidden > 0 ? (_jsxs(Box, { children: [_jsx(Text, { children: ' ' }), _jsxs(Text, { dimColor: true, children: ["\u2026 +", footer.pending, " pending, ", footer.completed, " completed"] })] })) : null] })) : null] }));
110
- }
111
- //# sourceMappingURL=agent-progress-card.js.map
@@ -1,9 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Text } from 'ink';
3
- import { AgentTree } from './agent-tree.js';
4
- export function AgentTreePane(props) {
5
- const onWatch = props.agents.filter((a) => a.status === 'queued' || a.status === 'thinking').length;
6
- const total = props.agents.length;
7
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, dimColor: true, children: '─ agents ' }), _jsx(Text, { dimColor: true, children: `(${total} total, ${onWatch} on watch)` })] }), _jsx(AgentTree, { agents: props.agents, nowEpochMs: props.nowEpochMs })] }));
8
- }
9
- //# sourceMappingURL=agent-tree-pane.js.map
@@ -1,87 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Text } from 'ink';
3
- export function AgentTree(props) {
4
- if (props.agents.length === 0) {
5
- return (_jsx(Box, { children: _jsx(Text, { dimColor: true, children: "No agents on watch. Type a brief to dispatch one." }) }));
6
- }
7
- const now = props.nowEpochMs ?? Date.now();
8
- return (_jsx(Box, { flexDirection: "column", children: props.agents.map((agent, index) => (_jsx(AgentRow, { agent: agent, last: index === props.agents.length - 1, nowEpochMs: now }, agent.taskId))) }));
9
- }
10
- function AgentRow({ agent, last, nowEpochMs, }) {
11
- const branch = last ? '└' : '├';
12
- const glyph = statusGlyph(agent.status);
13
- const glyphColor = statusColor(agent.status);
14
- const elapsed = formatElapsed(agent.startedAtEpochMs, nowEpochMs);
15
- const tokens = formatTokens(agent.tokensIn + agent.tokensOut);
16
- const name = agent.personaName.padEnd(8, ' ');
17
- const role = agent.role.padEnd(10, ' ');
18
- const detail = agent.detail.length > 60 ? `${agent.detail.slice(0, 57)}…` : agent.detail;
19
- return (_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: ` ${branch} ` }), _jsx(Text, { bold: true, children: `${name}` }), _jsx(Text, { dimColor: true, children: ` ${role} ` }), _jsx(Text, { color: glyphColor, children: glyph }), _jsx(Text, { children: ` ${detail}` }), _jsx(Text, { dimColor: true, children: ` (${elapsed}${tokens ? ` · ↓ ${tokens}` : ''})` })] }));
20
- }
21
- function statusGlyph(status) {
22
- switch (status) {
23
- case 'queued':
24
- return '□';
25
- case 'thinking':
26
- return '⏳';
27
- case 'shipped':
28
- return '✓';
29
- // — `replied` = orchestrator finished talking but no
30
- // tool/delegate side-effect emitted. Distinct arrow glyph so the
31
- // operator can tell at a glance that no real work shipped, even
32
- // though the LLM completed cleanly. Defeats the fake-shipped
33
- // anti-pattern (memory feedback_no_fake_dispatch_promises).
34
- case 'replied':
35
- return '→';
36
- // `unverified` (PUGI-538c-FU-OUTCOME) — files landed on disk but
37
- // the verify-gate did not certify the run (no test command in
38
- // the workspace). Tilde glyph reads as "approximately done"
39
- // without claiming the verified-shipped guarantee.
40
- case 'unverified':
41
- return '~';
42
- case 'blocked':
43
- return '✗';
44
- case 'failed':
45
- return '✗';
46
- }
47
- }
48
- function statusColor(status) {
49
- switch (status) {
50
- case 'queued':
51
- return undefined;
52
- case 'thinking':
53
- return 'cyan';
54
- case 'shipped':
55
- return 'green';
56
- // Dim/grey-ish — succeeded technically but no work was shipped.
57
- case 'replied':
58
- return 'gray';
59
- // Yellow — work landed but the verify-gate could not certify it.
60
- // Same hue as `blocked` because both are "advisory, not green";
61
- // the glyph distinguishes them (`~` vs `✗`).
62
- case 'unverified':
63
- return 'yellow';
64
- case 'blocked':
65
- return 'yellow';
66
- case 'failed':
67
- return 'red';
68
- }
69
- }
70
- function formatElapsed(startedAtEpochMs, nowEpochMs) {
71
- const ms = Math.max(0, nowEpochMs - startedAtEpochMs);
72
- if (ms < 60_000)
73
- return `${Math.floor(ms / 1000)}s`;
74
- const minutes = Math.floor(ms / 60_000);
75
- const seconds = Math.floor((ms % 60_000) / 1000);
76
- return `${minutes}m ${seconds.toString().padStart(2, '0')}s`;
77
- }
78
- function formatTokens(total) {
79
- if (total <= 0)
80
- return '';
81
- if (total < 1_000)
82
- return total.toString();
83
- if (total < 1_000_000)
84
- return `${(total / 1_000).toFixed(1)}k`;
85
- return `${(total / 1_000_000).toFixed(1)}m`;
86
- }
87
- //# sourceMappingURL=agent-tree.js.map