@pugi/cli 0.1.0-beta.7 → 0.1.0-beta.87
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +96 -0
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/assets/pugi-prozr2-mascot.ansi +9 -0
- package/bin/run.js +33 -1
- package/dist/commands/deploy.js +40 -40
- package/dist/commands/flatten.js +191 -0
- package/dist/commands/jobs-watch.js +201 -0
- package/dist/commands/jobs.js +42 -27
- package/dist/commands/smoke.js +133 -0
- package/dist/core/agent-progress/cleanup.js +134 -0
- package/dist/core/agent-progress/schema.js +144 -0
- package/dist/core/agent-progress/writer.js +101 -0
- package/dist/core/agents/adaptive-router.js +330 -0
- package/dist/core/agents/query-decomposer.js +297 -0
- package/dist/core/agents/registry.js +2 -2
- package/dist/core/approvals/shortcut-resolver.js +98 -0
- package/dist/core/artifact-chain/dispatcher.js +148 -0
- package/dist/core/artifact-chain/exporter.js +164 -0
- package/dist/core/artifact-chain/state.js +243 -0
- package/dist/core/artifact-chain/steps.js +169 -0
- package/dist/core/ask-user/question.js +92 -0
- package/dist/core/audit/audit-trail.js +275 -0
- package/dist/core/auth/ensure-authenticated.js +129 -0
- package/dist/core/auth/env-provider.js +238 -0
- package/dist/core/auto-open-browser.js +4 -4
- package/dist/core/auto-update/channels.js +122 -0
- package/dist/core/auto-update/checker.js +241 -0
- package/dist/core/auto-update/state.js +235 -0
- package/dist/core/bare-mode/index.js +107 -0
- package/dist/core/bash/redirect.js +281 -0
- package/dist/core/bash-classifier.js +436 -40
- package/dist/core/checkpoint/resumer.js +149 -0
- package/dist/core/checkpoint/rewinder.js +291 -0
- package/dist/core/checkpoints/shadow-git.js +670 -0
- package/dist/core/citations/parser.js +109 -0
- package/dist/core/classifier/yolo-classifier.js +88 -0
- package/dist/core/codegraph/decision-store.js +248 -0
- package/dist/core/codegraph/detect-repo.js +459 -0
- package/dist/core/codegraph/install.js +134 -0
- package/dist/core/codegraph/offer-hook.js +220 -0
- package/dist/core/compact/auto-trigger.js +96 -0
- package/dist/core/compact/buffer-rewriter.js +115 -0
- package/dist/core/compact/summarizer.js +208 -0
- package/dist/core/compact/token-counter.js +108 -0
- package/dist/core/consensus/anvil-fanout.js +25 -25
- package/dist/core/consensus/diff-capture.js +121 -12
- package/dist/core/consensus/rubric.js +21 -21
- package/dist/core/context/builder.js +6 -6
- package/dist/core/context/compaction-events.js +8 -8
- package/dist/core/context/compaction.js +31 -31
- package/dist/core/context/index.js +15 -8
- package/dist/core/context/invariants.js +51 -51
- package/dist/core/context/markdown-loader.js +28 -10
- package/dist/core/context/markdown-traverse.js +255 -0
- package/dist/core/context/pugiignore.js +41 -41
- package/dist/core/context/repo-skeleton.js +37 -37
- package/dist/core/context/tool-eviction.js +55 -0
- package/dist/core/context/watcher.js +32 -32
- package/dist/core/context/working-set.js +23 -23
- package/dist/core/coordinator/agent-tools.js +77 -0
- package/dist/core/coordinator/agent-toolset.js +65 -0
- package/dist/core/coordinator/fsm.js +73 -0
- package/dist/core/coordinator/mode-fsm.js +70 -0
- package/dist/core/cost/rate-card.js +129 -0
- package/dist/core/cost/tracker.js +221 -0
- package/dist/core/credentials.js +12 -12
- package/dist/core/cron/scheduler.js +138 -0
- package/dist/core/denial-tracking/index.js +8 -0
- package/dist/core/denial-tracking/state.js +264 -0
- package/dist/core/diagnostics/probe-runner.js +93 -0
- package/dist/core/diagnostics/probes/api.js +46 -0
- package/dist/core/diagnostics/probes/auth.js +93 -0
- package/dist/core/diagnostics/probes/bare-mode.js +42 -0
- package/dist/core/diagnostics/probes/cli-version.js +127 -0
- package/dist/core/diagnostics/probes/config.js +72 -0
- package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
- package/dist/core/diagnostics/probes/disk.js +81 -0
- package/dist/core/diagnostics/probes/engine-live.js +46 -0
- package/dist/core/diagnostics/probes/git.js +65 -0
- package/dist/core/diagnostics/probes/hooks.js +118 -0
- package/dist/core/diagnostics/probes/mcp.js +75 -0
- package/dist/core/diagnostics/probes/node.js +59 -0
- package/dist/core/diagnostics/probes/pnpm.js +36 -0
- package/dist/core/diagnostics/probes/pugi-md.js +89 -0
- package/dist/core/diagnostics/probes/sandbox.js +40 -0
- package/dist/core/diagnostics/probes/session.js +74 -0
- package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
- package/dist/core/diagnostics/probes/workspace.js +63 -0
- package/dist/core/diagnostics/types.js +70 -0
- package/dist/core/dispatch/cache-cleanup.js +197 -0
- package/dist/core/dispatch/cache-handoff.js +295 -0
- package/dist/core/edits/apply-patch-layer-e.js +189 -0
- package/dist/core/edits/dispatch.js +293 -7
- package/dist/core/edits/format-matrix.js +26 -0
- package/dist/core/edits/fuzzy-ladder.js +650 -0
- package/dist/core/edits/index.js +3 -1
- package/dist/core/edits/journal.js +199 -0
- package/dist/core/edits/layer-a-apply.js +15 -15
- package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
- package/dist/core/edits/layer-b-apply.js +9 -9
- package/dist/core/edits/layer-c-apply.js +6 -6
- package/dist/core/edits/layer-d-ast.js +557 -14
- package/dist/core/edits/marker-parser.js +12 -12
- package/dist/core/edits/security-gate.js +27 -27
- package/dist/core/edits/verify-hook.js +273 -0
- package/dist/core/edits/worktree.js +322 -0
- package/dist/core/engine/anvil-client.js +140 -26
- package/dist/core/engine/auto-compact.js +179 -0
- package/dist/core/engine/budgets.js +186 -0
- package/dist/core/engine/context-prefix.js +155 -0
- package/dist/core/engine/index.js +1 -1
- package/dist/core/engine/intensity.js +158 -0
- package/dist/core/engine/intent.js +260 -0
- package/dist/core/engine/native-pugi.js +1295 -227
- package/dist/core/engine/prompts.js +134 -16
- package/dist/core/engine/strip-internal-fields.js +124 -0
- package/dist/core/engine/tool-bridge.js +1295 -59
- package/dist/core/evaluation/golden-dataset.js +293 -0
- package/dist/core/feedback/queue.js +177 -0
- package/dist/core/feedback/submitter.js +145 -0
- package/dist/core/file-cache.js +113 -1
- package/dist/core/flatten/flatten-repo.js +439 -0
- package/dist/core/format/osc8-link.js +28 -0
- package/dist/core/hook-chains.js +392 -0
- package/dist/core/hooks/citation-verify-hook.js +138 -0
- package/dist/core/hooks/citation-verify.js +112 -0
- package/dist/core/hooks/events.js +44 -0
- package/dist/core/hooks/index.js +15 -0
- package/dist/core/hooks/registry.js +213 -0
- package/dist/core/hooks/runner.js +236 -0
- package/dist/core/hooks/v2/event-emitter.js +115 -0
- package/dist/core/hooks/v2/executor.js +282 -0
- package/dist/core/hooks/v2/index.js +25 -0
- package/dist/core/hooks/v2/lifecycle.js +104 -0
- package/dist/core/hooks/v2/loader.js +216 -0
- package/dist/core/hooks/v2/matcher.js +125 -0
- package/dist/core/hooks/v2/trust.js +143 -0
- package/dist/core/hooks/v2/types.js +86 -0
- package/dist/core/image/renderer.js +71 -0
- package/dist/core/init/detector.js +582 -0
- package/dist/core/init/template-renderer.js +242 -0
- package/dist/core/jobs/registry.js +18 -18
- package/dist/core/ledger/results-tsv.js +142 -0
- package/dist/core/log-discipline/stdout-redirect.js +51 -0
- package/dist/core/lsp/cache.js +105 -0
- package/dist/core/lsp/client.js +776 -0
- package/dist/core/lsp/language-detect.js +66 -0
- package/dist/core/lsp/post-edit-diagnostics.js +171 -0
- package/dist/core/lsp/symbol-tools.js +372 -0
- package/dist/core/mcp/client.js +97 -28
- package/dist/core/mcp/http-server.js +553 -0
- package/dist/core/mcp/orchestrator-tools.js +662 -0
- package/dist/core/mcp/permission.js +190 -0
- package/dist/core/mcp/registry.js +39 -17
- package/dist/core/mcp/server-tools.js +219 -0
- package/dist/core/mcp/server.js +397 -0
- package/dist/core/mcp/trust.js +10 -10
- package/dist/core/memory/dual-write.js +416 -0
- package/dist/core/memory/passive-extract.js +130 -0
- package/dist/core/memory/phase1-kinds.js +20 -0
- package/dist/core/memory/secret-scanner.js +304 -0
- package/dist/core/memory-sync/queue.js +170 -0
- package/dist/core/metrics/extract.js +113 -0
- package/dist/core/modes/roo-modes.js +68 -0
- package/dist/core/onboarding/ensure-initialized.js +133 -0
- package/dist/core/onboarding/marker.js +111 -0
- package/dist/core/onboarding/telemetry-state.js +108 -0
- package/dist/core/output-style/presets.js +176 -0
- package/dist/core/output-style/state.js +185 -0
- package/dist/core/path-security.js +287 -5
- package/dist/core/permission.js +82 -22
- package/dist/core/permissions/auto-classifier.js +124 -0
- package/dist/core/permissions/bash-parser.js +371 -0
- package/dist/core/permissions/circuit-breaker.js +83 -0
- package/dist/core/permissions/constrained-edit.js +91 -0
- package/dist/core/permissions/gate.js +278 -0
- package/dist/core/permissions/index.js +20 -0
- package/dist/core/permissions/mode.js +174 -0
- package/dist/core/permissions/network-egress.js +137 -0
- package/dist/core/permissions/state.js +241 -0
- package/dist/core/permissions/tool-class.js +93 -0
- package/dist/core/plan-mode/ui-state.js +51 -0
- package/dist/core/plans/plan-artifact.js +721 -0
- package/dist/core/policy-limits/etag-store.js +122 -0
- package/dist/core/prd-check/parser.js +215 -0
- package/dist/core/prd-check/reporter.js +127 -0
- package/dist/core/prd-check/session-review.js +557 -0
- package/dist/core/prd-check/verifiers.js +223 -0
- package/dist/core/prompt-cache/client-cache.js +99 -0
- package/dist/core/prompts/assembly.js +29 -0
- package/dist/core/prompts/registry.js +364 -0
- package/dist/core/pugi-md/cc-compat-rules.js +735 -0
- package/dist/core/pugi-md/context-injector.js +76 -0
- package/dist/core/pugi-md/walk-up.js +207 -0
- package/dist/core/python/uv-installer.js +270 -0
- package/dist/core/python/uv-resolver.js +83 -0
- package/dist/core/rate-limit/narrator.js +146 -0
- package/dist/core/recipes/cli-types.js +20 -0
- package/dist/core/recipes/loader.js +103 -0
- package/dist/core/recipes/runner.js +345 -0
- package/dist/core/recipes/schema.js +587 -0
- package/dist/core/release-notes/parser.js +241 -0
- package/dist/core/release-notes/state.js +116 -0
- package/dist/core/repl/ask.js +37 -37
- package/dist/core/repl/cancellation.js +26 -26
- package/dist/core/repl/cap-warning.js +4 -4
- package/dist/core/repl/clipboard-read.js +11 -11
- package/dist/core/repl/dispatch-fsm.js +12 -12
- package/dist/core/repl/history-search.js +15 -15
- package/dist/core/repl/history.js +28 -18
- package/dist/core/repl/kill-ring.js +5 -5
- package/dist/core/repl/model-pricing.js +135 -0
- package/dist/core/repl/privacy-banner.js +22 -22
- package/dist/core/repl/session.js +2157 -214
- package/dist/core/repl/slash-commands.js +533 -40
- package/dist/core/repl/store/index.js +1 -1
- package/dist/core/repl/store/jsonl-log.js +22 -22
- package/dist/core/repl/store/lockfile.js +10 -10
- package/dist/core/repl/store/session-store.js +136 -107
- package/dist/core/repl/store/types.js +15 -15
- package/dist/core/repl/store/uuid-v7.js +12 -12
- package/dist/core/repl/workspace-context.js +43 -21
- package/dist/core/repo-map/build.js +125 -0
- package/dist/core/repo-map/cache.js +185 -0
- package/dist/core/repo-map/extractor.js +254 -0
- package/dist/core/repo-map/formatter.js +145 -0
- package/dist/core/repo-map/page-rank.js +105 -0
- package/dist/core/repo-map/scanner.js +211 -0
- package/dist/core/retry-budget/budget.js +284 -0
- package/dist/core/retry-budget/index.js +5 -0
- package/dist/core/retry-budget/retry-cap.js +74 -0
- package/dist/core/routing/lead-worker.js +43 -0
- package/dist/core/routing/pre-flight-estimator.js +108 -0
- package/dist/core/runs/run-tree.js +103 -0
- package/dist/core/security/injection-scanner.js +367 -0
- package/dist/core/security/output-filter.js +418 -0
- package/dist/core/session/env-file.js +105 -0
- package/dist/core/session/section-budgets.js +140 -0
- package/dist/core/session.js +92 -0
- package/dist/core/settings.js +286 -5
- package/dist/core/share/formatter.js +271 -0
- package/dist/core/share/redactor.js +221 -0
- package/dist/core/share/uploader.js +267 -0
- package/dist/core/skills/defaults.js +457 -0
- package/dist/core/skills/loader.js +22 -22
- package/dist/core/skills/sources.js +27 -27
- package/dist/core/smoke/headless-driver.js +174 -0
- package/dist/core/smoke/orchestrator.js +194 -0
- package/dist/core/smoke/runner.js +238 -0
- package/dist/core/smoke/scenario-parser.js +316 -0
- package/dist/core/statusline.js +99 -0
- package/dist/core/subagents/dispatcher-real.js +600 -0
- package/dist/core/subagents/dispatcher.js +132 -43
- package/dist/core/subagents/index.js +19 -6
- package/dist/core/subagents/isolation-matrix.js +213 -0
- package/dist/core/subagents/spawn.js +19 -4
- package/dist/core/telemetry/emitter.js +229 -0
- package/dist/core/telemetry/queue.js +251 -0
- package/dist/core/theme/context.js +91 -0
- package/dist/core/theme/presets.js +228 -0
- package/dist/core/theme/state.js +181 -0
- package/dist/core/todos/invariant.js +10 -0
- package/dist/core/todos/state.js +177 -0
- package/dist/core/tool-schema/compressor.js +89 -0
- package/dist/core/transport/version-interceptor.js +166 -0
- package/dist/core/trust.js +2 -2
- package/dist/core/tui/thinking-block.js +64 -0
- package/dist/core/vim/keymap.js +288 -0
- package/dist/core/vim/state.js +92 -0
- package/dist/core/watch-markers/marker-watcher.js +133 -0
- package/dist/core/worktree-manager/cleanup.js +123 -0
- package/dist/core/worktree-manager/manager.js +303 -0
- package/dist/index.js +28 -0
- package/dist/runtime/bootstrap.js +190 -0
- package/dist/runtime/cli.js +4162 -488
- package/dist/runtime/commands/agents.js +30 -30
- package/dist/runtime/commands/budget.js +5 -5
- package/dist/runtime/commands/cancel.js +231 -0
- package/dist/runtime/commands/chain.js +489 -0
- package/dist/runtime/commands/codegraph-status.js +227 -0
- package/dist/runtime/commands/compact.js +297 -0
- package/dist/runtime/commands/config.js +32 -32
- package/dist/runtime/commands/cost.js +199 -0
- package/dist/runtime/commands/delegate.js +244 -13
- package/dist/runtime/commands/dispatch.js +126 -0
- package/dist/runtime/commands/doctor.js +579 -0
- package/dist/runtime/commands/feedback.js +184 -0
- package/dist/runtime/commands/hooks.js +184 -0
- package/dist/runtime/commands/init.js +254 -0
- package/dist/runtime/commands/lsp.js +368 -0
- package/dist/runtime/commands/mcp.js +879 -0
- package/dist/runtime/commands/memory.js +582 -0
- package/dist/runtime/commands/model.js +237 -0
- package/dist/runtime/commands/onboarding.js +275 -0
- package/dist/runtime/commands/patch.js +128 -0
- package/dist/runtime/commands/permissions.js +112 -0
- package/dist/runtime/commands/plan.js +143 -0
- package/dist/runtime/commands/prd-check.js +285 -0
- package/dist/runtime/commands/privacy.js +17 -17
- package/dist/runtime/commands/recipe.js +325 -0
- package/dist/runtime/commands/redo-blob-store.js +92 -0
- package/dist/runtime/commands/redo.js +361 -0
- package/dist/runtime/commands/release-notes.js +229 -0
- package/dist/runtime/commands/repo-map.js +95 -0
- package/dist/runtime/commands/report.js +299 -0
- package/dist/runtime/commands/resume.js +118 -0
- package/dist/runtime/commands/review-consensus.js +68 -53
- package/dist/runtime/commands/rewind.js +333 -0
- package/dist/runtime/commands/roster.js +14 -14
- package/dist/runtime/commands/sessions.js +163 -0
- package/dist/runtime/commands/share.js +316 -0
- package/dist/runtime/commands/skills.js +31 -31
- package/dist/runtime/commands/status.js +186 -0
- package/dist/runtime/commands/stickers.js +82 -0
- package/dist/runtime/commands/style.js +194 -0
- package/dist/runtime/commands/theme.js +196 -0
- package/dist/runtime/commands/undo.js +54 -22
- package/dist/runtime/commands/update.js +289 -0
- package/dist/runtime/commands/vim.js +140 -0
- package/dist/runtime/commands/worktree.js +177 -0
- package/dist/runtime/commands/worktrees.js +155 -0
- package/dist/runtime/headless-repl.js +195 -0
- package/dist/runtime/headless.js +543 -0
- package/dist/runtime/load-hooks-or-exit.js +71 -0
- package/dist/runtime/plan-decompose.js +531 -0
- package/dist/runtime/update-check.js +28 -28
- package/dist/runtime/version.js +65 -0
- package/dist/skills/bundled/batch.js +617 -0
- package/dist/skills/bundled/index.js +45 -0
- package/dist/skills/bundled/loop.js +358 -0
- package/dist/skills/bundled/remember.js +383 -0
- package/dist/skills/bundled/simplify.js +289 -0
- package/dist/skills/bundled/skillify.js +373 -0
- package/dist/skills/bundled/stuck.js +558 -0
- package/dist/skills/bundled/verify.js +439 -0
- package/dist/testing/vcr.js +486 -0
- package/dist/tools/agent-tool.js +229 -0
- package/dist/tools/apply-patch.js +556 -0
- package/dist/tools/ask-user-question.js +222 -0
- package/dist/tools/ask-user.js +115 -0
- package/dist/tools/bash.js +623 -45
- package/dist/tools/brief.js +224 -0
- package/dist/tools/enter-worktree.js +250 -0
- package/dist/tools/exit-worktree.js +147 -0
- package/dist/tools/file-tools.js +161 -44
- package/dist/tools/lsp-tools.js +189 -0
- package/dist/tools/mcp-tool.js +260 -0
- package/dist/tools/multi-edit.js +361 -0
- package/dist/tools/powershell.js +268 -0
- package/dist/tools/registry.js +85 -0
- package/dist/tools/skill-tool.js +96 -0
- package/dist/tools/sleep.js +99 -0
- package/dist/tools/synthetic-output.js +133 -0
- package/dist/tools/tasks.js +208 -0
- package/dist/tools/todo-write.js +184 -0
- package/dist/tools/verify-plan-execution.js +295 -0
- package/dist/tools/web-fetch-injection-scanner.js +207 -0
- package/dist/tools/web-fetch.js +195 -10
- package/dist/tools/web-search.js +458 -0
- package/dist/tui/agent-progress-card.js +111 -0
- package/dist/tui/agent-tree.js +11 -1
- package/dist/tui/ask-modal.js +14 -14
- package/dist/tui/ask-user-question-prompt.js +203 -0
- package/dist/tui/compact-banner.js +81 -0
- package/dist/tui/conversation-pane.js +85 -11
- package/dist/tui/cost-table.js +111 -0
- package/dist/tui/device-flow.js +2 -2
- package/dist/tui/doctor-table.js +46 -0
- package/dist/tui/feedback-prompt.js +156 -0
- package/dist/tui/input-box.js +247 -32
- package/dist/tui/login-picker.js +3 -3
- package/dist/tui/markdown-render.js +6 -6
- package/dist/tui/onboarding-wizard.js +240 -0
- package/dist/tui/permissions-picker.js +86 -0
- package/dist/tui/render.js +35 -0
- package/dist/tui/repl-render.js +332 -54
- package/dist/tui/repl-splash-art.js +16 -16
- package/dist/tui/repl-splash-mascot.js +48 -24
- package/dist/tui/repl-splash.js +22 -22
- package/dist/tui/repl.js +124 -44
- package/dist/tui/slash-palette.js +6 -6
- package/dist/tui/splash.js +2 -2
- package/dist/tui/status-bar.js +109 -31
- package/dist/tui/status-table.js +7 -0
- package/dist/tui/stickers-art.js +136 -0
- package/dist/tui/style-table.js +28 -0
- package/dist/tui/theme-table.js +29 -0
- package/dist/tui/thinking-spinner.js +123 -0
- package/dist/tui/tool-stream-pane.js +53 -4
- package/dist/tui/update-banner.js +27 -2
- package/dist/tui/vim-input.js +267 -0
- package/dist/tui/welcome-banner.js +107 -0
- package/dist/tui/welcome-data.js +293 -0
- package/dist/tui/workspace-context.js +2 -2
- package/docs/examples/codegraph.mcp.json +10 -0
- package/package.json +23 -6
- package/test/scenarios/codegen-create-file.scenario.txt +13 -0
- package/test/scenarios/compact-force.scenario.txt +11 -0
- package/test/scenarios/identity.scenario.txt +11 -0
- package/test/scenarios/persona-handoff.scenario.txt +11 -0
- package/test/scenarios/walkback.scenario.txt +12 -0
- package/dist/core/engine/compaction-hook.js +0 -154
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* — `pugi style` top-level command + REPL slash
|
|
3
|
+
* companion.
|
|
4
|
+
*
|
|
5
|
+
* Operator surface:
|
|
6
|
+
*
|
|
7
|
+
* pugi style Show active preset + table.
|
|
8
|
+
* pugi style <name> Switch workspace preset (current cwd).
|
|
9
|
+
* pugi style <name> --persist Switch + also write user default.
|
|
10
|
+
* pugi style --reset Clear workspace override → back to default.
|
|
11
|
+
* pugi style --reset --user Also clear the user default.
|
|
12
|
+
* pugi style --list Print the catalogue (no flip).
|
|
13
|
+
* pugi style --json Structured envelope variant.
|
|
14
|
+
*
|
|
15
|
+
* The same runner powers `/style` from inside the REPL. The REPL
|
|
16
|
+
* dispatcher (see `core/repl/session.ts`) routes through here so the
|
|
17
|
+
* two surfaces stay single-sourced — operators trained on one read
|
|
18
|
+
* the same payload + table on the other.
|
|
19
|
+
*
|
|
20
|
+
* Exit codes:
|
|
21
|
+
* 0 — show / switch / reset all succeed
|
|
22
|
+
* 1 — unknown preset slug (returned BEFORE any write)
|
|
23
|
+
* 2 — conflicting flags (e.g. `--reset` with a positional slug)
|
|
24
|
+
*
|
|
25
|
+
* The exit codes are surfaced through `process.exitCode` by the
|
|
26
|
+
* dispatcher in `cli.ts` — this module returns a structured payload
|
|
27
|
+
* + writes via the injected `writeOutput`. Throwing is reserved for
|
|
28
|
+
* truly unexpected errors (fs permissions etc.); the spec hooks the
|
|
29
|
+
* happy + sad paths through `writeOutput` shape, not via try/catch
|
|
30
|
+
* on the throw.
|
|
31
|
+
*/
|
|
32
|
+
import { DEFAULT_OUTPUT_STYLE, isOutputStyleSlug, OUTPUT_STYLE_SLUGS, renderStyleTable, } from '../../core/output-style/presets.js';
|
|
33
|
+
import { clearUserOutputStyle, clearWorkspaceOutputStyle, resolveOutputStyle, setUserOutputStyle, setWorkspaceOutputStyle, } from '../../core/output-style/state.js';
|
|
34
|
+
/**
|
|
35
|
+
* Entry point. Parses `args`, applies the operation, emits the
|
|
36
|
+
* payload + text via `ctx.writeOutput`, and returns the exit code the
|
|
37
|
+
* dispatcher should hand back to the shell.
|
|
38
|
+
*/
|
|
39
|
+
export async function runStyleCommand(args, ctx) {
|
|
40
|
+
const flags = parseFlags(args);
|
|
41
|
+
// Reset path
|
|
42
|
+
if (flags.reset) {
|
|
43
|
+
if (flags.slug !== null) {
|
|
44
|
+
const payload = buildPayload({
|
|
45
|
+
status: 'invalid_flags',
|
|
46
|
+
ctx,
|
|
47
|
+
message: '/style --reset cannot be combined with a preset name. Use one or the other.',
|
|
48
|
+
});
|
|
49
|
+
ctx.writeOutput(payload, payload.message);
|
|
50
|
+
return 2;
|
|
51
|
+
}
|
|
52
|
+
if (flags.persist) {
|
|
53
|
+
const payload = buildPayload({
|
|
54
|
+
status: 'invalid_flags',
|
|
55
|
+
ctx,
|
|
56
|
+
message: '/style --reset cannot be combined with --persist. Use --reset --user to also clear the user default.',
|
|
57
|
+
});
|
|
58
|
+
ctx.writeOutput(payload, payload.message);
|
|
59
|
+
return 2;
|
|
60
|
+
}
|
|
61
|
+
clearWorkspaceOutputStyle({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
|
|
62
|
+
if (flags.user) {
|
|
63
|
+
clearUserOutputStyle({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
|
|
64
|
+
}
|
|
65
|
+
const payload = buildPayload({
|
|
66
|
+
status: 'reset',
|
|
67
|
+
ctx,
|
|
68
|
+
message: flags.user
|
|
69
|
+
? `Cleared workspace + user output style. Active: ${DEFAULT_OUTPUT_STYLE} (default).`
|
|
70
|
+
: `Cleared workspace output style. Active: ${describeActive(ctx)}.`,
|
|
71
|
+
});
|
|
72
|
+
ctx.writeOutput(payload, payload.message);
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
// List path
|
|
76
|
+
if (flags.list && flags.slug === null) {
|
|
77
|
+
const resolved = resolveOutputStyle({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
|
|
78
|
+
const payload = buildPayload({
|
|
79
|
+
status: 'listed',
|
|
80
|
+
ctx,
|
|
81
|
+
message: renderStyleTable(resolved.slug),
|
|
82
|
+
});
|
|
83
|
+
ctx.writeOutput(payload, payload.message);
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
// Switch path
|
|
87
|
+
if (flags.slug !== null) {
|
|
88
|
+
if (!isOutputStyleSlug(flags.slug)) {
|
|
89
|
+
const payload = buildPayload({
|
|
90
|
+
status: 'invalid_slug',
|
|
91
|
+
ctx,
|
|
92
|
+
attemptedSlug: flags.slug,
|
|
93
|
+
message: `Unknown style "${flags.slug}". Try one of: ${OUTPUT_STYLE_SLUGS.join(', ')}.`,
|
|
94
|
+
});
|
|
95
|
+
ctx.writeOutput(payload, payload.message);
|
|
96
|
+
return 1;
|
|
97
|
+
}
|
|
98
|
+
const before = resolveOutputStyle({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
|
|
99
|
+
setWorkspaceOutputStyle(flags.slug, { workspaceRoot: ctx.workspaceRoot, env: ctx.env });
|
|
100
|
+
if (flags.persist) {
|
|
101
|
+
setUserOutputStyle(flags.slug, { workspaceRoot: ctx.workspaceRoot, env: ctx.env });
|
|
102
|
+
}
|
|
103
|
+
const tail = flags.persist ? ' (workspace + user default)' : ' (workspace)';
|
|
104
|
+
const payload = buildPayload({
|
|
105
|
+
status: 'switched',
|
|
106
|
+
ctx,
|
|
107
|
+
previous: before.slug,
|
|
108
|
+
persistedToUser: flags.persist,
|
|
109
|
+
message: `Output style → ${flags.slug}${tail}. Was: ${before.slug} (${before.source}).`,
|
|
110
|
+
});
|
|
111
|
+
ctx.writeOutput(payload, payload.message);
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
// Show path (no args)
|
|
115
|
+
const resolved = resolveOutputStyle({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
|
|
116
|
+
const banner = `Active output style: ${resolved.slug} (${resolved.source})`;
|
|
117
|
+
const table = renderStyleTable(resolved.slug);
|
|
118
|
+
const payload = buildPayload({
|
|
119
|
+
status: 'show',
|
|
120
|
+
ctx,
|
|
121
|
+
message: `${banner}\n\n${table}`,
|
|
122
|
+
});
|
|
123
|
+
ctx.writeOutput(payload, payload.message);
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
function describeActive(ctx) {
|
|
127
|
+
const resolved = resolveOutputStyle({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
|
|
128
|
+
return `${resolved.slug} (${resolved.source})`;
|
|
129
|
+
}
|
|
130
|
+
function buildPayload(args) {
|
|
131
|
+
const resolved = resolveOutputStyle({ workspaceRoot: args.ctx.workspaceRoot, env: args.ctx.env });
|
|
132
|
+
const payload = {
|
|
133
|
+
command: 'style',
|
|
134
|
+
status: args.status,
|
|
135
|
+
active: resolved.slug,
|
|
136
|
+
source: resolved.source,
|
|
137
|
+
presets: OUTPUT_STYLE_SLUGS,
|
|
138
|
+
message: args.message,
|
|
139
|
+
};
|
|
140
|
+
if (args.previous !== undefined)
|
|
141
|
+
payload.previous = args.previous;
|
|
142
|
+
if (args.persistedToUser !== undefined)
|
|
143
|
+
payload.persistedToUser = args.persistedToUser;
|
|
144
|
+
if (args.attemptedSlug !== undefined)
|
|
145
|
+
payload.attemptedSlug = args.attemptedSlug;
|
|
146
|
+
return payload;
|
|
147
|
+
}
|
|
148
|
+
function parseFlags(args) {
|
|
149
|
+
const flags = {
|
|
150
|
+
slug: null,
|
|
151
|
+
persist: false,
|
|
152
|
+
reset: false,
|
|
153
|
+
user: false,
|
|
154
|
+
list: false,
|
|
155
|
+
};
|
|
156
|
+
for (const arg of args) {
|
|
157
|
+
if (arg === '--persist')
|
|
158
|
+
flags.persist = true;
|
|
159
|
+
else if (arg === '--reset')
|
|
160
|
+
flags.reset = true;
|
|
161
|
+
else if (arg === '--user')
|
|
162
|
+
flags.user = true;
|
|
163
|
+
else if (arg === '--list')
|
|
164
|
+
flags.list = true;
|
|
165
|
+
else if (arg.startsWith('-')) {
|
|
166
|
+
// Unknown flag — keep simple parser. Treat as positional so the
|
|
167
|
+
// downstream isOutputStyleSlug check rejects it with a clear
|
|
168
|
+
// "unknown style" message rather than swallowing silently.
|
|
169
|
+
if (flags.slug === null)
|
|
170
|
+
flags.slug = arg;
|
|
171
|
+
}
|
|
172
|
+
else if (flags.slug === null) {
|
|
173
|
+
flags.slug = arg;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Normalise the slug to lowercase so `pugi style TERSE` works the
|
|
177
|
+
// same as `pugi style terse`. The catalogue is lowercase-only by
|
|
178
|
+
// contract; this keeps operators from tripping on shift-key habits.
|
|
179
|
+
if (flags.slug !== null)
|
|
180
|
+
flags.slug = flags.slug.toLowerCase();
|
|
181
|
+
return flags;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Re-export for the slash-command dispatcher in `core/repl/session.ts`
|
|
185
|
+
* so it can render a compiled prompt block when the operator runs
|
|
186
|
+
* `/style --preview` (follow-up surface; current slash returns the
|
|
187
|
+
* runner's standard payload).
|
|
188
|
+
*
|
|
189
|
+
* Kept here so the runtime module is the single import point for
|
|
190
|
+
* style-related surfaces; consumers should NOT reach into
|
|
191
|
+
* `core/output-style/*` directly.
|
|
192
|
+
*/
|
|
193
|
+
export { OUTPUT_STYLES as OUTPUT_STYLE_CATALOGUE, } from '../../core/output-style/presets.js';
|
|
194
|
+
//# sourceMappingURL=style.js.map
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* — `pugi theme` top-level command + REPL slash
|
|
3
|
+
* companion.
|
|
4
|
+
*
|
|
5
|
+
* Operator surface:
|
|
6
|
+
*
|
|
7
|
+
* pugi theme Show active theme + table.
|
|
8
|
+
* pugi theme <name> Switch workspace theme (current cwd).
|
|
9
|
+
* pugi theme <name> --persist Switch + also write user default.
|
|
10
|
+
* pugi theme --reset Clear workspace override → back to default.
|
|
11
|
+
* pugi theme --reset --user Also clear the user default.
|
|
12
|
+
* pugi theme --list Print the catalogue (no flip).
|
|
13
|
+
* pugi theme --json Structured envelope variant.
|
|
14
|
+
*
|
|
15
|
+
* The same runner powers `/theme` from inside the REPL. The REPL
|
|
16
|
+
* dispatcher (see `core/repl/session.ts`) routes through here so the
|
|
17
|
+
* two surfaces stay single-sourced — operators trained on one read
|
|
18
|
+
* the same payload + table on the other. Matches the
|
|
19
|
+
* `/style` runner exactly so the two settings surfaces are
|
|
20
|
+
* paste-comparable for the operator + grep-comparable for future
|
|
21
|
+
* maintenance.
|
|
22
|
+
*
|
|
23
|
+
* Exit codes:
|
|
24
|
+
* 0 — show / switch / reset all succeed
|
|
25
|
+
* 1 — unknown preset slug (returned BEFORE any write)
|
|
26
|
+
* 2 — conflicting flags (e.g. `--reset` with a positional slug)
|
|
27
|
+
*
|
|
28
|
+
* The exit codes are surfaced through `process.exitCode` by the
|
|
29
|
+
* dispatcher in `cli.ts` — this module returns a structured payload
|
|
30
|
+
* + writes via the injected `writeOutput`. Throwing is reserved for
|
|
31
|
+
* truly unexpected errors (fs permissions etc.); the spec hooks the
|
|
32
|
+
* happy + sad paths through `writeOutput` shape, not via try/catch
|
|
33
|
+
* on the throw.
|
|
34
|
+
*/
|
|
35
|
+
import { DEFAULT_THEME, isThemeSlug, renderThemeTable, THEME_SLUGS, } from '../../core/theme/presets.js';
|
|
36
|
+
import { clearUserTheme, clearWorkspaceTheme, resolveTheme, setUserTheme, setWorkspaceTheme, } from '../../core/theme/state.js';
|
|
37
|
+
/**
|
|
38
|
+
* Entry point. Parses `args`, applies the operation, emits the
|
|
39
|
+
* payload + text via `ctx.writeOutput`, and returns the exit code the
|
|
40
|
+
* dispatcher should hand back to the shell.
|
|
41
|
+
*/
|
|
42
|
+
export async function runThemeCommand(args, ctx) {
|
|
43
|
+
const flags = parseFlags(args);
|
|
44
|
+
// Reset path
|
|
45
|
+
if (flags.reset) {
|
|
46
|
+
if (flags.slug !== null) {
|
|
47
|
+
const payload = buildPayload({
|
|
48
|
+
status: 'invalid_flags',
|
|
49
|
+
ctx,
|
|
50
|
+
message: '/theme --reset cannot be combined with a preset name. Use one or the other.',
|
|
51
|
+
});
|
|
52
|
+
ctx.writeOutput(payload, payload.message);
|
|
53
|
+
return 2;
|
|
54
|
+
}
|
|
55
|
+
if (flags.persist) {
|
|
56
|
+
const payload = buildPayload({
|
|
57
|
+
status: 'invalid_flags',
|
|
58
|
+
ctx,
|
|
59
|
+
message: '/theme --reset cannot be combined with --persist. Use --reset --user to also clear the user default.',
|
|
60
|
+
});
|
|
61
|
+
ctx.writeOutput(payload, payload.message);
|
|
62
|
+
return 2;
|
|
63
|
+
}
|
|
64
|
+
clearWorkspaceTheme({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
|
|
65
|
+
if (flags.user) {
|
|
66
|
+
clearUserTheme({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
|
|
67
|
+
}
|
|
68
|
+
const payload = buildPayload({
|
|
69
|
+
status: 'reset',
|
|
70
|
+
ctx,
|
|
71
|
+
message: flags.user
|
|
72
|
+
? `Cleared workspace + user theme. Active: ${DEFAULT_THEME} (default).`
|
|
73
|
+
: `Cleared workspace theme. Active: ${describeActive(ctx)}.`,
|
|
74
|
+
});
|
|
75
|
+
ctx.writeOutput(payload, payload.message);
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
// List path
|
|
79
|
+
if (flags.list && flags.slug === null) {
|
|
80
|
+
const resolved = resolveTheme({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
|
|
81
|
+
const payload = buildPayload({
|
|
82
|
+
status: 'listed',
|
|
83
|
+
ctx,
|
|
84
|
+
message: renderThemeTable(resolved.slug),
|
|
85
|
+
});
|
|
86
|
+
ctx.writeOutput(payload, payload.message);
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
// Switch path
|
|
90
|
+
if (flags.slug !== null) {
|
|
91
|
+
if (!isThemeSlug(flags.slug)) {
|
|
92
|
+
const payload = buildPayload({
|
|
93
|
+
status: 'invalid_slug',
|
|
94
|
+
ctx,
|
|
95
|
+
attemptedSlug: flags.slug,
|
|
96
|
+
message: `Unknown theme "${flags.slug}". Try one of: ${THEME_SLUGS.join(', ')}.`,
|
|
97
|
+
});
|
|
98
|
+
ctx.writeOutput(payload, payload.message);
|
|
99
|
+
return 1;
|
|
100
|
+
}
|
|
101
|
+
const before = resolveTheme({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
|
|
102
|
+
setWorkspaceTheme(flags.slug, { workspaceRoot: ctx.workspaceRoot, env: ctx.env });
|
|
103
|
+
if (flags.persist) {
|
|
104
|
+
setUserTheme(flags.slug, { workspaceRoot: ctx.workspaceRoot, env: ctx.env });
|
|
105
|
+
}
|
|
106
|
+
const tail = flags.persist ? ' (workspace + user default)' : ' (workspace)';
|
|
107
|
+
const payload = buildPayload({
|
|
108
|
+
status: 'switched',
|
|
109
|
+
ctx,
|
|
110
|
+
previous: before.slug,
|
|
111
|
+
persistedToUser: flags.persist,
|
|
112
|
+
message: `Theme → ${flags.slug}${tail}. Was: ${before.slug} (${before.source}).`,
|
|
113
|
+
});
|
|
114
|
+
ctx.writeOutput(payload, payload.message);
|
|
115
|
+
return 0;
|
|
116
|
+
}
|
|
117
|
+
// Show path (no args)
|
|
118
|
+
const resolved = resolveTheme({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
|
|
119
|
+
const banner = `Active theme: ${resolved.slug} (${resolved.source})`;
|
|
120
|
+
const table = renderThemeTable(resolved.slug);
|
|
121
|
+
const payload = buildPayload({
|
|
122
|
+
status: 'show',
|
|
123
|
+
ctx,
|
|
124
|
+
message: `${banner}\n\n${table}`,
|
|
125
|
+
});
|
|
126
|
+
ctx.writeOutput(payload, payload.message);
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
function describeActive(ctx) {
|
|
130
|
+
const resolved = resolveTheme({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
|
|
131
|
+
return `${resolved.slug} (${resolved.source})`;
|
|
132
|
+
}
|
|
133
|
+
function buildPayload(args) {
|
|
134
|
+
const resolved = resolveTheme({ workspaceRoot: args.ctx.workspaceRoot, env: args.ctx.env });
|
|
135
|
+
const payload = {
|
|
136
|
+
command: 'theme',
|
|
137
|
+
status: args.status,
|
|
138
|
+
active: resolved.slug,
|
|
139
|
+
source: resolved.source,
|
|
140
|
+
presets: THEME_SLUGS,
|
|
141
|
+
message: args.message,
|
|
142
|
+
};
|
|
143
|
+
if (args.previous !== undefined)
|
|
144
|
+
payload.previous = args.previous;
|
|
145
|
+
if (args.persistedToUser !== undefined)
|
|
146
|
+
payload.persistedToUser = args.persistedToUser;
|
|
147
|
+
if (args.attemptedSlug !== undefined)
|
|
148
|
+
payload.attemptedSlug = args.attemptedSlug;
|
|
149
|
+
return payload;
|
|
150
|
+
}
|
|
151
|
+
function parseFlags(args) {
|
|
152
|
+
const flags = {
|
|
153
|
+
slug: null,
|
|
154
|
+
persist: false,
|
|
155
|
+
reset: false,
|
|
156
|
+
user: false,
|
|
157
|
+
list: false,
|
|
158
|
+
};
|
|
159
|
+
for (const arg of args) {
|
|
160
|
+
if (arg === '--persist')
|
|
161
|
+
flags.persist = true;
|
|
162
|
+
else if (arg === '--reset')
|
|
163
|
+
flags.reset = true;
|
|
164
|
+
else if (arg === '--user')
|
|
165
|
+
flags.user = true;
|
|
166
|
+
else if (arg === '--list')
|
|
167
|
+
flags.list = true;
|
|
168
|
+
else if (arg.startsWith('-')) {
|
|
169
|
+
// Unknown flag — keep simple parser. Treat as positional so the
|
|
170
|
+
// downstream isThemeSlug check rejects it with a clear "unknown
|
|
171
|
+
// theme" message rather than swallowing silently. Mirrors the
|
|
172
|
+
// L18 style runner's behaviour for grep-parity.
|
|
173
|
+
if (flags.slug === null)
|
|
174
|
+
flags.slug = arg;
|
|
175
|
+
}
|
|
176
|
+
else if (flags.slug === null) {
|
|
177
|
+
flags.slug = arg;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Normalise the slug to lowercase so `pugi theme DARK` works the
|
|
181
|
+
// same as `pugi theme dark`. The catalogue is lowercase-only by
|
|
182
|
+
// contract; this keeps operators from tripping on shift-key habits.
|
|
183
|
+
if (flags.slug !== null)
|
|
184
|
+
flags.slug = flags.slug.toLowerCase();
|
|
185
|
+
return flags;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Re-export for the slash-command dispatcher in
|
|
189
|
+
* `core/repl/session.ts` so it can render a preview block if a
|
|
190
|
+
* future surface (`/theme --preview`) lands. Kept here so the
|
|
191
|
+
* runtime module is the single import point for theme-related
|
|
192
|
+
* surfaces; consumers should NOT reach into `core/theme/*` directly
|
|
193
|
+
* unless they are Ink components that need the React context.
|
|
194
|
+
*/
|
|
195
|
+
export { THEMES as THEME_CATALOGUE, } from '../../core/theme/presets.js';
|
|
196
|
+
//# sourceMappingURL=theme.js.map
|
|
@@ -3,34 +3,35 @@ import { existsSync, readFileSync, renameSync, unlinkSync, writeFileSync } from
|
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
4
|
import { hashContent } from '../../core/file-cache.js';
|
|
5
5
|
import { recordFileMutation, recordToolCall, recordToolResult, } from '../../core/session.js';
|
|
6
|
+
import { writeBlob } from './redo-blob-store.js';
|
|
6
7
|
/**
|
|
7
8
|
* `pugi undo` — revert the file mutations from the most recent successful
|
|
8
9
|
* `write` / `edit` / `multi_edit` tool result.
|
|
9
10
|
*
|
|
10
11
|
* Walk strategy:
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
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.
|
|
19
20
|
*
|
|
20
21
|
* Restore strategy (M1 — no blob store yet):
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
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:
|
|
24
25
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
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.
|
|
34
35
|
*
|
|
35
36
|
* If any single restore is unsafe the whole turn aborts with exit code
|
|
36
37
|
* 1 and no files are touched.
|
|
@@ -39,7 +40,7 @@ import { recordFileMutation, recordToolCall, recordToolResult, } from '../../cor
|
|
|
39
40
|
* but NOT raw content (see `core/file-cache.ts`). M1 ships without a
|
|
40
41
|
* dedicated blob CAS; `git show HEAD:<path>` is the cheapest authoritative
|
|
41
42
|
* source for pre-mutation content as long as the workspace is a git
|
|
42
|
-
* repository. The
|
|
43
|
+
* repository. The SQLite session store will replace this with a
|
|
43
44
|
* proper before-blob ledger.
|
|
44
45
|
*/
|
|
45
46
|
const MUTATING_TOOLS = new Set(['write', 'edit', 'multi_edit']);
|
|
@@ -81,6 +82,37 @@ export async function runUndoCommand(_args, ctx) {
|
|
|
81
82
|
const restored = [];
|
|
82
83
|
for (const step of plan.steps) {
|
|
83
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
|
+
}
|
|
84
116
|
executeRevert(ctx.workspaceRoot, step);
|
|
85
117
|
restored.push({ path: step.path, operation: step.operation });
|
|
86
118
|
}
|
|
@@ -119,7 +151,7 @@ export async function runUndoCommand(_args, ctx) {
|
|
|
119
151
|
restored,
|
|
120
152
|
}, [
|
|
121
153
|
`Undid ${restored.length} mutation(s) from ${target.toolCallId}:`,
|
|
122
|
-
...restored.map((entry) => `
|
|
154
|
+
...restored.map((entry) => ` ${entry.operation.padEnd(7)} ${entry.path}`),
|
|
123
155
|
].join('\n'));
|
|
124
156
|
}
|
|
125
157
|
function parseEvents(eventsPath) {
|