@pugi/cli 0.1.0-beta.99 → 1.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +11 -191
- package/bin/pugi +8 -0
- package/package.json +15 -71
- package/postinstall.mjs +31 -0
- package/CHANGELOG.md +0 -132
- package/THIRD_PARTY_NOTICES.md +0 -40
- package/assets/pugi-mascot.ansi +0 -16
- package/assets/pugi-prozr2-mascot.ansi +0 -9
- package/bin/run.js +0 -34
- package/dist/commands/deploy.js +0 -439
- package/dist/commands/flatten.js +0 -191
- package/dist/commands/jobs-watch.js +0 -201
- package/dist/commands/jobs.js +0 -260
- package/dist/commands/retro.js +0 -210
- package/dist/commands/smoke.js +0 -133
- package/dist/core/agent-progress/cleanup.js +0 -134
- package/dist/core/agent-progress/schema.js +0 -144
- package/dist/core/agent-progress/writer.js +0 -101
- package/dist/core/agents/adaptive-router.js +0 -330
- package/dist/core/agents/loader.js +0 -104
- package/dist/core/agents/query-decomposer.js +0 -297
- package/dist/core/agents/registry.js +0 -69
- package/dist/core/approvals/shortcut-resolver.js +0 -98
- package/dist/core/artifact-chain/dispatcher.js +0 -148
- package/dist/core/artifact-chain/exporter.js +0 -164
- package/dist/core/artifact-chain/state.js +0 -243
- package/dist/core/artifact-chain/steps.js +0 -169
- package/dist/core/ask-user/question.js +0 -92
- package/dist/core/audit/audit-trail.js +0 -275
- package/dist/core/auth/ensure-authenticated.js +0 -129
- package/dist/core/auth/env-provider.js +0 -238
- package/dist/core/auto-open-browser.js +0 -128
- package/dist/core/auto-update/channels.js +0 -122
- package/dist/core/auto-update/checker.js +0 -241
- package/dist/core/auto-update/state.js +0 -235
- package/dist/core/bare-mode/index.js +0 -107
- package/dist/core/bash/redirect.js +0 -281
- package/dist/core/bash-classifier.js +0 -1397
- package/dist/core/checkpoint/resumer.js +0 -149
- package/dist/core/checkpoint/rewinder.js +0 -291
- package/dist/core/checkpoints/shadow-git.js +0 -670
- package/dist/core/citations/parser.js +0 -109
- package/dist/core/classifier/yolo-classifier.js +0 -88
- package/dist/core/clipboard.js +0 -70
- package/dist/core/codegraph/decision-store.js +0 -248
- package/dist/core/codegraph/detect-repo.js +0 -459
- package/dist/core/codegraph/install.js +0 -134
- package/dist/core/codegraph/offer-hook.js +0 -220
- package/dist/core/compact/auto-trigger.js +0 -96
- package/dist/core/compact/buffer-rewriter.js +0 -115
- package/dist/core/compact/summarizer.js +0 -208
- package/dist/core/compact/token-counter.js +0 -108
- package/dist/core/consensus/anvil-fanout.js +0 -276
- package/dist/core/consensus/diff-capture.js +0 -491
- package/dist/core/consensus/rubric.js +0 -233
- package/dist/core/context/builder.js +0 -114
- package/dist/core/context/compaction-events.js +0 -99
- package/dist/core/context/compaction.js +0 -602
- package/dist/core/context/index.js +0 -28
- package/dist/core/context/invariants.js +0 -250
- package/dist/core/context/markdown-loader.js +0 -288
- package/dist/core/context/markdown-traverse.js +0 -255
- package/dist/core/context/pugiignore.js +0 -316
- package/dist/core/context/repo-skeleton.js +0 -533
- package/dist/core/context/tool-eviction.js +0 -55
- package/dist/core/context/watcher.js +0 -342
- package/dist/core/context/working-set.js +0 -165
- package/dist/core/coordinator/agent-tools.js +0 -77
- package/dist/core/coordinator/agent-toolset.js +0 -65
- package/dist/core/coordinator/fsm.js +0 -73
- package/dist/core/coordinator/mode-fsm.js +0 -70
- package/dist/core/cost/rate-card.js +0 -129
- package/dist/core/cost/tracker.js +0 -221
- package/dist/core/credentials.js +0 -355
- package/dist/core/cron/scheduler.js +0 -138
- package/dist/core/denial-tracking/index.js +0 -8
- package/dist/core/denial-tracking/state.js +0 -264
- package/dist/core/diagnostics/probe-runner.js +0 -93
- package/dist/core/diagnostics/probes/api.js +0 -46
- package/dist/core/diagnostics/probes/auth.js +0 -93
- package/dist/core/diagnostics/probes/bare-mode.js +0 -42
- package/dist/core/diagnostics/probes/cli-version.js +0 -127
- package/dist/core/diagnostics/probes/config.js +0 -72
- package/dist/core/diagnostics/probes/denial-tracking.js +0 -57
- package/dist/core/diagnostics/probes/disk.js +0 -81
- package/dist/core/diagnostics/probes/engine-live.js +0 -46
- package/dist/core/diagnostics/probes/git.js +0 -65
- package/dist/core/diagnostics/probes/hooks.js +0 -118
- package/dist/core/diagnostics/probes/mcp.js +0 -75
- package/dist/core/diagnostics/probes/node.js +0 -59
- package/dist/core/diagnostics/probes/pnpm.js +0 -36
- package/dist/core/diagnostics/probes/pugi-md.js +0 -89
- package/dist/core/diagnostics/probes/sandbox.js +0 -72
- package/dist/core/diagnostics/probes/session.js +0 -74
- package/dist/core/diagnostics/probes/status-snapshot.js +0 -488
- package/dist/core/diagnostics/probes/workspace.js +0 -63
- package/dist/core/diagnostics/types.js +0 -70
- package/dist/core/dispatch/cache-cleanup.js +0 -197
- package/dist/core/dispatch/cache-handoff.js +0 -295
- package/dist/core/edits/apply-patch-layer-e.js +0 -189
- package/dist/core/edits/dispatch.js +0 -511
- package/dist/core/edits/format-detector.js +0 -260
- package/dist/core/edits/format-matrix.js +0 -26
- package/dist/core/edits/fuzzy-ladder.js +0 -650
- package/dist/core/edits/index.js +0 -19
- package/dist/core/edits/journal.js +0 -199
- package/dist/core/edits/layer-a-apply.js +0 -217
- package/dist/core/edits/layer-a-fuzzy-apply.js +0 -198
- package/dist/core/edits/layer-b-apply.js +0 -211
- package/dist/core/edits/layer-c-apply.js +0 -160
- package/dist/core/edits/layer-d-ast.js +0 -572
- package/dist/core/edits/marker-parser.js +0 -401
- package/dist/core/edits/security-gate.js +0 -223
- package/dist/core/edits/verify-hook.js +0 -273
- package/dist/core/edits/worktree.js +0 -322
- package/dist/core/engine/adapter-runner.js +0 -8
- package/dist/core/engine/anvil-client.js +0 -344
- package/dist/core/engine/auto-compact.js +0 -179
- package/dist/core/engine/budgets.js +0 -195
- package/dist/core/engine/context-prefix.js +0 -155
- package/dist/core/engine/index.js +0 -12
- package/dist/core/engine/intensity.js +0 -163
- package/dist/core/engine/intent.js +0 -260
- package/dist/core/engine/native-pugi.js +0 -1616
- package/dist/core/engine/noop.js +0 -27
- package/dist/core/engine/prompts.js +0 -236
- package/dist/core/engine/strip-internal-fields.js +0 -124
- package/dist/core/engine/tool-bridge.js +0 -2173
- package/dist/core/engine/verification-patterns.js +0 -195
- package/dist/core/evaluation/golden-dataset.js +0 -293
- package/dist/core/feedback/queue.js +0 -177
- package/dist/core/feedback/submitter.js +0 -145
- package/dist/core/file-cache.js +0 -141
- package/dist/core/flatten/flatten-repo.js +0 -439
- package/dist/core/format/osc8-link.js +0 -28
- package/dist/core/hook-chains.js +0 -392
- package/dist/core/hooks/citation-verify-hook.js +0 -138
- package/dist/core/hooks/citation-verify.js +0 -112
- package/dist/core/hooks/events.js +0 -46
- package/dist/core/hooks/index.js +0 -15
- package/dist/core/hooks/registry.js +0 -216
- package/dist/core/hooks/runner.js +0 -236
- package/dist/core/hooks/v2/event-emitter.js +0 -115
- package/dist/core/hooks/v2/executor.js +0 -282
- package/dist/core/hooks/v2/index.js +0 -25
- package/dist/core/hooks/v2/lifecycle.js +0 -104
- package/dist/core/hooks/v2/loader.js +0 -216
- package/dist/core/hooks/v2/matcher.js +0 -125
- package/dist/core/hooks/v2/trust.js +0 -143
- package/dist/core/hooks/v2/types.js +0 -86
- package/dist/core/hooks/worktree-events.js +0 -158
- package/dist/core/hooks.js +0 -415
- package/dist/core/image/renderer.js +0 -71
- package/dist/core/index-store.js +0 -260
- package/dist/core/init/detector.js +0 -582
- package/dist/core/init/template-renderer.js +0 -242
- package/dist/core/jobs/registry.js +0 -462
- package/dist/core/ledger/results-tsv.js +0 -142
- package/dist/core/log-discipline/stdout-redirect.js +0 -51
- package/dist/core/lsp/cache.js +0 -105
- package/dist/core/lsp/client.js +0 -1229
- package/dist/core/lsp/language-detect.js +0 -66
- package/dist/core/lsp/post-edit-diagnostics.js +0 -171
- package/dist/core/lsp/server-detect.js +0 -173
- package/dist/core/lsp/symbol-cache.js +0 -162
- package/dist/core/lsp/symbol-tools.js +0 -664
- package/dist/core/mcp/client.js +0 -385
- package/dist/core/mcp/http-server.js +0 -553
- package/dist/core/mcp/orchestrator-config.js +0 -192
- package/dist/core/mcp/orchestrator-tools.js +0 -806
- package/dist/core/mcp/permission.js +0 -190
- package/dist/core/mcp/registry.js +0 -193
- package/dist/core/mcp/server-tools.js +0 -219
- package/dist/core/mcp/server.js +0 -397
- package/dist/core/mcp/trust.js +0 -91
- package/dist/core/memory/dual-write.js +0 -416
- package/dist/core/memory/passive-extract.js +0 -130
- package/dist/core/memory/phase1-kinds.js +0 -20
- package/dist/core/memory/secret-scanner.js +0 -304
- package/dist/core/memory-sync/queue.js +0 -170
- package/dist/core/metrics/extract.js +0 -113
- package/dist/core/modes/roo-modes.js +0 -68
- package/dist/core/onboarding/ensure-initialized.js +0 -133
- package/dist/core/onboarding/marker.js +0 -111
- package/dist/core/onboarding/telemetry-state.js +0 -108
- package/dist/core/output-style/presets.js +0 -176
- package/dist/core/output-style/state.js +0 -185
- package/dist/core/path-security.js +0 -345
- package/dist/core/permission.js +0 -369
- package/dist/core/permissions/auto-classifier.js +0 -124
- package/dist/core/permissions/bash-parser.js +0 -371
- package/dist/core/permissions/circuit-breaker.js +0 -83
- package/dist/core/permissions/constrained-edit.js +0 -91
- package/dist/core/permissions/gate.js +0 -278
- package/dist/core/permissions/index.js +0 -20
- package/dist/core/permissions/mode.js +0 -174
- package/dist/core/permissions/network-egress.js +0 -137
- package/dist/core/permissions/state.js +0 -241
- package/dist/core/permissions/tool-class.js +0 -107
- package/dist/core/plan-mode/ui-state.js +0 -51
- package/dist/core/plans/plan-artifact.js +0 -721
- package/dist/core/policy-limits/etag-store.js +0 -122
- package/dist/core/prd-check/parser.js +0 -215
- package/dist/core/prd-check/reporter.js +0 -127
- package/dist/core/prd-check/session-review.js +0 -557
- package/dist/core/prd-check/verifiers.js +0 -223
- package/dist/core/prompt-cache/client-cache.js +0 -99
- package/dist/core/prompts/assembly.js +0 -29
- package/dist/core/prompts/registry.js +0 -364
- package/dist/core/pugi-gitignore.js +0 -52
- package/dist/core/pugi-md/cc-compat-rules.js +0 -735
- package/dist/core/pugi-md/context-injector.js +0 -76
- package/dist/core/pugi-md/walk-up.js +0 -207
- package/dist/core/python/uv-installer.js +0 -270
- package/dist/core/python/uv-resolver.js +0 -83
- package/dist/core/rate-limit/narrator.js +0 -146
- package/dist/core/recipes/cli-types.js +0 -20
- package/dist/core/recipes/loader.js +0 -103
- package/dist/core/recipes/runner.js +0 -345
- package/dist/core/recipes/schema.js +0 -587
- package/dist/core/release-notes/parser.js +0 -241
- package/dist/core/release-notes/state.js +0 -116
- package/dist/core/repl/ask.js +0 -512
- package/dist/core/repl/cancellation.js +0 -98
- package/dist/core/repl/cap-warning.js +0 -91
- package/dist/core/repl/clipboard-read.js +0 -174
- package/dist/core/repl/dispatch-fsm.js +0 -220
- package/dist/core/repl/engine-bridge.js +0 -303
- package/dist/core/repl/history-search.js +0 -175
- package/dist/core/repl/history.js +0 -182
- package/dist/core/repl/kill-ring.js +0 -138
- package/dist/core/repl/model-pricing.js +0 -135
- package/dist/core/repl/privacy-banner.js +0 -71
- package/dist/core/repl/session.js +0 -4962
- package/dist/core/repl/slash-commands.js +0 -747
- package/dist/core/repl/store/index.js +0 -12
- package/dist/core/repl/store/jsonl-log.js +0 -321
- package/dist/core/repl/store/lockfile.js +0 -155
- package/dist/core/repl/store/session-store.js +0 -821
- package/dist/core/repl/store/types.js +0 -44
- package/dist/core/repl/store/uuid-v7.js +0 -68
- package/dist/core/repl/tool-route.js +0 -382
- package/dist/core/repl/workspace-context.js +0 -206
- package/dist/core/repo-map/build.js +0 -125
- package/dist/core/repo-map/cache.js +0 -185
- package/dist/core/repo-map/extractor.js +0 -254
- package/dist/core/repo-map/formatter.js +0 -145
- package/dist/core/repo-map/page-rank.js +0 -105
- package/dist/core/repo-map/scanner.js +0 -211
- package/dist/core/retro/git-collector.js +0 -251
- package/dist/core/retro/health-card.js +0 -25
- package/dist/core/retro/metrics.js +0 -342
- package/dist/core/retro/narrative.js +0 -249
- package/dist/core/retro/plane-collector.js +0 -274
- package/dist/core/retro/pr-issue-link.js +0 -65
- package/dist/core/retro/types.js +0 -16
- package/dist/core/retry-budget/budget.js +0 -284
- package/dist/core/retry-budget/index.js +0 -5
- package/dist/core/retry-budget/retry-cap.js +0 -74
- package/dist/core/routing/lead-worker.js +0 -43
- package/dist/core/routing/pre-flight-estimator.js +0 -108
- package/dist/core/runs/run-tree.js +0 -103
- package/dist/core/sandboxing/adapter.js +0 -29
- package/dist/core/sandboxing/index.js +0 -49
- package/dist/core/sandboxing/none.js +0 -19
- package/dist/core/sandboxing/seatbelt.js +0 -183
- package/dist/core/security/injection-scanner.js +0 -367
- package/dist/core/security/output-filter.js +0 -418
- package/dist/core/session/env-file.js +0 -105
- package/dist/core/session/section-budgets.js +0 -140
- package/dist/core/session.js +0 -377
- package/dist/core/settings.js +0 -400
- package/dist/core/share/formatter.js +0 -271
- package/dist/core/share/redactor.js +0 -221
- package/dist/core/share/uploader.js +0 -267
- package/dist/core/skills/defaults.js +0 -457
- package/dist/core/skills/loader.js +0 -454
- package/dist/core/skills/sources.js +0 -480
- package/dist/core/skills/trust.js +0 -172
- package/dist/core/smoke/headless-driver.js +0 -174
- package/dist/core/smoke/orchestrator.js +0 -194
- package/dist/core/smoke/runner.js +0 -238
- package/dist/core/smoke/scenario-parser.js +0 -316
- package/dist/core/statusline.js +0 -99
- package/dist/core/subagents/dispatcher-real.js +0 -600
- package/dist/core/subagents/dispatcher.js +0 -352
- package/dist/core/subagents/index.js +0 -39
- package/dist/core/subagents/isolation-matrix.js +0 -213
- package/dist/core/subagents/spawn.js +0 -101
- package/dist/core/telemetry/emitter.js +0 -229
- package/dist/core/telemetry/queue.js +0 -251
- package/dist/core/theme/context.js +0 -91
- package/dist/core/theme/presets.js +0 -228
- package/dist/core/theme/state.js +0 -181
- package/dist/core/todos/invariant.js +0 -10
- package/dist/core/todos/state.js +0 -177
- package/dist/core/tool-schema/compressor.js +0 -89
- package/dist/core/transport/version-interceptor.js +0 -166
- package/dist/core/trust.js +0 -109
- package/dist/core/tui/thinking-block.js +0 -64
- package/dist/core/vim/keymap.js +0 -288
- package/dist/core/vim/state.js +0 -92
- package/dist/core/watch-markers/marker-watcher.js +0 -133
- package/dist/core/worktree/include-parser.js +0 -249
- package/dist/core/worktree-manager/cleanup.js +0 -123
- package/dist/core/worktree-manager/manager.js +0 -303
- package/dist/index.js +0 -44
- package/dist/runtime/bootstrap.js +0 -190
- package/dist/runtime/cli.js +0 -8121
- package/dist/runtime/commands/agents.js +0 -385
- package/dist/runtime/commands/budget.js +0 -192
- package/dist/runtime/commands/cancel.js +0 -231
- package/dist/runtime/commands/chain.js +0 -489
- package/dist/runtime/commands/codegraph-status.js +0 -227
- package/dist/runtime/commands/compact.js +0 -297
- package/dist/runtime/commands/config.js +0 -595
- package/dist/runtime/commands/cost.js +0 -199
- package/dist/runtime/commands/delegate.js +0 -312
- package/dist/runtime/commands/dispatch.js +0 -126
- package/dist/runtime/commands/doctor.js +0 -579
- package/dist/runtime/commands/feedback.js +0 -184
- package/dist/runtime/commands/hooks.js +0 -187
- package/dist/runtime/commands/init.js +0 -254
- package/dist/runtime/commands/lsp.js +0 -368
- package/dist/runtime/commands/mcp.js +0 -935
- package/dist/runtime/commands/memory.js +0 -582
- package/dist/runtime/commands/model.js +0 -237
- package/dist/runtime/commands/onboarding.js +0 -275
- package/dist/runtime/commands/patch.js +0 -128
- package/dist/runtime/commands/permissions.js +0 -112
- package/dist/runtime/commands/plan.js +0 -143
- package/dist/runtime/commands/prd-check.js +0 -285
- package/dist/runtime/commands/privacy.js +0 -107
- package/dist/runtime/commands/recipe.js +0 -325
- package/dist/runtime/commands/redo-blob-store.js +0 -92
- package/dist/runtime/commands/redo.js +0 -361
- package/dist/runtime/commands/release-notes.js +0 -229
- package/dist/runtime/commands/repo-map.js +0 -95
- package/dist/runtime/commands/report.js +0 -299
- package/dist/runtime/commands/resume.js +0 -118
- package/dist/runtime/commands/review-consensus.js +0 -414
- package/dist/runtime/commands/rewind.js +0 -333
- package/dist/runtime/commands/roster.js +0 -117
- package/dist/runtime/commands/sessions.js +0 -163
- package/dist/runtime/commands/share.js +0 -316
- package/dist/runtime/commands/skills.js +0 -401
- package/dist/runtime/commands/status.js +0 -186
- package/dist/runtime/commands/stickers.js +0 -82
- package/dist/runtime/commands/style.js +0 -194
- package/dist/runtime/commands/theme.js +0 -196
- package/dist/runtime/commands/undo.js +0 -361
- package/dist/runtime/commands/update.js +0 -289
- package/dist/runtime/commands/vim.js +0 -140
- package/dist/runtime/commands/worktree.js +0 -177
- package/dist/runtime/commands/worktrees.js +0 -155
- package/dist/runtime/deprecation-warning.js +0 -69
- package/dist/runtime/engine-exit-code.js +0 -50
- package/dist/runtime/headless-repl.js +0 -195
- package/dist/runtime/headless.js +0 -548
- package/dist/runtime/load-hooks-or-exit.js +0 -71
- package/dist/runtime/plan-decompose.js +0 -531
- package/dist/runtime/sigint-guard.js +0 -272
- package/dist/runtime/stream-renderer.js +0 -195
- package/dist/runtime/update-check.js +0 -294
- package/dist/runtime/version.js +0 -65
- package/dist/runtime/worktree-bootstrap.js +0 -579
- package/dist/skills/bundled/batch.js +0 -617
- package/dist/skills/bundled/index.js +0 -45
- package/dist/skills/bundled/loop.js +0 -358
- package/dist/skills/bundled/remember.js +0 -383
- package/dist/skills/bundled/simplify.js +0 -289
- package/dist/skills/bundled/skillify.js +0 -373
- package/dist/skills/bundled/stuck.js +0 -558
- package/dist/skills/bundled/verify.js +0 -439
- package/dist/testing/vcr.js +0 -486
- package/dist/tools/agent-tool.js +0 -229
- package/dist/tools/apply-patch.js +0 -556
- package/dist/tools/ask-user-question.js +0 -337
- package/dist/tools/ask-user.js +0 -115
- package/dist/tools/bash.js +0 -1238
- package/dist/tools/brief.js +0 -224
- package/dist/tools/cron.js +0 -433
- package/dist/tools/enter-worktree.js +0 -250
- package/dist/tools/exit-worktree.js +0 -147
- package/dist/tools/file-tools.js +0 -553
- package/dist/tools/http-request.js +0 -336
- package/dist/tools/lsp-tools.js +0 -565
- package/dist/tools/mcp-tool.js +0 -260
- package/dist/tools/multi-edit.js +0 -361
- package/dist/tools/powershell.js +0 -268
- package/dist/tools/registry.js +0 -166
- package/dist/tools/server-tools.js +0 -892
- package/dist/tools/skill-tool.js +0 -96
- package/dist/tools/sleep.js +0 -99
- package/dist/tools/synthetic-output.js +0 -133
- package/dist/tools/tasks.js +0 -208
- package/dist/tools/todo-write.js +0 -184
- package/dist/tools/verify-plan-execution.js +0 -295
- package/dist/tools/web-fetch-injection-scanner.js +0 -207
- package/dist/tools/web-fetch.js +0 -720
- package/dist/tools/web-search.js +0 -458
- package/dist/tui/agent-progress-card.js +0 -111
- package/dist/tui/agent-tree-pane.js +0 -9
- package/dist/tui/agent-tree.js +0 -87
- package/dist/tui/ask-cli.js +0 -52
- package/dist/tui/ask-modal.js +0 -211
- package/dist/tui/ask-user-question-chips.js +0 -315
- package/dist/tui/ask-user-question-prompt.js +0 -203
- package/dist/tui/compact-banner.js +0 -81
- package/dist/tui/conversation-pane.js +0 -164
- package/dist/tui/cost-table.js +0 -111
- package/dist/tui/device-flow.js +0 -142
- package/dist/tui/doctor-table.js +0 -46
- package/dist/tui/feedback-prompt.js +0 -156
- package/dist/tui/input-box.js +0 -732
- package/dist/tui/login-picker.js +0 -69
- package/dist/tui/markdown-render.js +0 -266
- package/dist/tui/multi-file-diff-approval.js +0 -375
- package/dist/tui/onboarding-wizard.js +0 -240
- package/dist/tui/permissions-picker.js +0 -86
- package/dist/tui/render.js +0 -160
- package/dist/tui/repl-render.js +0 -770
- package/dist/tui/repl-splash-art.js +0 -64
- package/dist/tui/repl-splash-mascot.js +0 -154
- package/dist/tui/repl-splash.js +0 -117
- package/dist/tui/repl.js +0 -378
- package/dist/tui/slash-palette.js +0 -106
- package/dist/tui/splash-data.js +0 -61
- package/dist/tui/splash.js +0 -31
- package/dist/tui/status-bar.js +0 -209
- package/dist/tui/status-table.js +0 -7
- package/dist/tui/stickers-art.js +0 -136
- package/dist/tui/style-table.js +0 -28
- package/dist/tui/theme-table.js +0 -29
- package/dist/tui/thinking-spinner.js +0 -123
- package/dist/tui/tool-stream-pane.js +0 -140
- package/dist/tui/update-banner.js +0 -33
- package/dist/tui/vim-input.js +0 -267
- package/dist/tui/welcome-banner.js +0 -107
- package/dist/tui/welcome-data.js +0 -293
- package/dist/tui/workspace-context.js +0 -105
- package/docs/examples/codegraph.mcp.json +0 -10
- package/test/scenarios/codegen-create-file.scenario.txt +0 -13
- package/test/scenarios/compact-force.scenario.txt +0 -12
- package/test/scenarios/identity.scenario.txt +0 -11
- package/test/scenarios/persona-handoff.scenario.txt +0 -12
- package/test/scenarios/walkback.scenario.txt +0 -12
|
@@ -1,361 +0,0 @@
|
|
|
1
|
-
import { execFileSync } from 'node:child_process';
|
|
2
|
-
import { existsSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { resolve } from 'node:path';
|
|
4
|
-
import { hashContent } from '../../core/file-cache.js';
|
|
5
|
-
import { recordFileMutation, recordToolCall, recordToolResult, } from '../../core/session.js';
|
|
6
|
-
import { writeBlob } from './redo-blob-store.js';
|
|
7
|
-
/**
|
|
8
|
-
* `pugi undo` — revert the file mutations from the most recent successful
|
|
9
|
-
* `write` / `edit` / `multi_edit` tool result.
|
|
10
|
-
*
|
|
11
|
-
* Walk strategy:
|
|
12
|
-
* 1. Read `.pugi/events.jsonl` line by line into an array.
|
|
13
|
-
* 2. Walk backwards. Find the most recent `tool_result` whose
|
|
14
|
-
* `status === 'success'` and whose linked `tool_call` names a
|
|
15
|
-
* mutating tool (write / edit / multi_edit).
|
|
16
|
-
* 3. From that point, gather every `file_mutation` event that shares
|
|
17
|
-
* the same `toolCallId`. M1 records one `file_mutation` per
|
|
18
|
-
* mutating tool call, but the loop is shaped for the multi_edit
|
|
19
|
-
* future case where a single tool call mutates many files.
|
|
20
|
-
*
|
|
21
|
-
* Restore strategy (M1 — no blob store yet):
|
|
22
|
-
* For each mutation we restore from the workspace's git history when
|
|
23
|
-
* possible, with a strict safety gate to avoid silently overwriting
|
|
24
|
-
* the user's WORK-IN-PROGRESS:
|
|
25
|
-
*
|
|
26
|
-
* - `operation: create` → unlink the created file iff its current
|
|
27
|
-
* sha256 matches the recorded `afterHash`. Skip otherwise (the user
|
|
28
|
-
* has edited it since; refuse to delete their work).
|
|
29
|
-
* - `operation: update` → restore from `HEAD:<path>` iff (a) the file
|
|
30
|
-
* was tracked at HEAD AND (b) the HEAD content's sha256 matches the
|
|
31
|
-
* recorded `beforeHash`. Otherwise abort the turn (no partial
|
|
32
|
-
* reverts — the spec is atomic).
|
|
33
|
-
* - `operation: delete` → restore the deleted file from `HEAD:<path>`
|
|
34
|
-
* if tracked there; skip otherwise.
|
|
35
|
-
*
|
|
36
|
-
* If any single restore is unsafe the whole turn aborts with exit code
|
|
37
|
-
* 1 and no files are touched.
|
|
38
|
-
*
|
|
39
|
-
* Why git as the backing store: the file cache stores hash + metadata
|
|
40
|
-
* but NOT raw content (see `core/file-cache.ts`). M1 ships without a
|
|
41
|
-
* dedicated blob CAS; `git show HEAD:<path>` is the cheapest authoritative
|
|
42
|
-
* source for pre-mutation content as long as the workspace is a git
|
|
43
|
-
* repository. The SQLite session store will replace this with a
|
|
44
|
-
* proper before-blob ledger.
|
|
45
|
-
*/
|
|
46
|
-
const MUTATING_TOOLS = new Set(['write', 'edit', 'multi_edit']);
|
|
47
|
-
export async function runUndoCommand(_args, ctx) {
|
|
48
|
-
const eventsPath = resolve(ctx.workspaceRoot, '.pugi/events.jsonl');
|
|
49
|
-
if (!existsSync(eventsPath)) {
|
|
50
|
-
ctx.writeOutput({ command: 'undo', status: 'noop', reason: 'no_session' }, 'No session events found. Nothing to undo.');
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
const events = parseEvents(eventsPath);
|
|
54
|
-
const target = findLastMutationTurn(events);
|
|
55
|
-
if (!target) {
|
|
56
|
-
ctx.writeOutput({ command: 'undo', status: 'noop', reason: 'no_mutation' }, 'No mutating tool result found in the session log.');
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
if (target.mutations.length === 0) {
|
|
60
|
-
ctx.writeOutput({
|
|
61
|
-
command: 'undo',
|
|
62
|
-
status: 'noop',
|
|
63
|
-
reason: 'no_file_mutations',
|
|
64
|
-
toolCallId: target.toolCallId,
|
|
65
|
-
}, `Tool call ${target.toolCallId} recorded no file mutations.`);
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
// Pre-flight: confirm every mutation is reversible before touching
|
|
69
|
-
// disk. This keeps the operation atomic per spec.
|
|
70
|
-
const plan = planReverts(ctx.workspaceRoot, target.mutations);
|
|
71
|
-
if (plan.aborted) {
|
|
72
|
-
ctx.writeOutput({
|
|
73
|
-
command: 'undo',
|
|
74
|
-
status: 'aborted',
|
|
75
|
-
reason: plan.reason,
|
|
76
|
-
toolCallId: target.toolCallId,
|
|
77
|
-
unsafe: plan.unsafe,
|
|
78
|
-
}, `Refusing to undo ${target.toolCallId}: ${plan.reason}`);
|
|
79
|
-
process.exitCode = 1;
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
const restored = [];
|
|
83
|
-
for (const step of plan.steps) {
|
|
84
|
-
try {
|
|
85
|
-
// : snapshot the AFTER state into the redo
|
|
86
|
-
// blob store BEFORE we revert the file on disk. /redo reads this
|
|
87
|
-
// blob keyed by `step.beforeHash` (= original afterHash) to
|
|
88
|
-
// reapply the change. We only snapshot for operations that have
|
|
89
|
-
// on-disk AFTER content: `create` (file exists, about to be
|
|
90
|
-
// deleted) and `update` (file exists, about to be overwritten).
|
|
91
|
-
// For `delete` reverts (file was deleted by Pugi, the "after" is
|
|
92
|
-
// nothing) redo replays the delete itself — no content needed.
|
|
93
|
-
// Best-effort: a blob-store failure must not abort the undo, so
|
|
94
|
-
// the writeBlob call is wrapped и any error swallowed.
|
|
95
|
-
if (step.operation === 'create' || step.operation === 'update') {
|
|
96
|
-
try {
|
|
97
|
-
const abs = resolve(ctx.workspaceRoot, step.path);
|
|
98
|
-
if (existsSync(abs) && step.beforeHash) {
|
|
99
|
-
const current = readFileSync(abs, 'utf8');
|
|
100
|
-
// Defensive: only snapshot if the current sha matches the
|
|
101
|
-
// pre-revert hash the planner verified. The planner already
|
|
102
|
-
// gated this, but a TOCTOU between plan + execute would
|
|
103
|
-
// produce a wrong blob — silently dropping it is safer than
|
|
104
|
-
// shipping content under the wrong sha.
|
|
105
|
-
if (hashContent(current) === step.beforeHash) {
|
|
106
|
-
writeBlob(ctx.workspaceRoot, current);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
catch {
|
|
111
|
-
// Best-effort. Failure to snapshot means /redo will report
|
|
112
|
-
// "no captured content" — operator can re-run the mutation
|
|
113
|
-
// manually. Better than aborting the undo.
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
executeRevert(ctx.workspaceRoot, step);
|
|
117
|
-
restored.push({ path: step.path, operation: step.operation });
|
|
118
|
-
}
|
|
119
|
-
catch (error) {
|
|
120
|
-
// A revert failed after pre-flight said it was safe. Surface the
|
|
121
|
-
// error and bail out — the spec says no partial state on failure.
|
|
122
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
123
|
-
ctx.writeOutput({
|
|
124
|
-
command: 'undo',
|
|
125
|
-
status: 'failed',
|
|
126
|
-
reason: message,
|
|
127
|
-
toolCallId: target.toolCallId,
|
|
128
|
-
restored,
|
|
129
|
-
failedAt: step.path,
|
|
130
|
-
}, `Undo failed mid-flight on ${step.path}: ${message}`);
|
|
131
|
-
process.exitCode = 1;
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
// Audit the inverse mutations so the event log explains the undo.
|
|
136
|
-
const toolCallId = recordToolCall(ctx.session, 'undo', `revert ${target.toolCallId}`);
|
|
137
|
-
for (const step of plan.steps) {
|
|
138
|
-
recordFileMutation(ctx.session, {
|
|
139
|
-
toolCallId,
|
|
140
|
-
path: step.path,
|
|
141
|
-
operation: step.operation === 'create' ? 'delete' : 'update',
|
|
142
|
-
beforeHash: step.beforeHash,
|
|
143
|
-
afterHash: step.afterHash,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
recordToolResult(ctx.session, toolCallId, 'success', `Undid ${restored.length} mutation(s) from ${target.toolCallId}`);
|
|
147
|
-
ctx.writeOutput({
|
|
148
|
-
command: 'undo',
|
|
149
|
-
status: 'ok',
|
|
150
|
-
toolCallId: target.toolCallId,
|
|
151
|
-
restored,
|
|
152
|
-
}, [
|
|
153
|
-
`Undid ${restored.length} mutation(s) from ${target.toolCallId}:`,
|
|
154
|
-
...restored.map((entry) => ` ${entry.operation.padEnd(7)} ${entry.path}`),
|
|
155
|
-
].join('\n'));
|
|
156
|
-
}
|
|
157
|
-
function parseEvents(eventsPath) {
|
|
158
|
-
const raw = readFileSync(eventsPath, 'utf8');
|
|
159
|
-
const lines = raw.split('\n').filter((line) => line.trim().length > 0);
|
|
160
|
-
const out = [];
|
|
161
|
-
for (const line of lines) {
|
|
162
|
-
try {
|
|
163
|
-
const parsed = JSON.parse(line);
|
|
164
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
165
|
-
out.push(parsed);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
catch {
|
|
169
|
-
// Drop malformed lines silently — partial writes mid-shutdown can
|
|
170
|
-
// produce them and undo should still work for the rest.
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return out;
|
|
174
|
-
}
|
|
175
|
-
function findLastMutationTurn(events) {
|
|
176
|
-
const toolCalls = new Map();
|
|
177
|
-
const results = [];
|
|
178
|
-
for (let i = 0; i < events.length; i += 1) {
|
|
179
|
-
const event = events[i];
|
|
180
|
-
if (!event)
|
|
181
|
-
continue;
|
|
182
|
-
if (event.type === 'tool_call' && typeof event.id === 'string' && typeof event.tool === 'string') {
|
|
183
|
-
toolCalls.set(event.id, { id: event.id, tool: event.tool });
|
|
184
|
-
}
|
|
185
|
-
else if (event.type === 'tool_result' &&
|
|
186
|
-
typeof event.id === 'string' &&
|
|
187
|
-
typeof event.toolCallId === 'string' &&
|
|
188
|
-
(event.status === 'success' || event.status === 'error' || event.status === 'cancelled')) {
|
|
189
|
-
results.push({
|
|
190
|
-
id: event.id,
|
|
191
|
-
toolCallId: event.toolCallId,
|
|
192
|
-
status: event.status,
|
|
193
|
-
index: i,
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
for (let i = results.length - 1; i >= 0; i -= 1) {
|
|
198
|
-
const result = results[i];
|
|
199
|
-
if (result.status !== 'success')
|
|
200
|
-
continue;
|
|
201
|
-
const call = toolCalls.get(result.toolCallId);
|
|
202
|
-
if (!call)
|
|
203
|
-
continue;
|
|
204
|
-
if (!MUTATING_TOOLS.has(call.tool))
|
|
205
|
-
continue;
|
|
206
|
-
// Collect file_mutation events whose toolCallId matches. We do NOT
|
|
207
|
-
// window by event index: M1 emits the file_mutation alongside the
|
|
208
|
-
// tool_result, but a future iteration that emits them earlier
|
|
209
|
-
// (e.g. mid-stream for multi_edit) still maps correctly.
|
|
210
|
-
const mutations = [];
|
|
211
|
-
for (const event of events) {
|
|
212
|
-
if (event.type !== 'file_mutation')
|
|
213
|
-
continue;
|
|
214
|
-
if (event.toolCallId !== result.toolCallId)
|
|
215
|
-
continue;
|
|
216
|
-
if (typeof event.path !== 'string')
|
|
217
|
-
continue;
|
|
218
|
-
if (event.operation !== 'create' &&
|
|
219
|
-
event.operation !== 'update' &&
|
|
220
|
-
event.operation !== 'delete' &&
|
|
221
|
-
event.operation !== 'move') {
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
mutations.push({
|
|
225
|
-
toolCallId: result.toolCallId,
|
|
226
|
-
path: event.path,
|
|
227
|
-
operation: event.operation,
|
|
228
|
-
beforeHash: typeof event.beforeHash === 'string' ? event.beforeHash : undefined,
|
|
229
|
-
afterHash: typeof event.afterHash === 'string' ? event.afterHash : undefined,
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
return {
|
|
233
|
-
toolCallId: result.toolCallId,
|
|
234
|
-
resultIndex: result.index,
|
|
235
|
-
tool: call.tool,
|
|
236
|
-
mutations,
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
return null;
|
|
240
|
-
}
|
|
241
|
-
function planReverts(root, mutations) {
|
|
242
|
-
const steps = [];
|
|
243
|
-
const unsafe = [];
|
|
244
|
-
for (const mutation of mutations) {
|
|
245
|
-
const abs = resolve(root, mutation.path);
|
|
246
|
-
switch (mutation.operation) {
|
|
247
|
-
case 'create': {
|
|
248
|
-
if (!existsSync(abs)) {
|
|
249
|
-
// Already gone — nothing to undo for this entry.
|
|
250
|
-
continue;
|
|
251
|
-
}
|
|
252
|
-
const current = readFileSync(abs, 'utf8');
|
|
253
|
-
const currentHash = hashContent(current);
|
|
254
|
-
if (!mutation.afterHash || currentHash !== mutation.afterHash) {
|
|
255
|
-
unsafe.push(`${mutation.path}: created by Pugi, then modified by the user — refusing to delete uncommitted work`);
|
|
256
|
-
continue;
|
|
257
|
-
}
|
|
258
|
-
steps.push({
|
|
259
|
-
path: mutation.path,
|
|
260
|
-
operation: 'create',
|
|
261
|
-
beforeHash: mutation.afterHash,
|
|
262
|
-
afterHash: undefined,
|
|
263
|
-
});
|
|
264
|
-
break;
|
|
265
|
-
}
|
|
266
|
-
case 'update': {
|
|
267
|
-
if (!existsSync(abs)) {
|
|
268
|
-
unsafe.push(`${mutation.path}: file expected to exist for update revert, not found`);
|
|
269
|
-
continue;
|
|
270
|
-
}
|
|
271
|
-
const current = readFileSync(abs, 'utf8');
|
|
272
|
-
const currentHash = hashContent(current);
|
|
273
|
-
if (!mutation.afterHash || currentHash !== mutation.afterHash) {
|
|
274
|
-
unsafe.push(`${mutation.path}: updated by Pugi, then modified by the user — refusing to overwrite uncommitted work`);
|
|
275
|
-
continue;
|
|
276
|
-
}
|
|
277
|
-
const headContent = readHead(root, mutation.path);
|
|
278
|
-
if (headContent === null) {
|
|
279
|
-
unsafe.push(`${mutation.path}: no git HEAD version available for revert`);
|
|
280
|
-
continue;
|
|
281
|
-
}
|
|
282
|
-
if (mutation.beforeHash && hashContent(headContent) !== mutation.beforeHash) {
|
|
283
|
-
unsafe.push(`${mutation.path}: git HEAD differs from the pre-mutation hash — refusing to restore an unverifiable version`);
|
|
284
|
-
continue;
|
|
285
|
-
}
|
|
286
|
-
steps.push({
|
|
287
|
-
path: mutation.path,
|
|
288
|
-
operation: 'update',
|
|
289
|
-
beforeHash: mutation.afterHash,
|
|
290
|
-
afterHash: mutation.beforeHash,
|
|
291
|
-
restoreContent: headContent,
|
|
292
|
-
});
|
|
293
|
-
break;
|
|
294
|
-
}
|
|
295
|
-
case 'delete': {
|
|
296
|
-
const headContent = readHead(root, mutation.path);
|
|
297
|
-
if (headContent === null) {
|
|
298
|
-
unsafe.push(`${mutation.path}: deleted file has no git HEAD version, cannot restore`);
|
|
299
|
-
continue;
|
|
300
|
-
}
|
|
301
|
-
if (mutation.beforeHash && hashContent(headContent) !== mutation.beforeHash) {
|
|
302
|
-
unsafe.push(`${mutation.path}: git HEAD differs from the pre-deletion hash, cannot restore safely`);
|
|
303
|
-
continue;
|
|
304
|
-
}
|
|
305
|
-
steps.push({
|
|
306
|
-
path: mutation.path,
|
|
307
|
-
operation: 'delete',
|
|
308
|
-
beforeHash: undefined,
|
|
309
|
-
afterHash: mutation.beforeHash,
|
|
310
|
-
restoreContent: headContent,
|
|
311
|
-
});
|
|
312
|
-
break;
|
|
313
|
-
}
|
|
314
|
-
case 'move':
|
|
315
|
-
// Move reverts are not in M1 scope — the tool layer does not yet
|
|
316
|
-
// emit `move` mutations. Flag as unsafe so a future operator who
|
|
317
|
-
// hits this gets a clear failure instead of partial revert.
|
|
318
|
-
unsafe.push(`${mutation.path}: move undo is not supported in M1`);
|
|
319
|
-
break;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
if (unsafe.length > 0) {
|
|
323
|
-
return {
|
|
324
|
-
aborted: true,
|
|
325
|
-
reason: 'one or more files are unsafe to revert',
|
|
326
|
-
unsafe,
|
|
327
|
-
steps: [],
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
return { aborted: false, steps };
|
|
331
|
-
}
|
|
332
|
-
function executeRevert(root, step) {
|
|
333
|
-
const abs = resolve(root, step.path);
|
|
334
|
-
if (step.operation === 'create') {
|
|
335
|
-
// We previously created the file. Reverting means deleting it.
|
|
336
|
-
unlinkSync(abs);
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
if (step.restoreContent === undefined) {
|
|
340
|
-
throw new Error(`internal: restoreContent missing for ${step.path}`);
|
|
341
|
-
}
|
|
342
|
-
// Atomic write via tmp+rename — same pattern as file-tools.writeTool.
|
|
343
|
-
const tmp = `${abs}.pugi-undo-${Date.now()}`;
|
|
344
|
-
writeFileSync(tmp, step.restoreContent, { encoding: 'utf8', mode: 0o600 });
|
|
345
|
-
renameSync(tmp, abs);
|
|
346
|
-
}
|
|
347
|
-
function readHead(root, path) {
|
|
348
|
-
try {
|
|
349
|
-
const out = execFileSync('git', ['show', `HEAD:${path}`], {
|
|
350
|
-
cwd: root,
|
|
351
|
-
encoding: 'utf8',
|
|
352
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
353
|
-
maxBuffer: 64 * 1024 * 1024,
|
|
354
|
-
});
|
|
355
|
-
return out;
|
|
356
|
-
}
|
|
357
|
-
catch {
|
|
358
|
-
return null;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
//# sourceMappingURL=undo.js.map
|
|
@@ -1,289 +0,0 @@
|
|
|
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
|