@pugi/cli 0.1.0-beta.8 → 0.1.0-beta.88

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 (405) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/LICENSE +1 -1
  3. package/THIRD_PARTY_NOTICES.md +40 -0
  4. package/assets/pugi-prozr2-mascot.ansi +9 -0
  5. package/bin/run.js +33 -1
  6. package/dist/commands/deploy.js +40 -40
  7. package/dist/commands/flatten.js +191 -0
  8. package/dist/commands/jobs-watch.js +201 -0
  9. package/dist/commands/jobs.js +42 -27
  10. package/dist/commands/smoke.js +133 -0
  11. package/dist/core/agent-progress/cleanup.js +134 -0
  12. package/dist/core/agent-progress/schema.js +144 -0
  13. package/dist/core/agent-progress/writer.js +101 -0
  14. package/dist/core/agents/adaptive-router.js +330 -0
  15. package/dist/core/agents/query-decomposer.js +297 -0
  16. package/dist/core/agents/registry.js +3 -3
  17. package/dist/core/approvals/shortcut-resolver.js +98 -0
  18. package/dist/core/artifact-chain/dispatcher.js +148 -0
  19. package/dist/core/artifact-chain/exporter.js +164 -0
  20. package/dist/core/artifact-chain/state.js +243 -0
  21. package/dist/core/artifact-chain/steps.js +169 -0
  22. package/dist/core/ask-user/question.js +92 -0
  23. package/dist/core/audit/audit-trail.js +275 -0
  24. package/dist/core/auth/ensure-authenticated.js +129 -0
  25. package/dist/core/auth/env-provider.js +238 -0
  26. package/dist/core/auto-open-browser.js +4 -4
  27. package/dist/core/auto-update/channels.js +122 -0
  28. package/dist/core/auto-update/checker.js +241 -0
  29. package/dist/core/auto-update/state.js +235 -0
  30. package/dist/core/bare-mode/index.js +107 -0
  31. package/dist/core/bash/redirect.js +281 -0
  32. package/dist/core/bash-classifier.js +436 -40
  33. package/dist/core/checkpoint/resumer.js +149 -0
  34. package/dist/core/checkpoint/rewinder.js +291 -0
  35. package/dist/core/checkpoints/shadow-git.js +670 -0
  36. package/dist/core/citations/parser.js +109 -0
  37. package/dist/core/classifier/yolo-classifier.js +88 -0
  38. package/dist/core/codegraph/decision-store.js +248 -0
  39. package/dist/core/codegraph/detect-repo.js +459 -0
  40. package/dist/core/codegraph/install.js +134 -0
  41. package/dist/core/codegraph/offer-hook.js +220 -0
  42. package/dist/core/compact/auto-trigger.js +96 -0
  43. package/dist/core/compact/buffer-rewriter.js +115 -0
  44. package/dist/core/compact/summarizer.js +208 -0
  45. package/dist/core/compact/token-counter.js +108 -0
  46. package/dist/core/consensus/anvil-fanout.js +25 -25
  47. package/dist/core/consensus/diff-capture.js +121 -12
  48. package/dist/core/consensus/rubric.js +21 -21
  49. package/dist/core/context/builder.js +6 -6
  50. package/dist/core/context/compaction-events.js +8 -8
  51. package/dist/core/context/compaction.js +31 -31
  52. package/dist/core/context/index.js +15 -8
  53. package/dist/core/context/invariants.js +51 -51
  54. package/dist/core/context/markdown-loader.js +28 -10
  55. package/dist/core/context/markdown-traverse.js +255 -0
  56. package/dist/core/context/pugiignore.js +41 -41
  57. package/dist/core/context/repo-skeleton.js +37 -37
  58. package/dist/core/context/tool-eviction.js +55 -0
  59. package/dist/core/context/watcher.js +32 -32
  60. package/dist/core/context/working-set.js +23 -23
  61. package/dist/core/coordinator/agent-tools.js +77 -0
  62. package/dist/core/coordinator/agent-toolset.js +65 -0
  63. package/dist/core/coordinator/fsm.js +73 -0
  64. package/dist/core/coordinator/mode-fsm.js +70 -0
  65. package/dist/core/cost/rate-card.js +129 -0
  66. package/dist/core/cost/tracker.js +221 -0
  67. package/dist/core/credentials.js +12 -12
  68. package/dist/core/cron/scheduler.js +138 -0
  69. package/dist/core/denial-tracking/index.js +8 -0
  70. package/dist/core/denial-tracking/state.js +264 -0
  71. package/dist/core/diagnostics/probe-runner.js +93 -0
  72. package/dist/core/diagnostics/probes/api.js +46 -0
  73. package/dist/core/diagnostics/probes/auth.js +93 -0
  74. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  75. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  76. package/dist/core/diagnostics/probes/config.js +72 -0
  77. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  78. package/dist/core/diagnostics/probes/disk.js +81 -0
  79. package/dist/core/diagnostics/probes/engine-live.js +46 -0
  80. package/dist/core/diagnostics/probes/git.js +65 -0
  81. package/dist/core/diagnostics/probes/hooks.js +118 -0
  82. package/dist/core/diagnostics/probes/mcp.js +75 -0
  83. package/dist/core/diagnostics/probes/node.js +59 -0
  84. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  85. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  86. package/dist/core/diagnostics/probes/sandbox.js +40 -0
  87. package/dist/core/diagnostics/probes/session.js +74 -0
  88. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  89. package/dist/core/diagnostics/probes/workspace.js +63 -0
  90. package/dist/core/diagnostics/types.js +70 -0
  91. package/dist/core/dispatch/cache-cleanup.js +197 -0
  92. package/dist/core/dispatch/cache-handoff.js +295 -0
  93. package/dist/core/edits/apply-patch-layer-e.js +189 -0
  94. package/dist/core/edits/dispatch.js +293 -7
  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 +3 -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 +322 -0
  108. package/dist/core/engine/anvil-client.js +151 -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 +134 -16
  117. package/dist/core/engine/strip-internal-fields.js +124 -0
  118. package/dist/core/engine/tool-bridge.js +1295 -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 +44 -0
  129. package/dist/core/hooks/index.js +15 -0
  130. package/dist/core/hooks/registry.js +213 -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/image/renderer.js +71 -0
  141. package/dist/core/init/detector.js +582 -0
  142. package/dist/core/init/template-renderer.js +242 -0
  143. package/dist/core/jobs/registry.js +18 -18
  144. package/dist/core/ledger/results-tsv.js +142 -0
  145. package/dist/core/log-discipline/stdout-redirect.js +51 -0
  146. package/dist/core/lsp/cache.js +105 -0
  147. package/dist/core/lsp/client.js +776 -0
  148. package/dist/core/lsp/language-detect.js +66 -0
  149. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  150. package/dist/core/lsp/symbol-tools.js +372 -0
  151. package/dist/core/mcp/client.js +97 -28
  152. package/dist/core/mcp/http-server.js +553 -0
  153. package/dist/core/mcp/orchestrator-tools.js +662 -0
  154. package/dist/core/mcp/permission.js +190 -0
  155. package/dist/core/mcp/registry.js +39 -17
  156. package/dist/core/mcp/server-tools.js +219 -0
  157. package/dist/core/mcp/server.js +397 -0
  158. package/dist/core/mcp/trust.js +10 -10
  159. package/dist/core/memory/dual-write.js +416 -0
  160. package/dist/core/memory/passive-extract.js +130 -0
  161. package/dist/core/memory/phase1-kinds.js +20 -0
  162. package/dist/core/memory/secret-scanner.js +304 -0
  163. package/dist/core/memory-sync/queue.js +170 -0
  164. package/dist/core/metrics/extract.js +113 -0
  165. package/dist/core/modes/roo-modes.js +68 -0
  166. package/dist/core/onboarding/ensure-initialized.js +133 -0
  167. package/dist/core/onboarding/marker.js +111 -0
  168. package/dist/core/onboarding/telemetry-state.js +108 -0
  169. package/dist/core/output-style/presets.js +176 -0
  170. package/dist/core/output-style/state.js +185 -0
  171. package/dist/core/path-security.js +287 -5
  172. package/dist/core/permission.js +82 -22
  173. package/dist/core/permissions/auto-classifier.js +124 -0
  174. package/dist/core/permissions/bash-parser.js +371 -0
  175. package/dist/core/permissions/circuit-breaker.js +83 -0
  176. package/dist/core/permissions/constrained-edit.js +91 -0
  177. package/dist/core/permissions/gate.js +278 -0
  178. package/dist/core/permissions/index.js +20 -0
  179. package/dist/core/permissions/mode.js +174 -0
  180. package/dist/core/permissions/network-egress.js +137 -0
  181. package/dist/core/permissions/state.js +241 -0
  182. package/dist/core/permissions/tool-class.js +93 -0
  183. package/dist/core/plan-mode/ui-state.js +51 -0
  184. package/dist/core/plans/plan-artifact.js +721 -0
  185. package/dist/core/policy-limits/etag-store.js +122 -0
  186. package/dist/core/prd-check/parser.js +215 -0
  187. package/dist/core/prd-check/reporter.js +127 -0
  188. package/dist/core/prd-check/session-review.js +557 -0
  189. package/dist/core/prd-check/verifiers.js +223 -0
  190. package/dist/core/prompt-cache/client-cache.js +99 -0
  191. package/dist/core/prompts/assembly.js +29 -0
  192. package/dist/core/prompts/registry.js +364 -0
  193. package/dist/core/pugi-md/cc-compat-rules.js +735 -0
  194. package/dist/core/pugi-md/context-injector.js +76 -0
  195. package/dist/core/pugi-md/walk-up.js +207 -0
  196. package/dist/core/python/uv-installer.js +270 -0
  197. package/dist/core/python/uv-resolver.js +83 -0
  198. package/dist/core/rate-limit/narrator.js +146 -0
  199. package/dist/core/recipes/cli-types.js +20 -0
  200. package/dist/core/recipes/loader.js +103 -0
  201. package/dist/core/recipes/runner.js +345 -0
  202. package/dist/core/recipes/schema.js +587 -0
  203. package/dist/core/release-notes/parser.js +241 -0
  204. package/dist/core/release-notes/state.js +116 -0
  205. package/dist/core/repl/ask.js +37 -37
  206. package/dist/core/repl/cancellation.js +26 -26
  207. package/dist/core/repl/cap-warning.js +4 -4
  208. package/dist/core/repl/clipboard-read.js +11 -11
  209. package/dist/core/repl/dispatch-fsm.js +12 -12
  210. package/dist/core/repl/history-search.js +15 -15
  211. package/dist/core/repl/history.js +28 -18
  212. package/dist/core/repl/kill-ring.js +5 -5
  213. package/dist/core/repl/model-pricing.js +135 -0
  214. package/dist/core/repl/privacy-banner.js +22 -22
  215. package/dist/core/repl/session.js +2157 -214
  216. package/dist/core/repl/slash-commands.js +533 -40
  217. package/dist/core/repl/store/index.js +1 -1
  218. package/dist/core/repl/store/jsonl-log.js +22 -22
  219. package/dist/core/repl/store/lockfile.js +10 -10
  220. package/dist/core/repl/store/session-store.js +136 -107
  221. package/dist/core/repl/store/types.js +15 -15
  222. package/dist/core/repl/store/uuid-v7.js +12 -12
  223. package/dist/core/repl/workspace-context.js +43 -21
  224. package/dist/core/repo-map/build.js +125 -0
  225. package/dist/core/repo-map/cache.js +185 -0
  226. package/dist/core/repo-map/extractor.js +254 -0
  227. package/dist/core/repo-map/formatter.js +145 -0
  228. package/dist/core/repo-map/page-rank.js +105 -0
  229. package/dist/core/repo-map/scanner.js +211 -0
  230. package/dist/core/retry-budget/budget.js +284 -0
  231. package/dist/core/retry-budget/index.js +5 -0
  232. package/dist/core/retry-budget/retry-cap.js +74 -0
  233. package/dist/core/routing/lead-worker.js +43 -0
  234. package/dist/core/routing/pre-flight-estimator.js +108 -0
  235. package/dist/core/runs/run-tree.js +103 -0
  236. package/dist/core/security/injection-scanner.js +367 -0
  237. package/dist/core/security/output-filter.js +418 -0
  238. package/dist/core/session/env-file.js +105 -0
  239. package/dist/core/session/section-budgets.js +140 -0
  240. package/dist/core/session.js +92 -0
  241. package/dist/core/settings.js +298 -5
  242. package/dist/core/share/formatter.js +271 -0
  243. package/dist/core/share/redactor.js +221 -0
  244. package/dist/core/share/uploader.js +267 -0
  245. package/dist/core/skills/defaults.js +457 -0
  246. package/dist/core/skills/loader.js +22 -22
  247. package/dist/core/skills/sources.js +27 -27
  248. package/dist/core/smoke/headless-driver.js +174 -0
  249. package/dist/core/smoke/orchestrator.js +194 -0
  250. package/dist/core/smoke/runner.js +238 -0
  251. package/dist/core/smoke/scenario-parser.js +316 -0
  252. package/dist/core/statusline.js +99 -0
  253. package/dist/core/subagents/dispatcher-real.js +600 -0
  254. package/dist/core/subagents/dispatcher.js +132 -43
  255. package/dist/core/subagents/index.js +19 -6
  256. package/dist/core/subagents/isolation-matrix.js +213 -0
  257. package/dist/core/subagents/spawn.js +19 -4
  258. package/dist/core/telemetry/emitter.js +229 -0
  259. package/dist/core/telemetry/queue.js +251 -0
  260. package/dist/core/theme/context.js +91 -0
  261. package/dist/core/theme/presets.js +228 -0
  262. package/dist/core/theme/state.js +181 -0
  263. package/dist/core/todos/invariant.js +10 -0
  264. package/dist/core/todos/state.js +177 -0
  265. package/dist/core/tool-schema/compressor.js +89 -0
  266. package/dist/core/transport/version-interceptor.js +166 -0
  267. package/dist/core/trust.js +2 -2
  268. package/dist/core/tui/thinking-block.js +64 -0
  269. package/dist/core/vim/keymap.js +288 -0
  270. package/dist/core/vim/state.js +92 -0
  271. package/dist/core/watch-markers/marker-watcher.js +133 -0
  272. package/dist/core/worktree-manager/cleanup.js +123 -0
  273. package/dist/core/worktree-manager/manager.js +303 -0
  274. package/dist/index.js +36 -0
  275. package/dist/runtime/bootstrap.js +190 -0
  276. package/dist/runtime/cli.js +4203 -493
  277. package/dist/runtime/commands/agents.js +30 -30
  278. package/dist/runtime/commands/budget.js +5 -5
  279. package/dist/runtime/commands/cancel.js +231 -0
  280. package/dist/runtime/commands/chain.js +489 -0
  281. package/dist/runtime/commands/codegraph-status.js +227 -0
  282. package/dist/runtime/commands/compact.js +297 -0
  283. package/dist/runtime/commands/config.js +73 -39
  284. package/dist/runtime/commands/cost.js +199 -0
  285. package/dist/runtime/commands/delegate.js +244 -13
  286. package/dist/runtime/commands/dispatch.js +126 -0
  287. package/dist/runtime/commands/doctor.js +579 -0
  288. package/dist/runtime/commands/feedback.js +184 -0
  289. package/dist/runtime/commands/hooks.js +184 -0
  290. package/dist/runtime/commands/init.js +254 -0
  291. package/dist/runtime/commands/lsp.js +368 -0
  292. package/dist/runtime/commands/mcp.js +879 -0
  293. package/dist/runtime/commands/memory.js +582 -0
  294. package/dist/runtime/commands/model.js +237 -0
  295. package/dist/runtime/commands/onboarding.js +275 -0
  296. package/dist/runtime/commands/patch.js +128 -0
  297. package/dist/runtime/commands/permissions.js +112 -0
  298. package/dist/runtime/commands/plan.js +143 -0
  299. package/dist/runtime/commands/prd-check.js +285 -0
  300. package/dist/runtime/commands/privacy.js +17 -17
  301. package/dist/runtime/commands/recipe.js +325 -0
  302. package/dist/runtime/commands/redo-blob-store.js +92 -0
  303. package/dist/runtime/commands/redo.js +361 -0
  304. package/dist/runtime/commands/release-notes.js +229 -0
  305. package/dist/runtime/commands/repo-map.js +95 -0
  306. package/dist/runtime/commands/report.js +299 -0
  307. package/dist/runtime/commands/resume.js +118 -0
  308. package/dist/runtime/commands/review-consensus.js +68 -53
  309. package/dist/runtime/commands/rewind.js +333 -0
  310. package/dist/runtime/commands/roster.js +14 -14
  311. package/dist/runtime/commands/sessions.js +163 -0
  312. package/dist/runtime/commands/share.js +316 -0
  313. package/dist/runtime/commands/skills.js +31 -31
  314. package/dist/runtime/commands/status.js +186 -0
  315. package/dist/runtime/commands/stickers.js +82 -0
  316. package/dist/runtime/commands/style.js +194 -0
  317. package/dist/runtime/commands/theme.js +196 -0
  318. package/dist/runtime/commands/undo.js +54 -22
  319. package/dist/runtime/commands/update.js +289 -0
  320. package/dist/runtime/commands/vim.js +140 -0
  321. package/dist/runtime/commands/worktree.js +177 -0
  322. package/dist/runtime/commands/worktrees.js +155 -0
  323. package/dist/runtime/headless-repl.js +195 -0
  324. package/dist/runtime/headless.js +543 -0
  325. package/dist/runtime/load-hooks-or-exit.js +71 -0
  326. package/dist/runtime/plan-decompose.js +531 -0
  327. package/dist/runtime/sigint-guard.js +272 -0
  328. package/dist/runtime/update-check.js +28 -28
  329. package/dist/runtime/version.js +65 -0
  330. package/dist/skills/bundled/batch.js +617 -0
  331. package/dist/skills/bundled/index.js +45 -0
  332. package/dist/skills/bundled/loop.js +358 -0
  333. package/dist/skills/bundled/remember.js +383 -0
  334. package/dist/skills/bundled/simplify.js +289 -0
  335. package/dist/skills/bundled/skillify.js +373 -0
  336. package/dist/skills/bundled/stuck.js +558 -0
  337. package/dist/skills/bundled/verify.js +439 -0
  338. package/dist/testing/vcr.js +486 -0
  339. package/dist/tools/agent-tool.js +229 -0
  340. package/dist/tools/apply-patch.js +556 -0
  341. package/dist/tools/ask-user-question.js +288 -0
  342. package/dist/tools/ask-user.js +115 -0
  343. package/dist/tools/bash.js +624 -46
  344. package/dist/tools/brief.js +224 -0
  345. package/dist/tools/enter-worktree.js +250 -0
  346. package/dist/tools/exit-worktree.js +147 -0
  347. package/dist/tools/file-tools.js +161 -44
  348. package/dist/tools/lsp-tools.js +189 -0
  349. package/dist/tools/mcp-tool.js +260 -0
  350. package/dist/tools/multi-edit.js +361 -0
  351. package/dist/tools/powershell.js +268 -0
  352. package/dist/tools/registry.js +85 -0
  353. package/dist/tools/skill-tool.js +96 -0
  354. package/dist/tools/sleep.js +99 -0
  355. package/dist/tools/synthetic-output.js +133 -0
  356. package/dist/tools/tasks.js +208 -0
  357. package/dist/tools/todo-write.js +184 -0
  358. package/dist/tools/verify-plan-execution.js +295 -0
  359. package/dist/tools/web-fetch-injection-scanner.js +207 -0
  360. package/dist/tools/web-fetch.js +195 -10
  361. package/dist/tools/web-search.js +458 -0
  362. package/dist/tui/agent-progress-card.js +111 -0
  363. package/dist/tui/agent-tree.js +11 -1
  364. package/dist/tui/ask-modal.js +14 -14
  365. package/dist/tui/ask-user-question-chips.js +257 -0
  366. package/dist/tui/ask-user-question-prompt.js +203 -0
  367. package/dist/tui/compact-banner.js +81 -0
  368. package/dist/tui/conversation-pane.js +85 -11
  369. package/dist/tui/cost-table.js +111 -0
  370. package/dist/tui/device-flow.js +2 -2
  371. package/dist/tui/doctor-table.js +46 -0
  372. package/dist/tui/feedback-prompt.js +156 -0
  373. package/dist/tui/input-box.js +247 -32
  374. package/dist/tui/login-picker.js +3 -3
  375. package/dist/tui/markdown-render.js +6 -6
  376. package/dist/tui/onboarding-wizard.js +240 -0
  377. package/dist/tui/permissions-picker.js +86 -0
  378. package/dist/tui/render.js +35 -0
  379. package/dist/tui/repl-render.js +332 -54
  380. package/dist/tui/repl-splash-art.js +16 -16
  381. package/dist/tui/repl-splash-mascot.js +48 -24
  382. package/dist/tui/repl-splash.js +22 -22
  383. package/dist/tui/repl.js +124 -44
  384. package/dist/tui/slash-palette.js +6 -6
  385. package/dist/tui/splash.js +2 -2
  386. package/dist/tui/status-bar.js +109 -31
  387. package/dist/tui/status-table.js +7 -0
  388. package/dist/tui/stickers-art.js +136 -0
  389. package/dist/tui/style-table.js +28 -0
  390. package/dist/tui/theme-table.js +29 -0
  391. package/dist/tui/thinking-spinner.js +123 -0
  392. package/dist/tui/tool-stream-pane.js +53 -4
  393. package/dist/tui/update-banner.js +27 -2
  394. package/dist/tui/vim-input.js +267 -0
  395. package/dist/tui/welcome-banner.js +107 -0
  396. package/dist/tui/welcome-data.js +293 -0
  397. package/dist/tui/workspace-context.js +2 -2
  398. package/docs/examples/codegraph.mcp.json +10 -0
  399. package/package.json +25 -7
  400. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  401. package/test/scenarios/compact-force.scenario.txt +11 -0
  402. package/test/scenarios/identity.scenario.txt +11 -0
  403. package/test/scenarios/persona-handoff.scenario.txt +11 -0
  404. package/test/scenarios/walkback.scenario.txt +12 -0
  405. package/dist/core/engine/compaction-hook.js +0 -154
