@pugi/cli 0.1.0-beta.9 → 0.1.0-beta.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +132 -0
- package/LICENSE +1 -1
- 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 +3 -3
- 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 +13 -13
- 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 +333 -7
- package/dist/core/edits/format-detector.js +260 -0
- package/dist/core/edits/format-matrix.js +26 -0
- package/dist/core/edits/fuzzy-ladder.js +650 -0
- package/dist/core/edits/index.js +5 -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 +29 -29
- package/dist/core/engine/anvil-client.js +214 -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 +129 -19
- package/dist/core/engine/strip-internal-fields.js +124 -0
- package/dist/core/engine/tool-bridge.js +1792 -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 +46 -0
- package/dist/core/hooks/index.js +15 -0
- package/dist/core/hooks/registry.js +216 -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/hooks/worktree-events.js +158 -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 +551 -41
- package/dist/core/lsp/language-detect.js +66 -0
- package/dist/core/lsp/post-edit-diagnostics.js +171 -0
- package/dist/core/lsp/server-detect.js +173 -0
- package/dist/core/lsp/symbol-cache.js +162 -0
- package/dist/core/lsp/symbol-tools.js +664 -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 +2148 -217
- package/dist/core/repl/slash-commands.js +501 -41
- 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 +324 -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 +30 -30
- 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/include-parser.js +249 -0
- package/dist/core/worktree-manager/cleanup.js +123 -0
- package/dist/core/worktree-manager/manager.js +303 -0
- package/dist/index.js +36 -0
- package/dist/runtime/bootstrap.js +190 -0
- package/dist/runtime/cli.js +4185 -549
- package/dist/runtime/commands/agents.js +31 -31
- 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 +73 -39
- package/dist/runtime/commands/cost.js +199 -0
- package/dist/runtime/commands/delegate.js +27 -4
- 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 +187 -0
- package/dist/runtime/commands/init.js +254 -0
- package/dist/runtime/commands/lsp.js +200 -38
- 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 +12 -12
- 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 +8 -8
- 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 +22 -22
- package/dist/runtime/sigint-guard.js +272 -0
- package/dist/runtime/update-check.js +28 -28
- package/dist/runtime/version.js +65 -0
- package/dist/runtime/worktree-bootstrap.js +579 -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 +89 -28
- package/dist/tools/ask-user-question.js +337 -0
- package/dist/tools/ask-user.js +115 -0
- package/dist/tools/bash.js +624 -46
- package/dist/tools/brief.js +224 -0
- package/dist/tools/cron.js +433 -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 +377 -1
- 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 +99 -4
- 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-chips.js +315 -0
- 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/multi-file-diff-approval.js +375 -0
- package/dist/tui/onboarding-wizard.js +240 -0
- package/dist/tui/permissions-picker.js +86 -0
- package/dist/tui/render.js +36 -1
- package/dist/tui/repl-render.js +176 -25
- 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 +125 -45
- 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/package.json +31 -16
- package/test/scenarios/codegen-create-file.scenario.txt +13 -0
- package/test/scenarios/compact-force.scenario.txt +12 -0
- package/test/scenarios/identity.scenario.txt +12 -0
- package/test/scenarios/persona-handoff.scenario.txt +12 -0
- package/test/scenarios/walkback.scenario.txt +12 -0
- package/dist/core/engine/compaction-hook.js +0 -154
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* — `pugi update` dispatcher.
|
|
3
|
+
*
|
|
4
|
+
* Top-level command + in-REPL `/update` slash share this handler.
|
|
5
|
+
* Both surfaces delegate IO + persistence here so the channel
|
|
6
|
+
* resolution, the registry probe, and the install shell-out stay
|
|
7
|
+
* single-sourced.
|
|
8
|
+
*
|
|
9
|
+
* Command grammar:
|
|
10
|
+
*
|
|
11
|
+
* pugi update -> probe + interactive prompt
|
|
12
|
+
* pugi update --check -> probe + JSON envelope (scripted)
|
|
13
|
+
* pugi update --channel <name> -> switch channel + probe
|
|
14
|
+
* pugi update --apply -> probe + shell `npm i -g …`
|
|
15
|
+
* (confirms unless --yes)
|
|
16
|
+
* pugi update --apply --yes -> probe + shell, no confirmation
|
|
17
|
+
*
|
|
18
|
+
* Exit codes:
|
|
19
|
+
*
|
|
20
|
+
* 0 — happy path (no update OR update completed) OR `--check` JSON
|
|
21
|
+
* 1 — install / probe failure with structured error envelope
|
|
22
|
+
* 2 — argument error (bad channel, conflicting flags)
|
|
23
|
+
*
|
|
24
|
+
* R2 atomic swap deferred (sprint plan L27 mention): the upstream behavior
|
|
25
|
+
* also describes the upstream tool's R2-backed binary swap. Pugi ships
|
|
26
|
+
* exclusively via npm today; building a parallel R2 distribution
|
|
27
|
+
* channel + checksum verification + rollback is materially more work
|
|
28
|
+
* than the L27 acceptance criteria allow. Document the deferral in
|
|
29
|
+
* the PR body and revisit when npm's once-per-day rate-limit or
|
|
30
|
+
* outage cadence justifies the parallel pipeline.
|
|
31
|
+
*/
|
|
32
|
+
import { spawn } from 'node:child_process';
|
|
33
|
+
import { homedir } from 'node:os';
|
|
34
|
+
import { DEFAULT_UPDATE_CHANNEL, UPDATE_CHANNELS, describeChannel, npmTagForChannel, parseUpdateChannel, } from '../../core/auto-update/channels.js';
|
|
35
|
+
import { checkForChannelUpdate, } from '../../core/auto-update/checker.js';
|
|
36
|
+
import { resolveEffectiveChannel, setUpdateChannel, writeLastCheckedAt, } from '../../core/auto-update/state.js';
|
|
37
|
+
import { PUGI_CLI_VERSION } from '../version.js';
|
|
38
|
+
/**
|
|
39
|
+
* Default subprocess runner. Spawns `npm install -g @pugi/cli@<tag>`
|
|
40
|
+
* inheriting stdio so the operator sees the live npm output.
|
|
41
|
+
*/
|
|
42
|
+
export function defaultSpawnInstaller(channel) {
|
|
43
|
+
const tag = npmTagForChannel(channel);
|
|
44
|
+
const args = ['install', '-g', `@pugi/cli@${tag}`];
|
|
45
|
+
return new Promise((resolvePromise) => {
|
|
46
|
+
const child = spawn('npm', args, { stdio: 'inherit' });
|
|
47
|
+
child.on('exit', (code) => {
|
|
48
|
+
resolvePromise(typeof code === 'number' ? code : 1);
|
|
49
|
+
});
|
|
50
|
+
child.on('error', () => {
|
|
51
|
+
// ENOENT / EACCES — npm not on PATH or permission denied. We
|
|
52
|
+
// surface a non-zero code so the dispatcher's JSON envelope
|
|
53
|
+
// tells the operator the apply failed; the inherited stdio
|
|
54
|
+
// already printed the underlying error to the terminal.
|
|
55
|
+
resolvePromise(127);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Parse a CLI / slash argv into our `UpdateCommandFlags`. Returns
|
|
61
|
+
* `null` AND writes the usage error via `writeError` for conflicting
|
|
62
|
+
* combinations. Both `pugi update` and the in-REPL `/update <args>`
|
|
63
|
+
* surface call through this so the validation lives in one place.
|
|
64
|
+
*/
|
|
65
|
+
export function parseUpdateArgs(argv, options = {}) {
|
|
66
|
+
let check = false;
|
|
67
|
+
let apply = false;
|
|
68
|
+
let yes = false;
|
|
69
|
+
let json = options.jsonDefault ?? false;
|
|
70
|
+
let channel = null;
|
|
71
|
+
let channelInvalid;
|
|
72
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
73
|
+
const token = argv[i] ?? '';
|
|
74
|
+
if (token === '--check') {
|
|
75
|
+
check = true;
|
|
76
|
+
}
|
|
77
|
+
else if (token === '--apply') {
|
|
78
|
+
apply = true;
|
|
79
|
+
}
|
|
80
|
+
else if (token === '--yes' || token === '-y') {
|
|
81
|
+
yes = true;
|
|
82
|
+
}
|
|
83
|
+
else if (token === '--json') {
|
|
84
|
+
json = true;
|
|
85
|
+
}
|
|
86
|
+
else if (token === '--channel') {
|
|
87
|
+
const value = argv[i + 1];
|
|
88
|
+
i += 1;
|
|
89
|
+
const parsed = parseUpdateChannel(value);
|
|
90
|
+
if (!parsed) {
|
|
91
|
+
channelInvalid = value ?? '';
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
channel = parsed;
|
|
95
|
+
}
|
|
96
|
+
else if (token.startsWith('--channel=')) {
|
|
97
|
+
const value = token.slice('--channel='.length);
|
|
98
|
+
const parsed = parseUpdateChannel(value);
|
|
99
|
+
if (!parsed) {
|
|
100
|
+
channelInvalid = value;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
channel = parsed;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
return {
|
|
107
|
+
error: `pugi update: unknown argument '${token}'. See \`pugi update --help\`.`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (channelInvalid !== undefined) {
|
|
112
|
+
return {
|
|
113
|
+
error: `pugi update: unknown channel '${channelInvalid}'. Allowed: ${UPDATE_CHANNELS.join(' / ')}.`,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
check,
|
|
118
|
+
apply,
|
|
119
|
+
yes,
|
|
120
|
+
json,
|
|
121
|
+
channel,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Run the full command. Returns the structured envelope so the in-
|
|
126
|
+
* REPL slash can decide whether to render the human-readable text OR
|
|
127
|
+
* pretty-print the JSON.
|
|
128
|
+
*/
|
|
129
|
+
export async function runUpdateCommand(ctx) {
|
|
130
|
+
const flags = ctx.flags;
|
|
131
|
+
const home = ctx.home;
|
|
132
|
+
// 1. Resolve channel. `--channel <name>` wins; otherwise read the
|
|
133
|
+
// persisted preference; otherwise hard default `beta`.
|
|
134
|
+
const cliFlagChannel = flags.channel;
|
|
135
|
+
const effectiveChannel = resolveEffectiveChannel({
|
|
136
|
+
cliFlag: cliFlagChannel,
|
|
137
|
+
homeDir: home,
|
|
138
|
+
});
|
|
139
|
+
// 2. Persist the channel switch BEFORE the probe so a probe failure
|
|
140
|
+
// still leaves the operator on the channel they asked for.
|
|
141
|
+
let switched = false;
|
|
142
|
+
if (cliFlagChannel) {
|
|
143
|
+
setUpdateChannel(cliFlagChannel, home);
|
|
144
|
+
switched = true;
|
|
145
|
+
}
|
|
146
|
+
// 3. Probe the registry.
|
|
147
|
+
const outcome = await checkForChannelUpdate({
|
|
148
|
+
channel: effectiveChannel,
|
|
149
|
+
currentVersion: PUGI_CLI_VERSION,
|
|
150
|
+
...(ctx.fetchImpl ? { fetchImpl: ctx.fetchImpl } : {}),
|
|
151
|
+
...(ctx.registryUrl ? { registryUrl: ctx.registryUrl } : {}),
|
|
152
|
+
});
|
|
153
|
+
// 4. Record the timestamp on a successful probe (regardless of
|
|
154
|
+
// whether an update is available). Failed probes do NOT update
|
|
155
|
+
// the timestamp so the cold-start banner retries on the next
|
|
156
|
+
// invocation.
|
|
157
|
+
if (!outcome.error) {
|
|
158
|
+
const now = ctx.now ? new Date(ctx.now()) : new Date();
|
|
159
|
+
try {
|
|
160
|
+
writeLastCheckedAt(now, home);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// Best-effort — a read-only home should not crash the dispatcher.
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// 5. Build the envelope shape ALL action paths share.
|
|
167
|
+
const baseEnvelope = {
|
|
168
|
+
command: 'update',
|
|
169
|
+
ok: outcome.error === null,
|
|
170
|
+
available: outcome.available,
|
|
171
|
+
channel: outcome.channel,
|
|
172
|
+
npmTag: outcome.npmTag,
|
|
173
|
+
current: outcome.current,
|
|
174
|
+
latest: outcome.latest,
|
|
175
|
+
gap: outcome.gap,
|
|
176
|
+
installCommand: outcome.installCommand,
|
|
177
|
+
action: outcome.error ? 'error' : 'probe',
|
|
178
|
+
error: outcome.error,
|
|
179
|
+
meta: { cliVersion: PUGI_CLI_VERSION },
|
|
180
|
+
};
|
|
181
|
+
// 6. --check: emit the envelope, never prompt, never apply.
|
|
182
|
+
if (flags.check) {
|
|
183
|
+
const text = renderHumanText(baseEnvelope, { switched });
|
|
184
|
+
ctx.writeOutput(baseEnvelope, text);
|
|
185
|
+
return baseEnvelope;
|
|
186
|
+
}
|
|
187
|
+
// 7. No update available — bail with a friendly message.
|
|
188
|
+
if (!outcome.available || !outcome.latest) {
|
|
189
|
+
const envelope = {
|
|
190
|
+
...baseEnvelope,
|
|
191
|
+
action: outcome.error ? 'error' : (switched ? 'switch' : 'no_update'),
|
|
192
|
+
};
|
|
193
|
+
const text = renderHumanText(envelope, { switched });
|
|
194
|
+
ctx.writeOutput(envelope, text);
|
|
195
|
+
return envelope;
|
|
196
|
+
}
|
|
197
|
+
// 8. Update IS available. Branch on --apply.
|
|
198
|
+
if (!flags.apply) {
|
|
199
|
+
// Default: print the offer, suggest the install command, leave
|
|
200
|
+
// the install to the operator. Mirrors the upstream behavior note:
|
|
201
|
+
// operators install side-effects must remain explicit on the CLI
|
|
202
|
+
// happy path — `pugi update` showing the gap is informational,
|
|
203
|
+
// `pugi update --apply` is the destructive verb.
|
|
204
|
+
const envelope = {
|
|
205
|
+
...baseEnvelope,
|
|
206
|
+
action: switched ? 'switch' : 'probe',
|
|
207
|
+
};
|
|
208
|
+
const text = renderHumanText(envelope, { switched });
|
|
209
|
+
ctx.writeOutput(envelope, text);
|
|
210
|
+
return envelope;
|
|
211
|
+
}
|
|
212
|
+
// 9. --apply path. Confirm unless --yes, then spawn npm.
|
|
213
|
+
const installer = ctx.spawnInstaller ?? defaultSpawnInstaller;
|
|
214
|
+
let confirmed = flags.yes;
|
|
215
|
+
if (!confirmed) {
|
|
216
|
+
confirmed = await ctx.promptConfirm(`Run \`${outcome.installCommand}\` to update ${outcome.current} -> ${outcome.latest}? [y/N]`);
|
|
217
|
+
}
|
|
218
|
+
if (!confirmed) {
|
|
219
|
+
const envelope = {
|
|
220
|
+
...baseEnvelope,
|
|
221
|
+
action: 'probe',
|
|
222
|
+
ok: false,
|
|
223
|
+
error: 'apply_cancelled_by_operator',
|
|
224
|
+
};
|
|
225
|
+
const text = `Cancelled. Run \`${outcome.installCommand}\` manually when you are ready.`;
|
|
226
|
+
ctx.writeOutput(envelope, text);
|
|
227
|
+
return envelope;
|
|
228
|
+
}
|
|
229
|
+
const exitCode = await installer(outcome.channel);
|
|
230
|
+
const applyEnvelope = {
|
|
231
|
+
...baseEnvelope,
|
|
232
|
+
action: 'apply',
|
|
233
|
+
installExitCode: exitCode,
|
|
234
|
+
ok: exitCode === 0,
|
|
235
|
+
error: exitCode === 0 ? null : `npm_install_exit_${exitCode}`,
|
|
236
|
+
};
|
|
237
|
+
const applyText = exitCode === 0
|
|
238
|
+
? `Updated to @pugi/cli@${outcome.latest}. Restart your shell so the new binary takes effect.`
|
|
239
|
+
: `Update failed (npm exit ${exitCode}). Try \`${outcome.installCommand}\` manually.`;
|
|
240
|
+
ctx.writeOutput(applyEnvelope, applyText);
|
|
241
|
+
return applyEnvelope;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Build the operator-readable hint that lives next to the JSON
|
|
245
|
+
* envelope. The text + the JSON are both passed to `writeOutput`; the
|
|
246
|
+
* caller (`writeOutput` in cli.ts) picks based on `--json`. Exported
|
|
247
|
+
* so the spec can assert the literal strings the operator sees.
|
|
248
|
+
*/
|
|
249
|
+
export function renderHumanText(envelope, options = {}) {
|
|
250
|
+
const { current, latest, channel, installCommand, available, error } = envelope;
|
|
251
|
+
const lines = [];
|
|
252
|
+
lines.push(`Channel: ${channel} — ${describeChannel(channel)}`);
|
|
253
|
+
if (options.switched) {
|
|
254
|
+
lines.push(`Persisted channel selection -> ${channel}.`);
|
|
255
|
+
}
|
|
256
|
+
if (error) {
|
|
257
|
+
lines.push(`Update check failed: ${error}`);
|
|
258
|
+
lines.push(`Manual: \`${installCommand}\` (no probe necessary).`);
|
|
259
|
+
return lines.join('\n');
|
|
260
|
+
}
|
|
261
|
+
if (available && latest) {
|
|
262
|
+
lines.push(`Update available: ${current} -> ${latest}.`);
|
|
263
|
+
lines.push(`Run \`${installCommand}\` to upgrade.`);
|
|
264
|
+
lines.push(`Or \`pugi update --apply\` to upgrade with confirmation.`);
|
|
265
|
+
return lines.join('\n');
|
|
266
|
+
}
|
|
267
|
+
lines.push(`Up to date (${current} is the latest on ${channel}).`);
|
|
268
|
+
return lines.join('\n');
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Render a one-line cold-start hint that callers (REPL boot, doctor
|
|
272
|
+
* banner) splice above their own UI. Returns `null` when there is
|
|
273
|
+
* nothing to nudge the operator about. Pure — no IO.
|
|
274
|
+
*/
|
|
275
|
+
export function renderUpdateHint(outcome) {
|
|
276
|
+
if (!outcome.available || !outcome.latest)
|
|
277
|
+
return null;
|
|
278
|
+
return `Update available: ${outcome.current} -> ${outcome.latest}. Run \`pugi update\`.`;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Convenience entry point — resolve the effective channel from
|
|
282
|
+
* `~/.pugi/config.json` + DEFAULT_UPDATE_CHANNEL without forcing the
|
|
283
|
+
* caller to import multiple modules. Used by the cold-start banner +
|
|
284
|
+
* the doctor probe.
|
|
285
|
+
*/
|
|
286
|
+
export function effectiveChannel(home = homedir()) {
|
|
287
|
+
return resolveEffectiveChannel({ homeDir: home }) ?? DEFAULT_UPDATE_CHANNEL;
|
|
288
|
+
}
|
|
289
|
+
//# sourceMappingURL=update.js.map
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* — `pugi vim` top-level command + REPL slash
|
|
3
|
+
* companion.
|
|
4
|
+
*
|
|
5
|
+
* Operator surface:
|
|
6
|
+
*
|
|
7
|
+
* pugi vim Show current vim-mode state.
|
|
8
|
+
* pugi vim on Enable + persist (~/.pugi/config.json).
|
|
9
|
+
* pugi vim off Disable + persist (default).
|
|
10
|
+
* /vim Toggle from inside the REPL.
|
|
11
|
+
* /vim on Enable from inside the REPL.
|
|
12
|
+
* /vim off Disable from inside the REPL.
|
|
13
|
+
*
|
|
14
|
+
* The same runner backs both surfaces so the slash + the shell verb
|
|
15
|
+
* stay single-sourced — operators trained on one read the same
|
|
16
|
+
* payload + banner on the other.
|
|
17
|
+
*
|
|
18
|
+
* Exit codes:
|
|
19
|
+
* 0 — show / enable / disable / toggle happy path
|
|
20
|
+
* 2 — unknown subcommand (e.g. `pugi vim chaos`)
|
|
21
|
+
*
|
|
22
|
+
* NOTE: there are NO `--persist` flag and no workspace tier. Vim mode
|
|
23
|
+
* is a single user-level preference, matching the upstream behavior note
|
|
24
|
+
* that operators expect modal editing to be a body-memory trait, not
|
|
25
|
+
* a per-repo concern (see `core/vim/state.ts` header).
|
|
26
|
+
*/
|
|
27
|
+
import { resolveVimMode, setVimMode } from '../../core/vim/state.js';
|
|
28
|
+
/**
|
|
29
|
+
* Banner shown on enable so the operator can see the binding cheat
|
|
30
|
+
* sheet without reaching for `/help`. Echoed by both surfaces.
|
|
31
|
+
*/
|
|
32
|
+
export const VIM_ENABLED_BANNER = 'Vim mode on. Esc=normal, i=insert, :w=submit, :q=cancel.';
|
|
33
|
+
/**
|
|
34
|
+
* Entry point. Parses `args`, flips persistence as needed, emits the
|
|
35
|
+
* payload + text via `ctx.writeOutput`, and returns the exit code.
|
|
36
|
+
*/
|
|
37
|
+
export async function runVimCommand(args, ctx) {
|
|
38
|
+
// Validate args before reading state so a bad call never has a
|
|
39
|
+
// side-effect.
|
|
40
|
+
if (args.length > 1) {
|
|
41
|
+
const payload = {
|
|
42
|
+
command: 'vim',
|
|
43
|
+
status: 'invalid_args',
|
|
44
|
+
active: resolveVimMode({ env: ctx.env }),
|
|
45
|
+
message: `pugi vim takes at most one argument (on / off / toggle). Got: ${args.join(' ')}`,
|
|
46
|
+
attemptedArg: args.join(' '),
|
|
47
|
+
};
|
|
48
|
+
ctx.writeOutput(payload, payload.message);
|
|
49
|
+
return 2;
|
|
50
|
+
}
|
|
51
|
+
const current = resolveVimMode({ env: ctx.env });
|
|
52
|
+
const verb = args[0]?.toLowerCase() ?? '';
|
|
53
|
+
// No args → toggle (slash convention from the upstream behavior ). The
|
|
54
|
+
// CLI shell surface ALSO toggles on bare invocation so the two
|
|
55
|
+
// surfaces converge; if the operator wants the read-only show they
|
|
56
|
+
// can pipe through `pugi vim --json` or query the config directly.
|
|
57
|
+
// We surface the change as a `show` status when nothing flipped so
|
|
58
|
+
// scripts can distinguish read from write.
|
|
59
|
+
if (verb === '') {
|
|
60
|
+
const next = !current;
|
|
61
|
+
setVimMode(next, { env: ctx.env });
|
|
62
|
+
const status = next ? 'enabled' : 'disabled';
|
|
63
|
+
const message = next ? VIM_ENABLED_BANNER : 'Vim mode off.';
|
|
64
|
+
const payload = {
|
|
65
|
+
command: 'vim',
|
|
66
|
+
status,
|
|
67
|
+
active: next,
|
|
68
|
+
previous: current,
|
|
69
|
+
message,
|
|
70
|
+
};
|
|
71
|
+
ctx.writeOutput(payload, payload.message);
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
if (verb === 'on' || verb === 'enable' || verb === 'true') {
|
|
75
|
+
if (current) {
|
|
76
|
+
const payload = {
|
|
77
|
+
command: 'vim',
|
|
78
|
+
status: 'unchanged',
|
|
79
|
+
active: true,
|
|
80
|
+
previous: true,
|
|
81
|
+
message: 'Vim mode already on.',
|
|
82
|
+
};
|
|
83
|
+
ctx.writeOutput(payload, payload.message);
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
setVimMode(true, { env: ctx.env });
|
|
87
|
+
const payload = {
|
|
88
|
+
command: 'vim',
|
|
89
|
+
status: 'enabled',
|
|
90
|
+
active: true,
|
|
91
|
+
previous: current,
|
|
92
|
+
message: VIM_ENABLED_BANNER,
|
|
93
|
+
};
|
|
94
|
+
ctx.writeOutput(payload, payload.message);
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
if (verb === 'off' || verb === 'disable' || verb === 'false') {
|
|
98
|
+
if (!current) {
|
|
99
|
+
const payload = {
|
|
100
|
+
command: 'vim',
|
|
101
|
+
status: 'unchanged',
|
|
102
|
+
active: false,
|
|
103
|
+
previous: false,
|
|
104
|
+
message: 'Vim mode already off.',
|
|
105
|
+
};
|
|
106
|
+
ctx.writeOutput(payload, payload.message);
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
setVimMode(false, { env: ctx.env });
|
|
110
|
+
const payload = {
|
|
111
|
+
command: 'vim',
|
|
112
|
+
status: 'disabled',
|
|
113
|
+
active: false,
|
|
114
|
+
previous: current,
|
|
115
|
+
message: 'Vim mode off.',
|
|
116
|
+
};
|
|
117
|
+
ctx.writeOutput(payload, payload.message);
|
|
118
|
+
return 0;
|
|
119
|
+
}
|
|
120
|
+
if (verb === 'status' || verb === 'show') {
|
|
121
|
+
const payload = {
|
|
122
|
+
command: 'vim',
|
|
123
|
+
status: 'show',
|
|
124
|
+
active: current,
|
|
125
|
+
message: current ? 'Vim mode is on.' : 'Vim mode is off.',
|
|
126
|
+
};
|
|
127
|
+
ctx.writeOutput(payload, payload.message);
|
|
128
|
+
return 0;
|
|
129
|
+
}
|
|
130
|
+
const payload = {
|
|
131
|
+
command: 'vim',
|
|
132
|
+
status: 'invalid_args',
|
|
133
|
+
active: current,
|
|
134
|
+
message: `Unknown vim subcommand "${verb}". Try one of: on, off, status.`,
|
|
135
|
+
attemptedArg: verb,
|
|
136
|
+
};
|
|
137
|
+
ctx.writeOutput(payload, payload.message);
|
|
138
|
+
return 2;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=vim.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `pugi worktree <op>` —
|
|
2
|
+
* `pugi worktree <op>` — Phase 1.
|
|
3
3
|
*
|
|
4
4
|
* Manual control over the scratch worktree primitive. Three subcommands:
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* pugi worktree create [branch] # spawns `.pugi/worktrees/<uuid>`
|
|
7
|
+
* pugi worktree promote <path> # applies the worktree's diff back to cwd
|
|
8
|
+
* pugi worktree drop <path> # removes the worktree (idempotent)
|
|
9
9
|
*
|
|
10
10
|
* Output: human-readable by default, structured JSON under --json so
|
|
11
11
|
* scripted callers can chain (`pugi worktree create --json | jq .path`).
|
|
@@ -20,7 +20,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
20
20
|
import { resolve, sep } from 'node:path';
|
|
21
21
|
import { createWorktree, dropWorktree, promoteWorktree } from '../../core/edits/worktree.js';
|
|
22
22
|
/**
|
|
23
|
-
* R1 fix (2026-05-26, PR
|
|
23
|
+
* R1 fix (2026-05-26, PR r1, P2 #10): operator-facing path
|
|
24
24
|
* validation. The core `promoteWorktree` / `dropWorktree` primitives
|
|
25
25
|
* already gate their inputs, but mirroring the check at the CLI surface
|
|
26
26
|
* gives the operator a clean error message before we even attempt the
|
|
@@ -168,9 +168,9 @@ function usage() {
|
|
|
168
168
|
return {
|
|
169
169
|
ok: false,
|
|
170
170
|
text: 'Usage: pugi worktree <op>\n' +
|
|
171
|
-
'
|
|
172
|
-
'
|
|
173
|
-
'
|
|
171
|
+
' pugi worktree create [branch]\n' +
|
|
172
|
+
' pugi worktree promote <path>\n' +
|
|
173
|
+
' pugi worktree drop <path>',
|
|
174
174
|
exitCode: 2,
|
|
175
175
|
};
|
|
176
176
|
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `pugi worktrees <op>` — .
|
|
3
|
+
*
|
|
4
|
+
* Plural surface for the agent-bound worktree manager. Distinct from the
|
|
5
|
+
* singular `pugi worktree <op>` which manages UUID-keyed
|
|
6
|
+
* scratch worktrees for the manual `create / promote / drop` flow.
|
|
7
|
+
*
|
|
8
|
+
* pugi worktrees list # show active agent worktrees
|
|
9
|
+
* pugi worktrees cleanup <agent-id> # remove one
|
|
10
|
+
* pugi worktrees cleanup --all-stale # sweep orphans + completed/failed
|
|
11
|
+
* pugi worktrees cleanup --all-stale --dry-run
|
|
12
|
+
*
|
|
13
|
+
* Output: human-readable by default, NDJSON envelope under --json.
|
|
14
|
+
*
|
|
15
|
+
* Brand voice: ASCII only, no emoji, no banned words.
|
|
16
|
+
*/
|
|
17
|
+
import { runStaleCleanup } from '../../core/worktree-manager/cleanup.js';
|
|
18
|
+
import { WorktreeManager } from '../../core/worktree-manager/manager.js';
|
|
19
|
+
export async function runWorktreesCommand(args, opts) {
|
|
20
|
+
const [op, ...rest] = args;
|
|
21
|
+
if (!op)
|
|
22
|
+
return usage();
|
|
23
|
+
if (op === 'list') {
|
|
24
|
+
return runList(opts);
|
|
25
|
+
}
|
|
26
|
+
if (op === 'cleanup') {
|
|
27
|
+
return runCleanup(rest, opts);
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
ok: false,
|
|
31
|
+
text: `unknown worktrees operation: ${op}. Supported: list, cleanup`,
|
|
32
|
+
exitCode: 2,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function runList(opts) {
|
|
36
|
+
const manager = new WorktreeManager({ cwd: opts.cwd });
|
|
37
|
+
const result = manager.list();
|
|
38
|
+
if (!result.ok) {
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
text: opts.json
|
|
42
|
+
? JSON.stringify(result, null, 2)
|
|
43
|
+
: `worktrees list failed: ${result.reason}: ${result.detail}`,
|
|
44
|
+
exitCode: 1,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const rows = result.value;
|
|
48
|
+
if (opts.json) {
|
|
49
|
+
return {
|
|
50
|
+
ok: true,
|
|
51
|
+
text: JSON.stringify({ worktrees: rows }, null, 2),
|
|
52
|
+
exitCode: 0,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (rows.length === 0) {
|
|
56
|
+
return { ok: true, text: 'no agent worktrees', exitCode: 0 };
|
|
57
|
+
}
|
|
58
|
+
const lines = [];
|
|
59
|
+
lines.push('AGENT ID BRANCH GIT PATH');
|
|
60
|
+
for (const row of rows) {
|
|
61
|
+
lines.push(`${pad(row.agentId, 28)}${pad(row.branch, 36)}${pad(row.gitTracked ? 'yes' : 'no', 8)}${row.path}`);
|
|
62
|
+
}
|
|
63
|
+
return { ok: true, text: lines.join('\n'), exitCode: 0 };
|
|
64
|
+
}
|
|
65
|
+
function runCleanup(args, opts) {
|
|
66
|
+
// --all-stale sweeps every classified stale entry. Otherwise the next
|
|
67
|
+
// positional arg is the agent id to remove.
|
|
68
|
+
const allStale = args.includes('--all-stale');
|
|
69
|
+
if (allStale) {
|
|
70
|
+
const report = runStaleCleanup({ cwd: opts.cwd }, opts.dryRun ? { dryRun: true } : {});
|
|
71
|
+
return formatStaleReport(report, opts);
|
|
72
|
+
}
|
|
73
|
+
const agentId = args.find((a) => !a.startsWith('--'));
|
|
74
|
+
if (!agentId) {
|
|
75
|
+
return {
|
|
76
|
+
ok: false,
|
|
77
|
+
text: 'Usage: pugi worktrees cleanup <agent-id> | --all-stale [--dry-run]',
|
|
78
|
+
exitCode: 2,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const manager = new WorktreeManager({ cwd: opts.cwd });
|
|
82
|
+
const result = manager.cleanup(agentId);
|
|
83
|
+
if (!result.ok) {
|
|
84
|
+
// Missing-worktree is idempotent: surface the structured note but
|
|
85
|
+
// return exit 0 so a double-cleanup in a teardown hook never trips
|
|
86
|
+
// CI. Every other failure mode (invalid id, git failure) stays
|
|
87
|
+
// exit 1.
|
|
88
|
+
return {
|
|
89
|
+
ok: false,
|
|
90
|
+
text: opts.json
|
|
91
|
+
? JSON.stringify(result, null, 2)
|
|
92
|
+
: `cleanup failed: ${result.reason}: ${result.detail}`,
|
|
93
|
+
exitCode: result.reason === 'worktree_missing' ? 0 : 1,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
ok: true,
|
|
98
|
+
text: opts.json
|
|
99
|
+
? JSON.stringify({ removed: result.value.removed }, null, 2)
|
|
100
|
+
: `worktree removed: ${result.value.removed}`,
|
|
101
|
+
exitCode: 0,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function formatStaleReport(report, opts) {
|
|
105
|
+
if (opts.json) {
|
|
106
|
+
return {
|
|
107
|
+
ok: report.errors.length === 0,
|
|
108
|
+
text: JSON.stringify(report, null, 2),
|
|
109
|
+
exitCode: report.errors.length === 0 ? 0 : 1,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
const lines = [];
|
|
113
|
+
lines.push(`scanned: ${report.scanned} worktree(s)`);
|
|
114
|
+
if (report.removed.length > 0) {
|
|
115
|
+
lines.push(`removed: ${report.removed.length}`);
|
|
116
|
+
for (const r of report.removed)
|
|
117
|
+
lines.push(` - ${r}`);
|
|
118
|
+
}
|
|
119
|
+
if (report.preserved.length > 0) {
|
|
120
|
+
lines.push(`preserved: ${report.preserved.length}`);
|
|
121
|
+
for (const p of report.preserved)
|
|
122
|
+
lines.push(` - ${p.agentId} (${p.reason})`);
|
|
123
|
+
}
|
|
124
|
+
if (opts.dryRun) {
|
|
125
|
+
lines.push('dry-run: classifications only, nothing removed');
|
|
126
|
+
for (const c of report.classified)
|
|
127
|
+
lines.push(` - ${c.agentId}: ${c.class}`);
|
|
128
|
+
}
|
|
129
|
+
if (report.errors.length > 0) {
|
|
130
|
+
lines.push(`errors: ${report.errors.length}`);
|
|
131
|
+
for (const e of report.errors)
|
|
132
|
+
lines.push(` - ${e.agentId}: ${e.detail}`);
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
ok: report.errors.length === 0,
|
|
136
|
+
text: lines.join('\n'),
|
|
137
|
+
exitCode: report.errors.length === 0 ? 0 : 1,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function usage() {
|
|
141
|
+
return {
|
|
142
|
+
ok: false,
|
|
143
|
+
text: 'Usage: pugi worktrees <op>\n' +
|
|
144
|
+
' pugi worktrees list\n' +
|
|
145
|
+
' pugi worktrees cleanup <agent-id>\n' +
|
|
146
|
+
' pugi worktrees cleanup --all-stale [--dry-run]',
|
|
147
|
+
exitCode: 2,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function pad(s, n) {
|
|
151
|
+
if (s.length >= n)
|
|
152
|
+
return `${s.slice(0, n - 1)} `;
|
|
153
|
+
return s + ' '.repeat(n - s.length);
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=worktrees.js.map
|