@pugi/cli 0.1.0-beta.8 → 0.1.0-beta.87
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.
- package/CHANGELOG.md +96 -0
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/assets/pugi-prozr2-mascot.ansi +9 -0
- package/bin/run.js +33 -1
- package/dist/commands/deploy.js +40 -40
- package/dist/commands/flatten.js +191 -0
- package/dist/commands/jobs-watch.js +201 -0
- package/dist/commands/jobs.js +42 -27
- package/dist/commands/smoke.js +133 -0
- package/dist/core/agent-progress/cleanup.js +134 -0
- package/dist/core/agent-progress/schema.js +144 -0
- package/dist/core/agent-progress/writer.js +101 -0
- package/dist/core/agents/adaptive-router.js +330 -0
- package/dist/core/agents/query-decomposer.js +297 -0
- package/dist/core/agents/registry.js +2 -2
- package/dist/core/approvals/shortcut-resolver.js +98 -0
- package/dist/core/artifact-chain/dispatcher.js +148 -0
- package/dist/core/artifact-chain/exporter.js +164 -0
- package/dist/core/artifact-chain/state.js +243 -0
- package/dist/core/artifact-chain/steps.js +169 -0
- package/dist/core/ask-user/question.js +92 -0
- package/dist/core/audit/audit-trail.js +275 -0
- package/dist/core/auth/ensure-authenticated.js +129 -0
- package/dist/core/auth/env-provider.js +238 -0
- package/dist/core/auto-open-browser.js +4 -4
- package/dist/core/auto-update/channels.js +122 -0
- package/dist/core/auto-update/checker.js +241 -0
- package/dist/core/auto-update/state.js +235 -0
- package/dist/core/bare-mode/index.js +107 -0
- package/dist/core/bash/redirect.js +281 -0
- package/dist/core/bash-classifier.js +436 -40
- package/dist/core/checkpoint/resumer.js +149 -0
- package/dist/core/checkpoint/rewinder.js +291 -0
- package/dist/core/checkpoints/shadow-git.js +670 -0
- package/dist/core/citations/parser.js +109 -0
- package/dist/core/classifier/yolo-classifier.js +88 -0
- package/dist/core/codegraph/decision-store.js +248 -0
- package/dist/core/codegraph/detect-repo.js +459 -0
- package/dist/core/codegraph/install.js +134 -0
- package/dist/core/codegraph/offer-hook.js +220 -0
- package/dist/core/compact/auto-trigger.js +96 -0
- package/dist/core/compact/buffer-rewriter.js +115 -0
- package/dist/core/compact/summarizer.js +208 -0
- package/dist/core/compact/token-counter.js +108 -0
- package/dist/core/consensus/anvil-fanout.js +25 -25
- package/dist/core/consensus/diff-capture.js +121 -12
- package/dist/core/consensus/rubric.js +21 -21
- package/dist/core/context/builder.js +6 -6
- package/dist/core/context/compaction-events.js +8 -8
- package/dist/core/context/compaction.js +31 -31
- package/dist/core/context/index.js +15 -8
- package/dist/core/context/invariants.js +51 -51
- package/dist/core/context/markdown-loader.js +28 -10
- package/dist/core/context/markdown-traverse.js +255 -0
- package/dist/core/context/pugiignore.js +41 -41
- package/dist/core/context/repo-skeleton.js +37 -37
- package/dist/core/context/tool-eviction.js +55 -0
- package/dist/core/context/watcher.js +32 -32
- package/dist/core/context/working-set.js +23 -23
- package/dist/core/coordinator/agent-tools.js +77 -0
- package/dist/core/coordinator/agent-toolset.js +65 -0
- package/dist/core/coordinator/fsm.js +73 -0
- package/dist/core/coordinator/mode-fsm.js +70 -0
- package/dist/core/cost/rate-card.js +129 -0
- package/dist/core/cost/tracker.js +221 -0
- package/dist/core/credentials.js +12 -12
- package/dist/core/cron/scheduler.js +138 -0
- package/dist/core/denial-tracking/index.js +8 -0
- package/dist/core/denial-tracking/state.js +264 -0
- package/dist/core/diagnostics/probe-runner.js +93 -0
- package/dist/core/diagnostics/probes/api.js +46 -0
- package/dist/core/diagnostics/probes/auth.js +93 -0
- package/dist/core/diagnostics/probes/bare-mode.js +42 -0
- package/dist/core/diagnostics/probes/cli-version.js +127 -0
- package/dist/core/diagnostics/probes/config.js +72 -0
- package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
- package/dist/core/diagnostics/probes/disk.js +81 -0
- package/dist/core/diagnostics/probes/engine-live.js +46 -0
- package/dist/core/diagnostics/probes/git.js +65 -0
- package/dist/core/diagnostics/probes/hooks.js +118 -0
- package/dist/core/diagnostics/probes/mcp.js +75 -0
- package/dist/core/diagnostics/probes/node.js +59 -0
- package/dist/core/diagnostics/probes/pnpm.js +36 -0
- package/dist/core/diagnostics/probes/pugi-md.js +89 -0
- package/dist/core/diagnostics/probes/sandbox.js +40 -0
- package/dist/core/diagnostics/probes/session.js +74 -0
- package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
- package/dist/core/diagnostics/probes/workspace.js +63 -0
- package/dist/core/diagnostics/types.js +70 -0
- package/dist/core/dispatch/cache-cleanup.js +197 -0
- package/dist/core/dispatch/cache-handoff.js +295 -0
- package/dist/core/edits/apply-patch-layer-e.js +189 -0
- package/dist/core/edits/dispatch.js +293 -7
- package/dist/core/edits/format-matrix.js +26 -0
- package/dist/core/edits/fuzzy-ladder.js +650 -0
- package/dist/core/edits/index.js +3 -1
- package/dist/core/edits/journal.js +199 -0
- package/dist/core/edits/layer-a-apply.js +15 -15
- package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
- package/dist/core/edits/layer-b-apply.js +9 -9
- package/dist/core/edits/layer-c-apply.js +6 -6
- package/dist/core/edits/layer-d-ast.js +557 -14
- package/dist/core/edits/marker-parser.js +12 -12
- package/dist/core/edits/security-gate.js +27 -27
- package/dist/core/edits/verify-hook.js +273 -0
- package/dist/core/edits/worktree.js +322 -0
- package/dist/core/engine/anvil-client.js +140 -26
- package/dist/core/engine/auto-compact.js +179 -0
- package/dist/core/engine/budgets.js +186 -0
- package/dist/core/engine/context-prefix.js +155 -0
- package/dist/core/engine/index.js +1 -1
- package/dist/core/engine/intensity.js +158 -0
- package/dist/core/engine/intent.js +260 -0
- package/dist/core/engine/native-pugi.js +1295 -227
- package/dist/core/engine/prompts.js +134 -16
- package/dist/core/engine/strip-internal-fields.js +124 -0
- package/dist/core/engine/tool-bridge.js +1295 -59
- package/dist/core/evaluation/golden-dataset.js +293 -0
- package/dist/core/feedback/queue.js +177 -0
- package/dist/core/feedback/submitter.js +145 -0
- package/dist/core/file-cache.js +113 -1
- package/dist/core/flatten/flatten-repo.js +439 -0
- package/dist/core/format/osc8-link.js +28 -0
- package/dist/core/hook-chains.js +392 -0
- package/dist/core/hooks/citation-verify-hook.js +138 -0
- package/dist/core/hooks/citation-verify.js +112 -0
- package/dist/core/hooks/events.js +44 -0
- package/dist/core/hooks/index.js +15 -0
- package/dist/core/hooks/registry.js +213 -0
- package/dist/core/hooks/runner.js +236 -0
- package/dist/core/hooks/v2/event-emitter.js +115 -0
- package/dist/core/hooks/v2/executor.js +282 -0
- package/dist/core/hooks/v2/index.js +25 -0
- package/dist/core/hooks/v2/lifecycle.js +104 -0
- package/dist/core/hooks/v2/loader.js +216 -0
- package/dist/core/hooks/v2/matcher.js +125 -0
- package/dist/core/hooks/v2/trust.js +143 -0
- package/dist/core/hooks/v2/types.js +86 -0
- package/dist/core/image/renderer.js +71 -0
- package/dist/core/init/detector.js +582 -0
- package/dist/core/init/template-renderer.js +242 -0
- package/dist/core/jobs/registry.js +18 -18
- package/dist/core/ledger/results-tsv.js +142 -0
- package/dist/core/log-discipline/stdout-redirect.js +51 -0
- package/dist/core/lsp/cache.js +105 -0
- package/dist/core/lsp/client.js +776 -0
- package/dist/core/lsp/language-detect.js +66 -0
- package/dist/core/lsp/post-edit-diagnostics.js +171 -0
- package/dist/core/lsp/symbol-tools.js +372 -0
- package/dist/core/mcp/client.js +97 -28
- package/dist/core/mcp/http-server.js +553 -0
- package/dist/core/mcp/orchestrator-tools.js +662 -0
- package/dist/core/mcp/permission.js +190 -0
- package/dist/core/mcp/registry.js +39 -17
- package/dist/core/mcp/server-tools.js +219 -0
- package/dist/core/mcp/server.js +397 -0
- package/dist/core/mcp/trust.js +10 -10
- package/dist/core/memory/dual-write.js +416 -0
- package/dist/core/memory/passive-extract.js +130 -0
- package/dist/core/memory/phase1-kinds.js +20 -0
- package/dist/core/memory/secret-scanner.js +304 -0
- package/dist/core/memory-sync/queue.js +170 -0
- package/dist/core/metrics/extract.js +113 -0
- package/dist/core/modes/roo-modes.js +68 -0
- package/dist/core/onboarding/ensure-initialized.js +133 -0
- package/dist/core/onboarding/marker.js +111 -0
- package/dist/core/onboarding/telemetry-state.js +108 -0
- package/dist/core/output-style/presets.js +176 -0
- package/dist/core/output-style/state.js +185 -0
- package/dist/core/path-security.js +287 -5
- package/dist/core/permission.js +82 -22
- package/dist/core/permissions/auto-classifier.js +124 -0
- package/dist/core/permissions/bash-parser.js +371 -0
- package/dist/core/permissions/circuit-breaker.js +83 -0
- package/dist/core/permissions/constrained-edit.js +91 -0
- package/dist/core/permissions/gate.js +278 -0
- package/dist/core/permissions/index.js +20 -0
- package/dist/core/permissions/mode.js +174 -0
- package/dist/core/permissions/network-egress.js +137 -0
- package/dist/core/permissions/state.js +241 -0
- package/dist/core/permissions/tool-class.js +93 -0
- package/dist/core/plan-mode/ui-state.js +51 -0
- package/dist/core/plans/plan-artifact.js +721 -0
- package/dist/core/policy-limits/etag-store.js +122 -0
- package/dist/core/prd-check/parser.js +215 -0
- package/dist/core/prd-check/reporter.js +127 -0
- package/dist/core/prd-check/session-review.js +557 -0
- package/dist/core/prd-check/verifiers.js +223 -0
- package/dist/core/prompt-cache/client-cache.js +99 -0
- package/dist/core/prompts/assembly.js +29 -0
- package/dist/core/prompts/registry.js +364 -0
- package/dist/core/pugi-md/cc-compat-rules.js +735 -0
- package/dist/core/pugi-md/context-injector.js +76 -0
- package/dist/core/pugi-md/walk-up.js +207 -0
- package/dist/core/python/uv-installer.js +270 -0
- package/dist/core/python/uv-resolver.js +83 -0
- package/dist/core/rate-limit/narrator.js +146 -0
- package/dist/core/recipes/cli-types.js +20 -0
- package/dist/core/recipes/loader.js +103 -0
- package/dist/core/recipes/runner.js +345 -0
- package/dist/core/recipes/schema.js +587 -0
- package/dist/core/release-notes/parser.js +241 -0
- package/dist/core/release-notes/state.js +116 -0
- package/dist/core/repl/ask.js +37 -37
- package/dist/core/repl/cancellation.js +26 -26
- package/dist/core/repl/cap-warning.js +4 -4
- package/dist/core/repl/clipboard-read.js +11 -11
- package/dist/core/repl/dispatch-fsm.js +12 -12
- package/dist/core/repl/history-search.js +15 -15
- package/dist/core/repl/history.js +28 -18
- package/dist/core/repl/kill-ring.js +5 -5
- package/dist/core/repl/model-pricing.js +135 -0
- package/dist/core/repl/privacy-banner.js +22 -22
- package/dist/core/repl/session.js +2157 -214
- package/dist/core/repl/slash-commands.js +533 -40
- package/dist/core/repl/store/index.js +1 -1
- package/dist/core/repl/store/jsonl-log.js +22 -22
- package/dist/core/repl/store/lockfile.js +10 -10
- package/dist/core/repl/store/session-store.js +136 -107
- package/dist/core/repl/store/types.js +15 -15
- package/dist/core/repl/store/uuid-v7.js +12 -12
- package/dist/core/repl/workspace-context.js +43 -21
- package/dist/core/repo-map/build.js +125 -0
- package/dist/core/repo-map/cache.js +185 -0
- package/dist/core/repo-map/extractor.js +254 -0
- package/dist/core/repo-map/formatter.js +145 -0
- package/dist/core/repo-map/page-rank.js +105 -0
- package/dist/core/repo-map/scanner.js +211 -0
- package/dist/core/retry-budget/budget.js +284 -0
- package/dist/core/retry-budget/index.js +5 -0
- package/dist/core/retry-budget/retry-cap.js +74 -0
- package/dist/core/routing/lead-worker.js +43 -0
- package/dist/core/routing/pre-flight-estimator.js +108 -0
- package/dist/core/runs/run-tree.js +103 -0
- package/dist/core/security/injection-scanner.js +367 -0
- package/dist/core/security/output-filter.js +418 -0
- package/dist/core/session/env-file.js +105 -0
- package/dist/core/session/section-budgets.js +140 -0
- package/dist/core/session.js +92 -0
- package/dist/core/settings.js +286 -5
- package/dist/core/share/formatter.js +271 -0
- package/dist/core/share/redactor.js +221 -0
- package/dist/core/share/uploader.js +267 -0
- package/dist/core/skills/defaults.js +457 -0
- package/dist/core/skills/loader.js +22 -22
- package/dist/core/skills/sources.js +27 -27
- package/dist/core/smoke/headless-driver.js +174 -0
- package/dist/core/smoke/orchestrator.js +194 -0
- package/dist/core/smoke/runner.js +238 -0
- package/dist/core/smoke/scenario-parser.js +316 -0
- package/dist/core/statusline.js +99 -0
- package/dist/core/subagents/dispatcher-real.js +600 -0
- package/dist/core/subagents/dispatcher.js +132 -43
- package/dist/core/subagents/index.js +19 -6
- package/dist/core/subagents/isolation-matrix.js +213 -0
- package/dist/core/subagents/spawn.js +19 -4
- package/dist/core/telemetry/emitter.js +229 -0
- package/dist/core/telemetry/queue.js +251 -0
- package/dist/core/theme/context.js +91 -0
- package/dist/core/theme/presets.js +228 -0
- package/dist/core/theme/state.js +181 -0
- package/dist/core/todos/invariant.js +10 -0
- package/dist/core/todos/state.js +177 -0
- package/dist/core/tool-schema/compressor.js +89 -0
- package/dist/core/transport/version-interceptor.js +166 -0
- package/dist/core/trust.js +2 -2
- package/dist/core/tui/thinking-block.js +64 -0
- package/dist/core/vim/keymap.js +288 -0
- package/dist/core/vim/state.js +92 -0
- package/dist/core/watch-markers/marker-watcher.js +133 -0
- package/dist/core/worktree-manager/cleanup.js +123 -0
- package/dist/core/worktree-manager/manager.js +303 -0
- package/dist/index.js +28 -0
- package/dist/runtime/bootstrap.js +190 -0
- package/dist/runtime/cli.js +4151 -489
- package/dist/runtime/commands/agents.js +30 -30
- package/dist/runtime/commands/budget.js +5 -5
- package/dist/runtime/commands/cancel.js +231 -0
- package/dist/runtime/commands/chain.js +489 -0
- package/dist/runtime/commands/codegraph-status.js +227 -0
- package/dist/runtime/commands/compact.js +297 -0
- package/dist/runtime/commands/config.js +32 -32
- package/dist/runtime/commands/cost.js +199 -0
- package/dist/runtime/commands/delegate.js +244 -13
- package/dist/runtime/commands/dispatch.js +126 -0
- package/dist/runtime/commands/doctor.js +579 -0
- package/dist/runtime/commands/feedback.js +184 -0
- package/dist/runtime/commands/hooks.js +184 -0
- package/dist/runtime/commands/init.js +254 -0
- package/dist/runtime/commands/lsp.js +368 -0
- package/dist/runtime/commands/mcp.js +879 -0
- package/dist/runtime/commands/memory.js +582 -0
- package/dist/runtime/commands/model.js +237 -0
- package/dist/runtime/commands/onboarding.js +275 -0
- package/dist/runtime/commands/patch.js +128 -0
- package/dist/runtime/commands/permissions.js +112 -0
- package/dist/runtime/commands/plan.js +143 -0
- package/dist/runtime/commands/prd-check.js +285 -0
- package/dist/runtime/commands/privacy.js +17 -17
- package/dist/runtime/commands/recipe.js +325 -0
- package/dist/runtime/commands/redo-blob-store.js +92 -0
- package/dist/runtime/commands/redo.js +361 -0
- package/dist/runtime/commands/release-notes.js +229 -0
- package/dist/runtime/commands/repo-map.js +95 -0
- package/dist/runtime/commands/report.js +299 -0
- package/dist/runtime/commands/resume.js +118 -0
- package/dist/runtime/commands/review-consensus.js +68 -53
- package/dist/runtime/commands/rewind.js +333 -0
- package/dist/runtime/commands/roster.js +14 -14
- package/dist/runtime/commands/sessions.js +163 -0
- package/dist/runtime/commands/share.js +316 -0
- package/dist/runtime/commands/skills.js +31 -31
- package/dist/runtime/commands/status.js +186 -0
- package/dist/runtime/commands/stickers.js +82 -0
- package/dist/runtime/commands/style.js +194 -0
- package/dist/runtime/commands/theme.js +196 -0
- package/dist/runtime/commands/undo.js +54 -22
- package/dist/runtime/commands/update.js +289 -0
- package/dist/runtime/commands/vim.js +140 -0
- package/dist/runtime/commands/worktree.js +177 -0
- package/dist/runtime/commands/worktrees.js +155 -0
- package/dist/runtime/headless-repl.js +195 -0
- package/dist/runtime/headless.js +543 -0
- package/dist/runtime/load-hooks-or-exit.js +71 -0
- package/dist/runtime/plan-decompose.js +531 -0
- package/dist/runtime/update-check.js +28 -28
- package/dist/runtime/version.js +65 -0
- package/dist/skills/bundled/batch.js +617 -0
- package/dist/skills/bundled/index.js +45 -0
- package/dist/skills/bundled/loop.js +358 -0
- package/dist/skills/bundled/remember.js +383 -0
- package/dist/skills/bundled/simplify.js +289 -0
- package/dist/skills/bundled/skillify.js +373 -0
- package/dist/skills/bundled/stuck.js +558 -0
- package/dist/skills/bundled/verify.js +439 -0
- package/dist/testing/vcr.js +486 -0
- package/dist/tools/agent-tool.js +229 -0
- package/dist/tools/apply-patch.js +556 -0
- package/dist/tools/ask-user-question.js +222 -0
- package/dist/tools/ask-user.js +115 -0
- package/dist/tools/bash.js +623 -45
- package/dist/tools/brief.js +224 -0
- package/dist/tools/enter-worktree.js +250 -0
- package/dist/tools/exit-worktree.js +147 -0
- package/dist/tools/file-tools.js +161 -44
- package/dist/tools/lsp-tools.js +189 -0
- package/dist/tools/mcp-tool.js +260 -0
- package/dist/tools/multi-edit.js +361 -0
- package/dist/tools/powershell.js +268 -0
- package/dist/tools/registry.js +85 -0
- package/dist/tools/skill-tool.js +96 -0
- package/dist/tools/sleep.js +99 -0
- package/dist/tools/synthetic-output.js +133 -0
- package/dist/tools/tasks.js +208 -0
- package/dist/tools/todo-write.js +184 -0
- package/dist/tools/verify-plan-execution.js +295 -0
- package/dist/tools/web-fetch-injection-scanner.js +207 -0
- package/dist/tools/web-fetch.js +195 -10
- package/dist/tools/web-search.js +458 -0
- package/dist/tui/agent-progress-card.js +111 -0
- package/dist/tui/agent-tree.js +11 -1
- package/dist/tui/ask-modal.js +14 -14
- package/dist/tui/ask-user-question-prompt.js +203 -0
- package/dist/tui/compact-banner.js +81 -0
- package/dist/tui/conversation-pane.js +85 -11
- package/dist/tui/cost-table.js +111 -0
- package/dist/tui/device-flow.js +2 -2
- package/dist/tui/doctor-table.js +46 -0
- package/dist/tui/feedback-prompt.js +156 -0
- package/dist/tui/input-box.js +247 -32
- package/dist/tui/login-picker.js +3 -3
- package/dist/tui/markdown-render.js +6 -6
- package/dist/tui/onboarding-wizard.js +240 -0
- package/dist/tui/permissions-picker.js +86 -0
- package/dist/tui/render.js +35 -0
- package/dist/tui/repl-render.js +332 -54
- package/dist/tui/repl-splash-art.js +16 -16
- package/dist/tui/repl-splash-mascot.js +48 -24
- package/dist/tui/repl-splash.js +22 -22
- package/dist/tui/repl.js +124 -44
- package/dist/tui/slash-palette.js +6 -6
- package/dist/tui/splash.js +2 -2
- package/dist/tui/status-bar.js +109 -31
- package/dist/tui/status-table.js +7 -0
- package/dist/tui/stickers-art.js +136 -0
- package/dist/tui/style-table.js +28 -0
- package/dist/tui/theme-table.js +29 -0
- package/dist/tui/thinking-spinner.js +123 -0
- package/dist/tui/tool-stream-pane.js +53 -4
- package/dist/tui/update-banner.js +27 -2
- package/dist/tui/vim-input.js +267 -0
- package/dist/tui/welcome-banner.js +107 -0
- package/dist/tui/welcome-data.js +293 -0
- package/dist/tui/workspace-context.js +2 -2
- package/docs/examples/codegraph.mcp.json +10 -0
- package/package.json +23 -6
- package/test/scenarios/codegen-create-file.scenario.txt +13 -0
- package/test/scenarios/compact-force.scenario.txt +11 -0
- package/test/scenarios/identity.scenario.txt +11 -0
- package/test/scenarios/persona-handoff.scenario.txt +11 -0
- package/test/scenarios/walkback.scenario.txt +12 -0
- package/dist/core/engine/compaction-hook.js +0 -154
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `pugi permissions` / `/permissions` — 4-mode gate control.
|
|
3
|
+
*
|
|
4
|
+
* Two entry points share one runtime helper:
|
|
5
|
+
* 1. `/permissions` in the REPL — forwarded by `core/repl/session.ts`.
|
|
6
|
+
* 2. `pugi permissions ...` top-level CLI command (handler in
|
|
7
|
+
* `runtime/cli.ts`).
|
|
8
|
+
*
|
|
9
|
+
* Both pass a `PermissionsCommand` payload describing the operator
|
|
10
|
+
* intent (show / flip / persist) and a `writeOutput` callback that
|
|
11
|
+
* lets the caller route the rendered lines into the right surface
|
|
12
|
+
* (REPL transcript vs. stdout). The helper is intentionally I/O-free
|
|
13
|
+
* itself — it produces lines and lets the caller stream them.
|
|
14
|
+
*/
|
|
15
|
+
import { DEFAULT_PERMISSION_MODE, PERMISSION_MODES, PERMISSION_MODE_GLOSS, getCurrentMode, getGlobalDefaultMode, setCurrentMode, setGlobalDefaultMode, } from '../../core/permissions/index.js';
|
|
16
|
+
/**
|
|
17
|
+
* Run the `/permissions` or `pugi permissions` flow. Side effects:
|
|
18
|
+
* - When `command.mode` is undefined: prints the current mode + the
|
|
19
|
+
* 4-mode table (no writes).
|
|
20
|
+
* - When `command.mode === 'bypass'` without `confirmBypass`: prints
|
|
21
|
+
* a refusal + the safety copy, no writes.
|
|
22
|
+
* - When `command.mode` is set + valid: writes workspace session
|
|
23
|
+
* state; optionally writes global default when `persist` is true.
|
|
24
|
+
* - Always prints the new effective mode + a one-line confirmation.
|
|
25
|
+
*/
|
|
26
|
+
export async function runPermissionsCommand(command, ctx) {
|
|
27
|
+
if (!command.mode) {
|
|
28
|
+
renderCurrentMode(ctx);
|
|
29
|
+
renderModeTable(ctx);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (command.mode === 'bypassPermissions' && !command.confirmBypass) {
|
|
33
|
+
ctx.writeOutput('bypassPermissions disables policy hooks (skill steering, denial tracking) AND skips the deny-list.');
|
|
34
|
+
ctx.writeOutput('Catastrophic patterns (rm -rf /, fork bomb, dd if=/) still trip the circuit-breaker, но that is the only guardrail left.');
|
|
35
|
+
ctx.writeOutput('Run `/permissions bypassPermissions --confirm` to acknowledge before flipping.');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
setCurrentMode(ctx.workspaceRoot, command.mode);
|
|
39
|
+
if (command.persist) {
|
|
40
|
+
setGlobalDefaultMode(command.mode, ctx.homeDir);
|
|
41
|
+
}
|
|
42
|
+
const persistedHint = command.persist
|
|
43
|
+
? ' Persisted to ~/.pugi/config.json for future sessions.'
|
|
44
|
+
: '';
|
|
45
|
+
ctx.writeOutput(`Permission mode set to '${command.mode}'.${persistedHint} ${PERMISSION_MODE_GLOSS[command.mode]}`);
|
|
46
|
+
if (command.mode === 'bypassPermissions') {
|
|
47
|
+
ctx.writeOutput('bypassPermissions — all tools execute without prompts AND policy hooks disabled (circuit-breaker still trips on rm -rf /). Switch back with /permissions dontAsk.');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Print the resolved current mode + the layered source. The merge
|
|
52
|
+
* order mirrors `resolveMode()`: workspace > global > default.
|
|
53
|
+
*/
|
|
54
|
+
function renderCurrentMode(ctx) {
|
|
55
|
+
const workspace = getCurrentMode(ctx.workspaceRoot);
|
|
56
|
+
const global = getGlobalDefaultMode(ctx.homeDir);
|
|
57
|
+
const effective = workspace ?? global ?? DEFAULT_PERMISSION_MODE;
|
|
58
|
+
const source = workspace
|
|
59
|
+
? 'workspace session.json'
|
|
60
|
+
: global
|
|
61
|
+
? 'global ~/.pugi/config.json'
|
|
62
|
+
: 'default (no override)';
|
|
63
|
+
ctx.writeOutput(`Current permission mode: ${effective} (source: ${source})`);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Print the 4-mode reference table. Keeps the gloss + the side-effect
|
|
67
|
+
* matrix in one place so the operator can see the contract while they
|
|
68
|
+
* decide which mode to switch to.
|
|
69
|
+
*/
|
|
70
|
+
function renderModeTable(ctx) {
|
|
71
|
+
ctx.writeOutput('');
|
|
72
|
+
ctx.writeOutput('Permission modes (Shift+Tab cycles in REPL):');
|
|
73
|
+
for (const mode of PERMISSION_MODES) {
|
|
74
|
+
// : longest canonical name is `bypassPermissions` (17 chars).
|
|
75
|
+
ctx.writeOutput(` ${mode.padEnd(18)} ${PERMISSION_MODE_GLOSS[mode]}`);
|
|
76
|
+
}
|
|
77
|
+
ctx.writeOutput('');
|
|
78
|
+
ctx.writeOutput('Switch with `/permissions <mode> [--persist]`. bypassPermissions requires `--confirm`.');
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Render the one-shot banner shown on session boot when the effective
|
|
82
|
+
* mode is `bypass`. The caller (engine adapter / REPL bootstrap) calls
|
|
83
|
+
* this once per session — repeated invocations are idempotent in copy
|
|
84
|
+
* but the caller is responsible for the once-only semantics.
|
|
85
|
+
*/
|
|
86
|
+
export function renderBypassBanner(writeOutput) {
|
|
87
|
+
writeOutput('bypassPermissions — all tools execute without prompts AND policy hooks disabled (circuit-breaker still trips on rm -rf /). Switch back with /permissions dontAsk.');
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Resolve the effective mode + the layered source label used by the
|
|
91
|
+
* Ink picker. Shares the merge order with `renderCurrentMode` above so
|
|
92
|
+
* the picker title and the bare `/permissions` print stay in lock-step.
|
|
93
|
+
*
|
|
94
|
+
* Exposed publicly because the host (session.ts) needs the same merge
|
|
95
|
+
* shape to seed the Ink picker BEFORE it mounts.
|
|
96
|
+
*/
|
|
97
|
+
export function resolveLayeredMode(workspaceRoot, homeDir) {
|
|
98
|
+
const workspace = getCurrentMode(workspaceRoot);
|
|
99
|
+
const global = getGlobalDefaultMode(homeDir);
|
|
100
|
+
const effective = workspace ?? global ?? DEFAULT_PERMISSION_MODE;
|
|
101
|
+
const source = workspace
|
|
102
|
+
? 'workspace session.json'
|
|
103
|
+
: global
|
|
104
|
+
? 'global ~/.pugi/config.json'
|
|
105
|
+
: 'default (no override)';
|
|
106
|
+
return {
|
|
107
|
+
effective,
|
|
108
|
+
source,
|
|
109
|
+
firstRun: !workspace && !global,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=permissions.js.map
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `pugi plan` / `/plan` — quick mode-switch shortcut.
|
|
3
|
+
*
|
|
4
|
+
* `/plan` is the slick UX shortcut for `/permissions plan`: one keystroke
|
|
5
|
+
* (well, five) puts the gate into plan mode + surfaces a banner so the
|
|
6
|
+
* operator knows write/dispatch tools are refused. The model goes off and
|
|
7
|
+
* thinks / researches without side effects until the operator types
|
|
8
|
+
* `/plan --back` (restore previous mode) or explicitly flips with
|
|
9
|
+
* `/permissions <mode>`.
|
|
10
|
+
*
|
|
11
|
+
* The slash and CLI surfaces both go through `runPlanCommand` — same
|
|
12
|
+
* separation as `runPermissionsCommand`. The runtime is I/O free w.r.t.
|
|
13
|
+
* the engine; the optional one-shot dispatch (`/plan <prompt>`) is
|
|
14
|
+
* handled by the CLI dispatcher AFTER this helper sets the workspace
|
|
15
|
+
* mode so the existing `runEngineTask('plan')` path sees plan mode as
|
|
16
|
+
* the workspace state without needing a parallel code path.
|
|
17
|
+
*
|
|
18
|
+
* Verdicts (the helper returns one so the caller can decide what to do
|
|
19
|
+
* after the mode write — print the banner, dispatch the engine, no-op):
|
|
20
|
+
* - `entered` — first `/plan` from a non-plan mode. Print the
|
|
21
|
+
* banner. Caller may then run a one-shot prompt.
|
|
22
|
+
* - `already-in-plan` — `/plan` while already in plan. No-op + show
|
|
23
|
+
* current. No banner reprint.
|
|
24
|
+
* - `reverted` — `/plan --back` popped the snapshot. Print a
|
|
25
|
+
* one-line confirmation; no banner.
|
|
26
|
+
* - `no-previous` — `/plan --back` without a snapshot. Print a
|
|
27
|
+
* clear "nothing to revert" line.
|
|
28
|
+
* - `persisted` — `/plan --persist` wrote the global default
|
|
29
|
+
* AND set workspace state to plan. Banner +
|
|
30
|
+
* persistence-confirmation line.
|
|
31
|
+
*
|
|
32
|
+
* `previousMode` semantics: stashed BEFORE the workspace write on
|
|
33
|
+
* `entered` / `persisted`. Cleared after a successful `reverted` so a
|
|
34
|
+
* second `--back` reports `no-previous` instead of looping back to plan.
|
|
35
|
+
*/
|
|
36
|
+
import { PERMISSION_MODES, PERMISSION_MODE_GLOSS, getCurrentMode, getGlobalDefaultMode, getPreviousMode, setCurrentMode, setGlobalDefaultMode, setPreviousMode, } from '../../core/permissions/index.js';
|
|
37
|
+
/**
|
|
38
|
+
* Run the `/plan` flow. Side effects:
|
|
39
|
+
*
|
|
40
|
+
* command.back = true:
|
|
41
|
+
* - If a previousMode snapshot exists → restore it, clear snapshot,
|
|
42
|
+
* return `reverted`.
|
|
43
|
+
* - Otherwise → no writes, return `no-previous`.
|
|
44
|
+
*
|
|
45
|
+
* command.back = false, current mode is plan:
|
|
46
|
+
* - If `--persist`, write global config (no workspace re-write — it
|
|
47
|
+
* is already plan).
|
|
48
|
+
* - Print "already in plan" + the banner-summary line. Return
|
|
49
|
+
* `already-in-plan`.
|
|
50
|
+
*
|
|
51
|
+
* command.back = false, current mode is NOT plan:
|
|
52
|
+
* - Snapshot current mode → previousPermissionMode.
|
|
53
|
+
* - Write workspace mode = plan.
|
|
54
|
+
* - If `--persist`, also write global config.
|
|
55
|
+
* - Print the banner + (if persisted) the persistence line.
|
|
56
|
+
* - Return `entered` or `persisted`.
|
|
57
|
+
*
|
|
58
|
+
* --back + --persist is a no-op for persistence (revert never writes
|
|
59
|
+
* global config) but the revert itself fires.
|
|
60
|
+
*/
|
|
61
|
+
export async function runPlanCommand(command, ctx) {
|
|
62
|
+
const current = effectiveMode(ctx);
|
|
63
|
+
if (command.back) {
|
|
64
|
+
const prev = getPreviousMode(ctx.workspaceRoot);
|
|
65
|
+
if (!prev) {
|
|
66
|
+
ctx.writeOutput(`No previous mode to restore. Current: ${current}. Use \`/permissions <mode>\` to switch explicitly.`);
|
|
67
|
+
return { verdict: 'no-previous', mode: current };
|
|
68
|
+
}
|
|
69
|
+
setCurrentMode(ctx.workspaceRoot, prev);
|
|
70
|
+
setPreviousMode(ctx.workspaceRoot, null);
|
|
71
|
+
ctx.writeOutput(`Switched back to '${prev}' mode. ${PERMISSION_MODE_GLOSS[prev]}`);
|
|
72
|
+
return { verdict: 'reverted', mode: prev };
|
|
73
|
+
}
|
|
74
|
+
if (current === 'plan') {
|
|
75
|
+
// Repeat /plan in plan mode is a no-op for the mode write, but
|
|
76
|
+
// --persist still honours the operator's intent to lock plan as
|
|
77
|
+
// the global default for future sessions.
|
|
78
|
+
if (command.persist) {
|
|
79
|
+
setGlobalDefaultMode('plan', ctx.homeDir);
|
|
80
|
+
ctx.writeOutput('Already in plan mode. Persisted plan as the default for future sessions (~/.pugi/config.json).');
|
|
81
|
+
return { verdict: 'persisted', mode: 'plan' };
|
|
82
|
+
}
|
|
83
|
+
ctx.writeOutput(`Already in plan mode. ${PERMISSION_MODE_GLOSS.plan} Switch back with \`/plan --back\` or \`/permissions <mode>\`.`);
|
|
84
|
+
return { verdict: 'already-in-plan', mode: 'plan' };
|
|
85
|
+
}
|
|
86
|
+
// Entering plan mode from a non-plan baseline. Stash the current mode
|
|
87
|
+
// BEFORE the write so /plan --back can pop it. We intentionally use the
|
|
88
|
+
// observed effective mode (workspace || global || default) rather than
|
|
89
|
+
// strictly the workspace value — if the operator's previous mode was
|
|
90
|
+
// sourced from the global config, `--back` should restore that observed
|
|
91
|
+
// state, not silently degrade to default.
|
|
92
|
+
setPreviousMode(ctx.workspaceRoot, current);
|
|
93
|
+
setCurrentMode(ctx.workspaceRoot, 'plan');
|
|
94
|
+
if (command.persist) {
|
|
95
|
+
setGlobalDefaultMode('plan', ctx.homeDir);
|
|
96
|
+
}
|
|
97
|
+
for (const line of renderPlanBanner()) {
|
|
98
|
+
ctx.writeOutput(line);
|
|
99
|
+
}
|
|
100
|
+
if (command.persist) {
|
|
101
|
+
ctx.writeOutput('Persisted plan as the default for future sessions (~/.pugi/config.json).');
|
|
102
|
+
return { verdict: 'persisted', mode: 'plan' };
|
|
103
|
+
}
|
|
104
|
+
return { verdict: 'entered', mode: 'plan' };
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Render the plan-mode banner as a sequence of lines. The slash + CLI
|
|
108
|
+
* surfaces both print these line-by-line through their respective
|
|
109
|
+
* `writeOutput` sinks so the Ink REPL conversation pane and the plain
|
|
110
|
+
* stdout pipeline render identically.
|
|
111
|
+
*
|
|
112
|
+
* The box-drawing uses light-line glyphs (U+2500 family) which render in
|
|
113
|
+
* every modern terminal we target (Linux/macOS/Windows Terminal/iTerm/
|
|
114
|
+
* Ghostty/Alacritty). No emoji per brand-voice gate.
|
|
115
|
+
*/
|
|
116
|
+
export function renderPlanBanner() {
|
|
117
|
+
return [
|
|
118
|
+
'┌─ Plan mode active ────────────────────────────────────────┐',
|
|
119
|
+
'│ Read-only tools allowed. Write/dispatch tools blocked. │',
|
|
120
|
+
'│ Pugi will think + research without making changes. │',
|
|
121
|
+
'│ Switch back: /plan --back or /permissions <mode> │',
|
|
122
|
+
'└───────────────────────────────────────────────────────────┘',
|
|
123
|
+
];
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Resolve the effective mode at the moment the helper was invoked,
|
|
127
|
+
* mirroring `resolveMode` but without taking a CLI flag (the `/plan`
|
|
128
|
+
* helper is called AFTER the top-level `--mode` flag has been applied
|
|
129
|
+
* to the workspace, so the file state is the source of truth here).
|
|
130
|
+
*/
|
|
131
|
+
function effectiveMode(ctx) {
|
|
132
|
+
const workspace = getCurrentMode(ctx.workspaceRoot);
|
|
133
|
+
if (workspace)
|
|
134
|
+
return workspace;
|
|
135
|
+
const global = getGlobalDefaultMode(ctx.homeDir);
|
|
136
|
+
if (global)
|
|
137
|
+
return global;
|
|
138
|
+
// canonical default — `PERMISSION_MODES[0]` is `default` (the
|
|
139
|
+
// CC-parity ask-every-call ground state). Fall back literally if the
|
|
140
|
+
// array is empty (defensive — should never happen).
|
|
141
|
+
return PERMISSION_MODES[0] ?? 'default';
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `pugi prd-check` — Pugi verified-deliverable gate .
|
|
3
|
+
*
|
|
4
|
+
* Runs a PRD ↔ delivered-artifact verification sweep BEFORE the
|
|
5
|
+
* operator (or an autonomous agent) claims a feature is done.
|
|
6
|
+
* Reads `docs/prd/<feature>.md` (or any explicit path), parses the
|
|
7
|
+
* acceptance-criteria section, and runs file / test / doc / command
|
|
8
|
+
* / route verifiers per criterion. Output mirrors `pugi doctor`:
|
|
9
|
+
* either a plain-text table or a JSON envelope (`--json`).
|
|
10
|
+
*
|
|
11
|
+
* Module contract (mirrors L17 doctor split):
|
|
12
|
+
*
|
|
13
|
+
* - parser + verifiers + reporter are pure with respect to deps;
|
|
14
|
+
* this file is the THIN wiring that resolves cwd, glob-expands
|
|
15
|
+
* `--all`, loads each PRD, and forwards the structured result
|
|
16
|
+
* к the supplied writeOutput sink.
|
|
17
|
+
*
|
|
18
|
+
* - `runPrdCheckCommand` is the single entry point. Both the
|
|
19
|
+
* top-level `pugi prd-check` shell command AND the in-REPL
|
|
20
|
+
* `/prd-check` slash command call it. The function returns the
|
|
21
|
+
* `PrdCheckEnvelope[]` so the REPL can render via Ink without
|
|
22
|
+
* re-running the verification.
|
|
23
|
+
*
|
|
24
|
+
* - Exit codes follow `exitCodeFor` from the reporter:
|
|
25
|
+
* 0 — healthy (every criterion PASS or SKIPPED)
|
|
26
|
+
* 1 — failing (≥1 FAIL across the scanned PRDs)
|
|
27
|
+
* 2 — unparsed (≥1 PRD missing the acceptance section)
|
|
28
|
+
* When `--all` scans multiple PRDs the worst verdict wins (1 > 2 > 0).
|
|
29
|
+
*
|
|
30
|
+
* - The `knownCommands` set is sourced from the CLI registry — we
|
|
31
|
+
* accept it as an injected parameter so the spec can drive
|
|
32
|
+
* command-verification without importing the entire cli.ts
|
|
33
|
+
* module (which would pull the engine graph into the test).
|
|
34
|
+
*/
|
|
35
|
+
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
36
|
+
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
37
|
+
import { parsePrd } from '../../core/prd-check/parser.js';
|
|
38
|
+
import { buildEnvelope, exitCodeFor, renderTable, } from '../../core/prd-check/reporter.js';
|
|
39
|
+
import { defaultSessionReviewDeps, renderSessionReview, runSessionReview, } from '../../core/prd-check/session-review.js';
|
|
40
|
+
import { createDefaultDeps, verifyAll, } from '../../core/prd-check/verifiers.js';
|
|
41
|
+
const DEFAULT_PRD_DIR = 'docs/prd';
|
|
42
|
+
/**
|
|
43
|
+
* Run the gate. Resolves which PRDs to inspect, runs the parser +
|
|
44
|
+
* verifiers + reporter chain per PRD, emits the combined output,
|
|
45
|
+
* and sets `process.exitCode` to the worst verdict across the set.
|
|
46
|
+
*/
|
|
47
|
+
export async function runPrdCheckCommand(ctx) {
|
|
48
|
+
// final : session-review mode. Orthogonal to
|
|
49
|
+
// the verifier-graph mode — no PRD path resolution, no command
|
|
50
|
+
// registry. The runner walks up for PRD.md, reads the NDJSON
|
|
51
|
+
// events log, and dispatches a cross-review subagent.
|
|
52
|
+
if (ctx.flags.session) {
|
|
53
|
+
const status = (line) => {
|
|
54
|
+
// Emit status lines through the output sink so the slash
|
|
55
|
+
// surface can render them inline. The status payload is
|
|
56
|
+
// intentionally lightweight — only the human-facing text.
|
|
57
|
+
ctx.writeOutput({
|
|
58
|
+
command: 'prd-check',
|
|
59
|
+
overall: 'healthy',
|
|
60
|
+
envelopes: [],
|
|
61
|
+
}, line);
|
|
62
|
+
};
|
|
63
|
+
const sessionDeps = ctx.sessionDeps ??
|
|
64
|
+
defaultSessionReviewDeps({ onStatus: status });
|
|
65
|
+
const review = await runSessionReview(ctx.cwd, sessionDeps);
|
|
66
|
+
const overall = review.status === 'ok'
|
|
67
|
+
? review.outstanding.length === 0
|
|
68
|
+
? 'healthy'
|
|
69
|
+
: 'failing'
|
|
70
|
+
: 'unparsed';
|
|
71
|
+
const result = {
|
|
72
|
+
command: 'prd-check',
|
|
73
|
+
overall,
|
|
74
|
+
envelopes: [],
|
|
75
|
+
sessionReview: review,
|
|
76
|
+
};
|
|
77
|
+
ctx.writeOutput(result, renderSessionReview(review));
|
|
78
|
+
process.exitCode = exitCodeFor(overall);
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
const paths = resolveTargets(ctx);
|
|
82
|
+
if (paths.length === 0) {
|
|
83
|
+
const result = {
|
|
84
|
+
command: 'prd-check',
|
|
85
|
+
overall: 'unparsed',
|
|
86
|
+
envelopes: [],
|
|
87
|
+
};
|
|
88
|
+
ctx.writeOutput(result, 'No PRD files found.');
|
|
89
|
+
process.exitCode = exitCodeFor('unparsed');
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
const deps = ctx.deps ??
|
|
93
|
+
createDefaultDeps({
|
|
94
|
+
workspaceRoot: ctx.cwd,
|
|
95
|
+
knownCommands: ctx.knownCommands,
|
|
96
|
+
});
|
|
97
|
+
const envelopes = [];
|
|
98
|
+
for (const path of paths) {
|
|
99
|
+
envelopes.push(checkSinglePrd(path, ctx.cwd, deps));
|
|
100
|
+
}
|
|
101
|
+
const overall = combineOverall(envelopes.map((e) => e.overall));
|
|
102
|
+
const result = {
|
|
103
|
+
command: 'prd-check',
|
|
104
|
+
overall,
|
|
105
|
+
envelopes,
|
|
106
|
+
};
|
|
107
|
+
const text = renderRun(result, ctx.cwd);
|
|
108
|
+
ctx.writeOutput(result, text);
|
|
109
|
+
process.exitCode = exitCodeFor(overall);
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Render the combined plain-text view. Multi-PRD runs print one
|
|
114
|
+
* table per envelope separated by a divider; single-PRD runs print
|
|
115
|
+
* just the table to keep the output narrow.
|
|
116
|
+
*/
|
|
117
|
+
function renderRun(result, cwd) {
|
|
118
|
+
if (result.envelopes.length === 0) {
|
|
119
|
+
return 'No PRD files found.';
|
|
120
|
+
}
|
|
121
|
+
if (result.envelopes.length === 1) {
|
|
122
|
+
return renderTable(result.envelopes[0]);
|
|
123
|
+
}
|
|
124
|
+
const parts = [];
|
|
125
|
+
for (const envelope of result.envelopes) {
|
|
126
|
+
const relPath = relative(cwd, envelope.prdPath) || envelope.prdPath;
|
|
127
|
+
parts.push(renderTable({ ...envelope, prdPath: relPath }));
|
|
128
|
+
parts.push('');
|
|
129
|
+
}
|
|
130
|
+
parts.push('-'.repeat(50));
|
|
131
|
+
const summary = `${result.envelopes.length} PRD(s) scanned. Overall: ${result.overall.toUpperCase()}`;
|
|
132
|
+
parts.push(summary);
|
|
133
|
+
return parts.join('\n');
|
|
134
|
+
}
|
|
135
|
+
function checkSinglePrd(prdPath, workspaceRoot, deps) {
|
|
136
|
+
let source;
|
|
137
|
+
try {
|
|
138
|
+
source = readFileSync(prdPath, 'utf8');
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
142
|
+
return {
|
|
143
|
+
command: 'prd-check',
|
|
144
|
+
prdPath: relative(workspaceRoot, prdPath) || prdPath,
|
|
145
|
+
title: null,
|
|
146
|
+
overall: 'unparsed',
|
|
147
|
+
counts: { pass: 0, fail: 0, skipped: 0 },
|
|
148
|
+
criteria: [
|
|
149
|
+
{
|
|
150
|
+
index: 0,
|
|
151
|
+
text: `PRD file unreadable: ${message}`,
|
|
152
|
+
status: 'fail',
|
|
153
|
+
results: [],
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const parsed = parsePrd(source);
|
|
159
|
+
const verified = verifyAll(parsed.criteria, deps);
|
|
160
|
+
return buildEnvelope({
|
|
161
|
+
prdPath: relative(workspaceRoot, prdPath) || prdPath,
|
|
162
|
+
title: parsed.title,
|
|
163
|
+
hasAcceptanceSection: parsed.hasAcceptanceSection,
|
|
164
|
+
verified,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function resolveTargets(ctx) {
|
|
168
|
+
if (ctx.flags.all) {
|
|
169
|
+
const prdDir = resolve(ctx.cwd, DEFAULT_PRD_DIR);
|
|
170
|
+
return listMarkdownFiles(prdDir);
|
|
171
|
+
}
|
|
172
|
+
if (ctx.prdPath) {
|
|
173
|
+
const absolute = isAbsolute(ctx.prdPath)
|
|
174
|
+
? ctx.prdPath
|
|
175
|
+
: resolve(ctx.cwd, ctx.prdPath);
|
|
176
|
+
return [absolute];
|
|
177
|
+
}
|
|
178
|
+
return [];
|
|
179
|
+
}
|
|
180
|
+
function listMarkdownFiles(dir) {
|
|
181
|
+
let entries;
|
|
182
|
+
try {
|
|
183
|
+
entries = readdirSync(dir);
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return [];
|
|
187
|
+
}
|
|
188
|
+
const out = [];
|
|
189
|
+
for (const entry of entries) {
|
|
190
|
+
const full = join(dir, entry);
|
|
191
|
+
let isDirectory = false;
|
|
192
|
+
try {
|
|
193
|
+
isDirectory = statSync(full).isDirectory();
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (isDirectory) {
|
|
199
|
+
out.push(...listMarkdownFiles(full));
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (entry.endsWith('.md')) {
|
|
203
|
+
out.push(full);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return out.sort();
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Combine per-PRD verdicts into the run-wide one. failing > unparsed > healthy.
|
|
210
|
+
* Exported for the spec.
|
|
211
|
+
*/
|
|
212
|
+
export function combineOverall(verdicts) {
|
|
213
|
+
if (verdicts.length === 0)
|
|
214
|
+
return 'unparsed';
|
|
215
|
+
if (verdicts.some((v) => v === 'failing'))
|
|
216
|
+
return 'failing';
|
|
217
|
+
if (verdicts.some((v) => v === 'unparsed'))
|
|
218
|
+
return 'unparsed';
|
|
219
|
+
return 'healthy';
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Parse the CLI argv tail. Accepts:
|
|
223
|
+
*
|
|
224
|
+
* pugi prd-check -> error (no target)
|
|
225
|
+
* pugi prd-check <path> -> single PRD
|
|
226
|
+
* pugi prd-check --all -> scan docs/prd/**.md
|
|
227
|
+
* pugi prd-check <path> --json -> single PRD, JSON envelope
|
|
228
|
+
*
|
|
229
|
+
* `--json` is also forwarded from the global flag set in cli.ts;
|
|
230
|
+
* the local parse re-honours it so the slash command can use the
|
|
231
|
+
* same parser without the global flag plumbing.
|
|
232
|
+
*/
|
|
233
|
+
export function parsePrdCheckArgs(args, options) {
|
|
234
|
+
let json = options.jsonDefault;
|
|
235
|
+
let all = false;
|
|
236
|
+
let session = false;
|
|
237
|
+
let prdPath;
|
|
238
|
+
for (const arg of args) {
|
|
239
|
+
if (arg === '--json') {
|
|
240
|
+
json = true;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (arg === '--all') {
|
|
244
|
+
all = true;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (arg === '--session') {
|
|
248
|
+
session = true;
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (arg.startsWith('--')) {
|
|
252
|
+
return { ok: false, error: `unknown flag: ${arg}` };
|
|
253
|
+
}
|
|
254
|
+
if (prdPath !== undefined) {
|
|
255
|
+
return { ok: false, error: `unexpected extra argument: ${arg}` };
|
|
256
|
+
}
|
|
257
|
+
prdPath = arg;
|
|
258
|
+
}
|
|
259
|
+
// final : `--session` is mutually exclusive with
|
|
260
|
+
// the verifier modes — it walks up for a PRD, reads NDJSON turns,
|
|
261
|
+
// and dispatches a subagent. No <path>, no --all, no command-
|
|
262
|
+
// registry inputs. Validating up-front gives operators a clear
|
|
263
|
+
// error instead of a silent fall-through.
|
|
264
|
+
if (session && (all || prdPath !== undefined)) {
|
|
265
|
+
return {
|
|
266
|
+
ok: false,
|
|
267
|
+
error: 'cannot combine --session with --all or <path>',
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
if (!session && !all && prdPath === undefined) {
|
|
271
|
+
return {
|
|
272
|
+
ok: false,
|
|
273
|
+
error: 'pugi prd-check <prd-path> | --all | --session (pass a PRD path, --all to scan docs/prd/**.md, or --session to review the live session against PRD.md)',
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
if (all && prdPath !== undefined) {
|
|
277
|
+
return { ok: false, error: 'cannot combine <path> with --all' };
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
ok: true,
|
|
281
|
+
flags: { json, all, session },
|
|
282
|
+
...(prdPath !== undefined ? { prdPath } : {}),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
//# sourceMappingURL=prd-check.js.map
|
|
@@ -7,22 +7,22 @@ import { dirname, resolve } from 'node:path';
|
|
|
7
7
|
* `pugi privacy` — read or update the operator's privacy mode.
|
|
8
8
|
*
|
|
9
9
|
* Subcommands:
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* - `pugi privacy show` — print current mode + source
|
|
11
|
+
* - `pugi privacy set <mode>` — write `local-only | metadata | full`
|
|
12
12
|
*
|
|
13
13
|
* Persistence:
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
14
|
+
* - Stored in `~/.pugi/privacy.json` (PUGI_HOME-aware).
|
|
15
|
+
* - The `privacy` key in `~/.pugi/config.json` is also consulted at
|
|
16
|
+
* read time (config takes precedence when both exist) so a user who
|
|
17
|
+
* already set `pugi config set privacy ...` sees the same value
|
|
18
|
+
* here.
|
|
19
19
|
*
|
|
20
20
|
* Modes:
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
21
|
+
* - `local-only` — nothing leaves the workstation. Engine commands
|
|
22
|
+
* refuse to ship transcripts; sync is a no-op.
|
|
23
|
+
* - `metadata` — only artifact metadata + session timeline ships
|
|
24
|
+
* (no raw file contents).
|
|
25
|
+
* - `full` — full sync allowed (operator-acknowledged).
|
|
26
26
|
*/
|
|
27
27
|
export const privacyModeSchema = z.enum(['local-only', 'metadata', 'full']);
|
|
28
28
|
const privacyFileSchema = z.object({
|
|
@@ -53,9 +53,9 @@ function writePrivacyFile(mode) {
|
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
55
|
* Effective privacy mode resolution order:
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
56
|
+
* 1. `~/.pugi/config.json` privacy key (when set via `pugi config set`)
|
|
57
|
+
* 2. `~/.pugi/privacy.json` (when set via `pugi privacy set`)
|
|
58
|
+
* 3. `metadata` (default — matches the M1 default-ship posture)
|
|
59
59
|
*/
|
|
60
60
|
export function resolvePrivacyMode() {
|
|
61
61
|
const config = readConfig();
|
|
@@ -75,8 +75,8 @@ export async function runPrivacyCommand(args, ctx) {
|
|
|
75
75
|
modes: ['local-only', 'metadata', 'full'],
|
|
76
76
|
}, [
|
|
77
77
|
'Usage:',
|
|
78
|
-
'
|
|
79
|
-
'
|
|
78
|
+
' pugi privacy show Show current privacy mode.',
|
|
79
|
+
' pugi privacy set <mode> Set mode to local-only, metadata, or full.',
|
|
80
80
|
].join('\n'));
|
|
81
81
|
return;
|
|
82
82
|
}
|