@@ -0,0 +1,582 @@
1
+ /**
2
+ * `pugi memory` — operator surface for the persona-memory layer
3
+ * .
4
+ *
5
+ * Subcommands:
6
+ *
7
+ * pugi memory list [--persona <slug>] [--kind <kind>] [--limit <n>]
8
+ * List memories for the current tenant — recency-first. Renders a
9
+ * table with TTL countdown.
10
+ *
11
+ * pugi memory recall <query> [--persona <slug>] [--top-k <n>]
12
+ * Hybrid recall (vector + BM25 + recency) for one persona.
13
+ *
14
+ * pugi memory write <kind> <content> [--persona <slug>] [--forget-after <iso>]
15
+ * Persist a new memory. Queues locally + retries on next sync when
16
+ * the admin-api is unreachable.
17
+ *
18
+ * pugi memory forget <id>
19
+ * Hard-delete one memory by id.
20
+ *
21
+ * pugi memory sync
22
+ * Push the local pending-write queue to the admin-api. Idempotent:
23
+ * successful ops are dropped from the queue, failed ops stay queued.
24
+ *
25
+ * Auth surface: the same `resolveActiveCredential` flow every other
26
+ * pugi command uses — bearer token + apiUrl from `~/.pugi/credentials.json`.
27
+ * The admin-api enforces the tenant boundary via `BridgeOrJwtGuard` +
28
+ * `prisma.withTenant`.
29
+ *
30
+ * Feature flag: the admin-api side honours `PERSONA_MEMORY_ENABLED` and
31
+ * returns HTTP 503 `{error: feature_disabled}` until the flag is
32
+ * flipped. The CLI surfaces that response verbatim so the operator
33
+ * sees the actual gate.
34
+ */
35
+ import { request } from 'undici';
36
+ import { resolveActiveCredential } from '../../core/credentials.js';
37
+ import { isScanDisabled, redactSecrets, scanForSecrets, } from '../../core/memory/secret-scanner.js';
38
+ import { PERSONA_MEMORY_KINDS, countPending, defaultQueuePath, enqueueMemoryOp, readMemoryQueue, rewriteMemoryQueue, } from '../../core/memory-sync/queue.js';
39
+ const SUB_USAGE = [
40
+ 'pugi memory list [--persona <slug>] [--kind <k>] [--limit <n>]',
41
+ 'pugi memory recall <query> [--persona <slug>] [--top-k <n>]',
42
+ 'pugi memory write <kind> <content> [--persona <slug>] [--forget-after <iso>]',
43
+ 'pugi memory forget <id>',
44
+ 'pugi memory sync',
45
+ ].join('\n ');
46
+ const DEFAULT_PERSONA = 'mira';
47
+ /** Single CLI entry — top-level `pugi memory` AND the in-REPL `/memory` slash both call this. */
48
+ export async function runMemoryCommand(args, ctx) {
49
+ const sub = (args[0] ?? '').toLowerCase();
50
+ const tail = args.slice(1);
51
+ switch (sub) {
52
+ case '':
53
+ case '-h':
54
+ case '--help':
55
+ return printUsage(ctx);
56
+ case 'list':
57
+ return runList(tail, ctx);
58
+ case 'recall':
59
+ return runRecall(tail, ctx);
60
+ case 'write':
61
+ case 'remember':
62
+ return runWrite(tail, ctx);
63
+ case 'forget':
64
+ case 'delete':
65
+ return runForget(tail, ctx);
66
+ case 'sync':
67
+ return runSync(ctx);
68
+ default:
69
+ ctx.writeOutput({ command: 'memory', sub, status: 'unknown_sub' }, `Unknown sub-command "${sub}".\nUsage:\n ${SUB_USAGE}`);
70
+ return { command: 'memory', sub, status: 'unknown_sub' };
71
+ }
72
+ }
73
+ function printUsage(ctx) {
74
+ ctx.writeOutput({ command: 'memory', sub: 'help', usage: SUB_USAGE }, `pugi memory — persona-memory operator surface .\nUsage:\n ${SUB_USAGE}`);
75
+ return { command: 'memory', sub: 'help', status: 'listed' };
76
+ }
77
+ // ===========================================================================
78
+ // list
79
+ // ===========================================================================
80
+ async function runList(args, ctx) {
81
+ const flags = parseFlags(args);
82
+ const cred = resolveActiveCredential();
83
+ if (!cred)
84
+ return emitUnauth('list', ctx);
85
+ const url = new URL(`${stripTrailing(cred.apiUrl)}/api/persona-memory`);
86
+ if (flags.persona)
87
+ url.searchParams.set('personaSlug', flags.persona);
88
+ if (flags.kind)
89
+ url.searchParams.set('kind', flags.kind);
90
+ if (flags.limit)
91
+ url.searchParams.set('limit', String(flags.limit));
92
+ const res = await safeGet(url.toString(), cred.apiKey);
93
+ if (res.status === 503)
94
+ return emitFeatureDisabled('list', ctx);
95
+ if (!res.ok) {
96
+ ctx.writeOutput({ command: 'memory', sub: 'list', status: 'invalid_args', reason: res.detail }, `pugi memory list: HTTP ${res.statusCode} — ${res.detail}`);
97
+ return { command: 'memory', sub: 'list', status: 'invalid_args', reason: res.detail };
98
+ }
99
+ const items = (res.body ?? []);
100
+ const now = (ctx.now ?? (() => new Date()))();
101
+ const lines = items.map((item) => formatMemoryRow(item, now));
102
+ const text = items.length === 0
103
+ ? 'No memories for the active tenant + filters.'
104
+ : ['kind strength age ttl id content', ...lines].join('\n');
105
+ ctx.writeOutput({ command: 'memory', sub: 'list', count: items.length, items }, text);
106
+ return { command: 'memory', sub: 'list', status: 'listed', count: items.length };
107
+ }
108
+ // ===========================================================================
109
+ // recall
110
+ // ===========================================================================
111
+ async function runRecall(args, ctx) {
112
+ const { positional, flags } = splitPositionalFlags(args);
113
+ if (positional.length === 0) {
114
+ ctx.writeOutput({ command: 'memory', sub: 'recall', status: 'invalid_args' }, 'pugi memory recall <query> — query is required.');
115
+ return { command: 'memory', sub: 'recall', status: 'invalid_args' };
116
+ }
117
+ const query = positional.join(' ').trim();
118
+ const persona = flags.persona ?? DEFAULT_PERSONA;
119
+ const topK = flags.topK ?? 5;
120
+ const cred = resolveActiveCredential();
121
+ if (!cred)
122
+ return emitUnauth('recall', ctx);
123
+ const url = new URL(`${stripTrailing(cred.apiUrl)}/api/persona-memory/recall`);
124
+ url.searchParams.set('personaSlug', persona);
125
+ url.searchParams.set('query', query);
126
+ url.searchParams.set('topK', String(topK));
127
+ const res = await safeGet(url.toString(), cred.apiKey);
128
+ if (res.status === 503)
129
+ return emitFeatureDisabled('recall', ctx);
130
+ if (!res.ok) {
131
+ ctx.writeOutput({ command: 'memory', sub: 'recall', status: 'invalid_args', reason: res.detail }, `pugi memory recall: HTTP ${res.statusCode} — ${res.detail}`);
132
+ return { command: 'memory', sub: 'recall', status: 'invalid_args', reason: res.detail };
133
+ }
134
+ const results = (res.body ?? []);
135
+ const text = results.length === 0
136
+ ? `No matches for "${query.slice(0, 64)}" on persona "${persona}".`
137
+ : results
138
+ .map((r, idx) => `${(idx + 1).toString().padStart(2, ' ')}. [${r.item.kind}] score=${r.score.toFixed(3)} ${r.item.content}`)
139
+ .join('\n');
140
+ ctx.writeOutput({ command: 'memory', sub: 'recall', count: results.length, results }, text);
141
+ return { command: 'memory', sub: 'recall', status: 'recalled', count: results.length };
142
+ }
143
+ // ===========================================================================
144
+ // write
145
+ // ===========================================================================
146
+ async function runWrite(args, ctx) {
147
+ // Backlog : `--curate` opts into the proposal-first
148
+ // mode powered by `pugi remember`. Default behaviour (silent enqueue)
149
+ // is preserved for back-compat per the bundled-skills spec — the
150
+ // operator has to explicitly opt in.
151
+ const curateRequested = args.includes('--curate');
152
+ // Backlog : `--allow-redacted` opts into the
153
+ // auto-scrub flow. Default behaviour is HARD reject; this flag tells
154
+ // the secret scanner to replace each secret with `[SECRET:<pattern>]`
155
+ // and persist the scrubbed payload, surfacing a warning so the
156
+ // operator notices.
157
+ const allowRedacted = args.includes('--allow-redacted');
158
+ const argsWithoutCurate = args
159
+ .filter((a) => a !== '--curate')
160
+ .filter((a) => a !== '--allow-redacted');
161
+ const { positional, flags } = splitPositionalFlags(argsWithoutCurate);
162
+ if (positional.length < 2) {
163
+ ctx.writeOutput({ command: 'memory', sub: 'write', status: 'invalid_args' }, 'pugi memory write <kind> <content> — kind + content required.');
164
+ return { command: 'memory', sub: 'write', status: 'invalid_args' };
165
+ }
166
+ if (curateRequested) {
167
+ // Hand off to the bundled remember skill. We surface a single line
168
+ // hint and a structured payload so scripted callers can detect the
169
+ // delegation without grepping prose.
170
+ const hint = 'Use `pugi remember "<content>"` for proposal-first curation. ' +
171
+ '`pugi memory write --curate` will route there once the interactive ' +
172
+ 'bundle wiring lands; meanwhile the silent enqueue is preserved.';
173
+ ctx.writeOutput({
174
+ command: 'memory',
175
+ sub: 'write',
176
+ status: 'curate_hint',
177
+ hint,
178
+ }, hint);
179
+ }
180
+ const kind = positional[0]?.toLowerCase() ?? '';
181
+ // `content` is `let` (not `const`) so the secret-scanner branch
182
+ // below can swap in a redacted version when --allow-redacted fires.
183
+ let content = positional.slice(1).join(' ').trim();
184
+ if (!PERSONA_MEMORY_KINDS.includes(kind)) {
185
+ ctx.writeOutput({
186
+ command: 'memory',
187
+ sub: 'write',
188
+ status: 'invalid_args',
189
+ reason: 'unknown_kind',
190
+ }, `pugi memory write: unknown kind "${kind}". Expected one of ${PERSONA_MEMORY_KINDS.join(' | ')}.`);
191
+ return { command: 'memory', sub: 'write', status: 'invalid_args', reason: 'unknown_kind' };
192
+ }
193
+ if (content.length === 0) {
194
+ ctx.writeOutput({ command: 'memory', sub: 'write', status: 'invalid_args' }, 'pugi memory write: content is empty.');
195
+ return { command: 'memory', sub: 'write', status: 'invalid_args' };
196
+ }
197
+ // Backlog — secret-scanner middleware. Runs BEFORE network +
198
+ // BEFORE offline enqueue so both code paths share the gate.
199
+ // - default: HARD reject (status: 'secret_blocked'), no write.
200
+ // - PUGI_MEMORY_SECRET_SCAN_DISABLE=1: skipped entirely.
201
+ // - --allow-redacted: matches replaced with `[SECRET:<pattern>]`
202
+ // placeholders, write proceeds + warning.
203
+ // The post-scan content is what gets enqueued / POSTed. We mutate
204
+ // `content` to keep the rest of the function untouched.
205
+ if (!isScanDisabled()) {
206
+ const matches = scanForSecrets(content);
207
+ if (matches.length > 0) {
208
+ if (allowRedacted) {
209
+ const scrubbed = redactSecrets(content);
210
+ content = scrubbed.redacted;
211
+ const names = Array.from(new Set(scrubbed.matches.map((m) => m.pattern))).join(', ');
212
+ ctx.writeOutput({
213
+ command: 'memory',
214
+ sub: 'write',
215
+ status: 'redacted',
216
+ patterns: names,
217
+ count: scrubbed.matches.length,
218
+ }, `pugi memory write: --allow-redacted scrubbed ${scrubbed.matches.length} secret(s) — pattern(s): ${names}. Persisting redacted content.`);
219
+ }
220
+ else {
221
+ const names = Array.from(new Set(matches.map((m) => m.pattern))).join(', ');
222
+ ctx.writeOutput({
223
+ command: 'memory',
224
+ sub: 'write',
225
+ status: 'secret_blocked',
226
+ patterns: names,
227
+ count: matches.length,
228
+ hint: 'pass --allow-redacted to auto-scrub, or PUGI_MEMORY_SECRET_SCAN_DISABLE=1 to bypass',
229
+ }, `pugi memory write: refused — content contains secret(s): ${names}. ` +
230
+ `Re-run with --allow-redacted to auto-scrub, or set PUGI_MEMORY_SECRET_SCAN_DISABLE=1 to bypass.`);
231
+ return {
232
+ command: 'memory',
233
+ sub: 'write',
234
+ status: 'secret_blocked',
235
+ reason: names,
236
+ };
237
+ }
238
+ }
239
+ }
240
+ const persona = flags.persona ?? DEFAULT_PERSONA;
241
+ const cred = resolveActiveCredential();
242
+ if (!cred) {
243
+ // Queue offline. Operator can run `pugi memory sync` once auth is up.
244
+ const pending = enqueueMemoryOp({
245
+ op: 'write',
246
+ personaSlug: persona,
247
+ kind: kind,
248
+ content,
249
+ forgetAfter: flags.forgetAfter ?? null,
250
+ });
251
+ ctx.writeOutput({
252
+ command: 'memory',
253
+ sub: 'write',
254
+ status: 'queued_offline',
255
+ pending,
256
+ }, `Queued offline (no active credential). ${pending} pending — run \`pugi memory sync\` after \`pugi login\`.`);
257
+ return { command: 'memory', sub: 'write', status: 'queued_offline', pending };
258
+ }
259
+ const url = `${stripTrailing(cred.apiUrl)}/api/persona-memory`;
260
+ const body = {
261
+ personaSlug: persona,
262
+ kind,
263
+ content,
264
+ forgetAfter: flags.forgetAfter ?? null,
265
+ };
266
+ const res = await safePost(url, cred.apiKey, body);
267
+ if (res.status === 503)
268
+ return emitFeatureDisabled('write', ctx);
269
+ if (!res.ok) {
270
+ // Server unreachable / 5xx → queue for retry. 4xx → surface error,
271
+ // don't queue (a malformed write would just loop on sync).
272
+ if (res.statusCode >= 500 || res.statusCode === 0) {
273
+ const pending = enqueueMemoryOp({
274
+ op: 'write',
275
+ personaSlug: persona,
276
+ kind: kind,
277
+ content,
278
+ forgetAfter: flags.forgetAfter ?? null,
279
+ });
280
+ ctx.writeOutput({
281
+ command: 'memory',
282
+ sub: 'write',
283
+ status: 'queued_offline',
284
+ pending,
285
+ reason: res.detail,
286
+ }, `Server unreachable (${res.detail}). Queued — ${pending} pending. Run \`pugi memory sync\` later.`);
287
+ return {
288
+ command: 'memory',
289
+ sub: 'write',
290
+ status: 'queued_offline',
291
+ pending,
292
+ reason: res.detail,
293
+ };
294
+ }
295
+ ctx.writeOutput({ command: 'memory', sub: 'write', status: 'invalid_args', reason: res.detail }, `pugi memory write: HTTP ${res.statusCode} — ${res.detail}`);
296
+ return {
297
+ command: 'memory',
298
+ sub: 'write',
299
+ status: 'invalid_args',
300
+ reason: res.detail,
301
+ };
302
+ }
303
+ const created = (res.body ?? {});
304
+ ctx.writeOutput({ command: 'memory', sub: 'write', status: 'written', item: created }, `Persisted [${kind}] on persona "${persona}": ${created.id}`);
305
+ return { command: 'memory', sub: 'write', status: 'written' };
306
+ }
307
+ // ===========================================================================
308
+ // forget
309
+ // ===========================================================================
310
+ async function runForget(args, ctx) {
311
+ const id = (args[0] ?? '').trim();
312
+ if (!id) {
313
+ ctx.writeOutput({ command: 'memory', sub: 'forget', status: 'invalid_args' }, 'pugi memory forget <id> — memory id is required.');
314
+ return { command: 'memory', sub: 'forget', status: 'invalid_args' };
315
+ }
316
+ const cred = resolveActiveCredential();
317
+ if (!cred) {
318
+ const pending = enqueueMemoryOp({ op: 'forget', id });
319
+ ctx.writeOutput({ command: 'memory', sub: 'forget', status: 'queued_offline', pending }, `Queued offline (no active credential). ${pending} pending — run \`pugi memory sync\` after \`pugi login\`.`);
320
+ return { command: 'memory', sub: 'forget', status: 'queued_offline', pending };
321
+ }
322
+ const url = `${stripTrailing(cred.apiUrl)}/api/persona-memory/${encodeURIComponent(id)}`;
323
+ const res = await safeDelete(url, cred.apiKey);
324
+ if (res.status === 503)
325
+ return emitFeatureDisabled('forget', ctx);
326
+ if (res.statusCode === 404) {
327
+ ctx.writeOutput({ command: 'memory', sub: 'forget', status: 'forget_not_found', id }, `No memory with id "${id}" in this tenant.`);
328
+ return { command: 'memory', sub: 'forget', status: 'forget_not_found' };
329
+ }
330
+ if (!res.ok) {
331
+ if (res.statusCode >= 500 || res.statusCode === 0) {
332
+ const pending = enqueueMemoryOp({ op: 'forget', id });
333
+ ctx.writeOutput({
334
+ command: 'memory',
335
+ sub: 'forget',
336
+ status: 'queued_offline',
337
+ pending,
338
+ reason: res.detail,
339
+ }, `Server unreachable. Queued — ${pending} pending.`);
340
+ return {
341
+ command: 'memory',
342
+ sub: 'forget',
343
+ status: 'queued_offline',
344
+ pending,
345
+ reason: res.detail,
346
+ };
347
+ }
348
+ ctx.writeOutput({ command: 'memory', sub: 'forget', status: 'invalid_args', reason: res.detail }, `pugi memory forget: HTTP ${res.statusCode} — ${res.detail}`);
349
+ return {
350
+ command: 'memory',
351
+ sub: 'forget',
352
+ status: 'invalid_args',
353
+ reason: res.detail,
354
+ };
355
+ }
356
+ ctx.writeOutput({ command: 'memory', sub: 'forget', status: 'forgot', id }, `Forgot "${id}".`);
357
+ return { command: 'memory', sub: 'forget', status: 'forgot' };
358
+ }
359
+ // ===========================================================================
360
+ // sync
361
+ // ===========================================================================
362
+ async function runSync(ctx) {
363
+ const cred = resolveActiveCredential();
364
+ if (!cred)
365
+ return emitUnauth('sync', ctx);
366
+ const pendingBefore = countPending();
367
+ if (pendingBefore === 0) {
368
+ ctx.writeOutput({ command: 'memory', sub: 'sync', status: 'sync_noop', pending: 0 }, 'No pending operations to sync.');
369
+ return { command: 'memory', sub: 'sync', status: 'sync_noop', pending: 0 };
370
+ }
371
+ const ops = readMemoryQueue();
372
+ const remaining = [];
373
+ let synced = 0;
374
+ for (const op of ops) {
375
+ const ok = await flushOne(cred.apiUrl, cred.apiKey, op);
376
+ if (ok) {
377
+ synced++;
378
+ }
379
+ else {
380
+ remaining.push(op);
381
+ }
382
+ }
383
+ rewriteMemoryQueue(remaining);
384
+ if (remaining.length === 0) {
385
+ ctx.writeOutput({
386
+ command: 'memory',
387
+ sub: 'sync',
388
+ status: 'synced',
389
+ pending: 0,
390
+ count: synced,
391
+ }, `Synced ${synced} pending operation(s). Queue is empty.`);
392
+ return { command: 'memory', sub: 'sync', status: 'synced', pending: 0, count: synced };
393
+ }
394
+ ctx.writeOutput({
395
+ command: 'memory',
396
+ sub: 'sync',
397
+ status: 'sync_partial',
398
+ pending: remaining.length,
399
+ count: synced,
400
+ }, `Synced ${synced} — ${remaining.length} still pending (server unreachable / 4xx).`);
401
+ return {
402
+ command: 'memory',
403
+ sub: 'sync',
404
+ status: 'sync_partial',
405
+ pending: remaining.length,
406
+ count: synced,
407
+ };
408
+ }
409
+ async function flushOne(apiUrl, apiKey, op) {
410
+ if (op.op === 'write') {
411
+ const res = await safePost(`${stripTrailing(apiUrl)}/api/persona-memory`, apiKey, {
412
+ personaSlug: op.personaSlug,
413
+ kind: op.kind,
414
+ content: op.content,
415
+ forgetAfter: op.forgetAfter ?? null,
416
+ });
417
+ if (res.ok)
418
+ return true;
419
+ // 4xx → drop from queue (would loop forever otherwise).
420
+ if (res.statusCode >= 400 && res.statusCode < 500)
421
+ return true;
422
+ return false;
423
+ }
424
+ // op.op === 'forget'
425
+ const res = await safeDelete(`${stripTrailing(apiUrl)}/api/persona-memory/${encodeURIComponent(op.id)}`, apiKey);
426
+ if (res.ok)
427
+ return true;
428
+ // 404 → drop (already gone); other 4xx → drop too.
429
+ if (res.statusCode >= 400 && res.statusCode < 500)
430
+ return true;
431
+ return false;
432
+ }
433
+ function parseFlags(args) {
434
+ const out = {};
435
+ for (let i = 0; i < args.length; i++) {
436
+ const a = args[i] ?? '';
437
+ if (a === '--persona' && args[i + 1]) {
438
+ out.persona = args[i + 1];
439
+ i++;
440
+ }
441
+ else if (a === '--kind' && args[i + 1]) {
442
+ out.kind = args[i + 1];
443
+ i++;
444
+ }
445
+ else if (a === '--limit' && args[i + 1]) {
446
+ const n = Number.parseInt(args[i + 1] ?? '', 10);
447
+ if (Number.isFinite(n))
448
+ out.limit = n;
449
+ i++;
450
+ }
451
+ else if (a === '--top-k' && args[i + 1]) {
452
+ const n = Number.parseInt(args[i + 1] ?? '', 10);
453
+ if (Number.isFinite(n))
454
+ out.topK = n;
455
+ i++;
456
+ }
457
+ else if (a === '--forget-after' && args[i + 1]) {
458
+ out.forgetAfter = args[i + 1];
459
+ i++;
460
+ }
461
+ }
462
+ return out;
463
+ }
464
+ function splitPositionalFlags(args) {
465
+ const positional = [];
466
+ const consumed = new Set();
467
+ for (let i = 0; i < args.length; i++) {
468
+ const a = args[i] ?? '';
469
+ if (a.startsWith('--')) {
470
+ consumed.add(i);
471
+ if (i + 1 < args.length && !(args[i + 1] ?? '').startsWith('--')) {
472
+ consumed.add(i + 1);
473
+ }
474
+ }
475
+ else if (!consumed.has(i)) {
476
+ positional.push(a);
477
+ }
478
+ }
479
+ const flagArgs = [];
480
+ for (let i = 0; i < args.length; i++) {
481
+ if (consumed.has(i))
482
+ flagArgs.push(args[i] ?? '');
483
+ }
484
+ return { positional, flags: parseFlags(flagArgs) };
485
+ }
486
+ function stripTrailing(url) {
487
+ return url.endsWith('/') ? url.slice(0, -1) : url;
488
+ }
489
+ function formatMemoryRow(item, now) {
490
+ const age = ageHuman(item.lastUsedAt, now);
491
+ const ttl = item.forgetAfter ? ttlHuman(item.forgetAfter, now) : 'none';
492
+ const content = item.content.replace(/[\r\n\t]+/g, ' ').slice(0, 60);
493
+ return `${item.kind.padEnd(12)} ${item.strength.toFixed(2).padEnd(8)} ${age.padEnd(8)} ${ttl.padEnd(11)} ${item.id.padEnd(36)} ${content}`;
494
+ }
495
+ function ageHuman(iso, now) {
496
+ const t = Date.parse(iso);
497
+ if (!Number.isFinite(t))
498
+ return '?';
499
+ const days = Math.floor((now.getTime() - t) / (1000 * 60 * 60 * 24));
500
+ if (days <= 0)
501
+ return '<1d';
502
+ if (days < 30)
503
+ return `${days}d`;
504
+ return `${Math.floor(days / 30)}mo`;
505
+ }
506
+ function ttlHuman(iso, now) {
507
+ const t = Date.parse(iso);
508
+ if (!Number.isFinite(t))
509
+ return '?';
510
+ const ms = t - now.getTime();
511
+ if (ms <= 0)
512
+ return 'expired';
513
+ const days = Math.floor(ms / (1000 * 60 * 60 * 24));
514
+ if (days >= 1)
515
+ return `${days}d`;
516
+ const hours = Math.floor(ms / (1000 * 60 * 60));
517
+ return `${hours}h`;
518
+ }
519
+ async function safeGet(url, apiKey) {
520
+ return safeRequest(url, apiKey, 'GET');
521
+ }
522
+ async function safeDelete(url, apiKey) {
523
+ return safeRequest(url, apiKey, 'DELETE');
524
+ }
525
+ async function safePost(url, apiKey, body) {
526
+ return safeRequest(url, apiKey, 'POST', body);
527
+ }
528
+ async function safeRequest(url, apiKey, method, body) {
529
+ try {
530
+ const headers = {
531
+ authorization: `Bearer ${apiKey}`,
532
+ accept: 'application/json',
533
+ };
534
+ let bodyText;
535
+ if (body !== undefined) {
536
+ bodyText = JSON.stringify(body);
537
+ headers['content-type'] = 'application/json';
538
+ }
539
+ const res = await request(url, { method, headers, body: bodyText });
540
+ const sc = res.statusCode;
541
+ if (sc < 200 || sc >= 300) {
542
+ const detail = await res.body.text().catch(() => '');
543
+ return {
544
+ ok: false,
545
+ status: sc,
546
+ statusCode: sc,
547
+ detail: detail.slice(0, 300),
548
+ };
549
+ }
550
+ const parsed = await res.body.json().catch(() => undefined);
551
+ return { ok: true, status: sc, statusCode: sc, body: parsed, detail: '' };
552
+ }
553
+ catch (err) {
554
+ return {
555
+ ok: false,
556
+ status: 0,
557
+ statusCode: 0,
558
+ detail: err instanceof Error ? err.message : String(err),
559
+ };
560
+ }
561
+ }
562
+ function emitUnauth(sub, ctx) {
563
+ ctx.writeOutput({
564
+ command: 'memory',
565
+ sub,
566
+ status: 'unauthenticated',
567
+ hint: 'pugi login',
568
+ }, 'pugi memory: no active credential. Run `pugi login` first.');
569
+ return { command: 'memory', sub, status: 'unauthenticated' };
570
+ }
571
+ function emitFeatureDisabled(sub, ctx) {
572
+ ctx.writeOutput({
573
+ command: 'memory',
574
+ sub,
575
+ status: 'feature_disabled',
576
+ hint: 'set PERSONA_MEMORY_ENABLED=true on the admin-api host',
577
+ }, 'persona-memory feature is disabled on the admin-api host (HTTP 503).');
578
+ return { command: 'memory', sub, status: 'feature_disabled' };
579
+ }
580
+ /** Re-export for the `pugi doctor` queue-pending probe + the spec. */
581
+ export { defaultQueuePath };
582
+ //# sourceMappingURL=memory.js.map