@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,579 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `pugi --worktree` bootstrap - PUGI-487.
|
|
3
|
-
*
|
|
4
|
-
* User-facing parity surface for parallel-agent isolation patterns
|
|
5
|
-
* seen across competitor AI coding CLIs. The flag creates an isolated
|
|
6
|
-
* git worktree at `.claude/worktrees/<name>/` on a new branch
|
|
7
|
-
* `worktree-<name>` and starts the REPL inside it. Operators can run
|
|
8
|
-
* several Pugi sessions in parallel without their filesystems
|
|
9
|
-
* colliding.
|
|
10
|
-
*
|
|
11
|
-
* Distinct namespace from the existing dispatcher worktree manager:
|
|
12
|
-
*
|
|
13
|
-
* - This module owns `.claude/worktrees/<name>/` (USER-facing,
|
|
14
|
-
* short kebab names like `feat-x` or auto-generated `bright-fox`).
|
|
15
|
-
* - `core/worktree-manager/` owns `.pugi/worktrees/<agent-id>/`
|
|
16
|
-
* (DISPATCHER-facing, agent-id keyed).
|
|
17
|
-
*
|
|
18
|
-
* Public surface:
|
|
19
|
-
*
|
|
20
|
-
* - `bootstrapWorktree(options)` - validate inputs, run hooks,
|
|
21
|
-
* create the worktree, copy `.worktreeinclude` matches, return
|
|
22
|
-
* a typed envelope ready for the CLI to chdir + REPL.
|
|
23
|
-
* - `generateWhimsicalName()` - adjective + animal pair for the
|
|
24
|
-
* auto-named case (`pugi --worktree` with no name).
|
|
25
|
-
* - `runUserWorktreeCleanup(options)` - the daily sweep that removes
|
|
26
|
-
* user-facing worktrees > N days old with no uncommitted state.
|
|
27
|
-
*
|
|
28
|
-
* Brand voice: ASCII only, no emoji, no banned words.
|
|
29
|
-
*/
|
|
30
|
-
import { spawnSync } from 'node:child_process';
|
|
31
|
-
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync, } from 'node:fs';
|
|
32
|
-
import { dirname, isAbsolute, posix, resolve, sep } from 'node:path';
|
|
33
|
-
import { loadHooksConfig } from '../core/hooks/registry.js';
|
|
34
|
-
import { fireWorktreeHooks, } from '../core/hooks/worktree-events.js';
|
|
35
|
-
import { collectWorktreeIncludeFiles, loadWorktreeInclude, } from '../core/worktree/include-parser.js';
|
|
36
|
-
import { isTrustedWorkspace } from '../core/trust.js';
|
|
37
|
-
/** Canonical directory prefix for user-facing worktrees. */
|
|
38
|
-
export const USER_WORKTREES_PREFIX = '.claude/worktrees';
|
|
39
|
-
/**
|
|
40
|
-
* Branch prefix for user-facing worktree branches. The full name is
|
|
41
|
-
* `worktree-<name>` so the operator can quickly see which branches in
|
|
42
|
-
* `git branch` came from `pugi --worktree`.
|
|
43
|
-
*/
|
|
44
|
-
export const USER_WORKTREE_BRANCH_PREFIX = 'worktree-';
|
|
45
|
-
/** Strict slug regex for the operator-supplied name. */
|
|
46
|
-
export const WORKTREE_NAME_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
47
|
-
/** Strict regex for the PR-shorthand sugar. */
|
|
48
|
-
export const PR_SHORTHAND_PATTERN = /^#(\d{1,8})$/;
|
|
49
|
-
/**
|
|
50
|
-
* The main entry point invoked by `runtime/cli.ts` when `--worktree`
|
|
51
|
-
* is set on the bare invocation. Returns the typed envelope so the
|
|
52
|
-
* CLI layer can decide whether to chdir + mount the REPL or surface
|
|
53
|
-
* an error.
|
|
54
|
-
*/
|
|
55
|
-
export async function bootstrapWorktree(options) {
|
|
56
|
-
const runGit = options.runGit ?? defaultGitRunner;
|
|
57
|
-
const repoRoot = resolve(options.repoRoot);
|
|
58
|
-
// Step 1: confirm we are actually in a git repo. The standard
|
|
59
|
-
// `rev-parse --git-dir` probe surfaces the most useful error when
|
|
60
|
-
// the operator runs `pugi --worktree` in `$HOME` by mistake.
|
|
61
|
-
const probe = runGit(['rev-parse', '--git-dir'], repoRoot);
|
|
62
|
-
if (probe.status !== 0) {
|
|
63
|
-
return failure({
|
|
64
|
-
name: '',
|
|
65
|
-
branch: '',
|
|
66
|
-
directory: '',
|
|
67
|
-
baseRef: options.baseRef ?? 'fresh',
|
|
68
|
-
resolvedBase: '',
|
|
69
|
-
reason: 'not_a_git_repo',
|
|
70
|
-
message: `not a git repository: ${repoRoot}`,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
// Step 2: trust gate. The default-restricted policy (see
|
|
74
|
-
// `core/trust.ts`) refuses to load workspace-scoped hooks unless the
|
|
75
|
-
// operator has explicitly trusted the directory. Worktrees inherit
|
|
76
|
-
// their parent's trust state so a `pugi init`-trusted root carries
|
|
77
|
-
// through to every child created via this flag.
|
|
78
|
-
const trustGate = options.isTrusted ?? isTrustedWorkspace;
|
|
79
|
-
const skipTrust = options.skipTrustGate === true ||
|
|
80
|
-
process.env.PUGI_SKIP_WORKTREE_TRUST === '1';
|
|
81
|
-
if (!skipTrust) {
|
|
82
|
-
const trusted = await trustGate(repoRoot);
|
|
83
|
-
if (!trusted) {
|
|
84
|
-
return failure({
|
|
85
|
-
name: '',
|
|
86
|
-
branch: '',
|
|
87
|
-
directory: '',
|
|
88
|
-
baseRef: options.baseRef ?? 'fresh',
|
|
89
|
-
resolvedBase: '',
|
|
90
|
-
reason: 'untrusted_workspace',
|
|
91
|
-
message: `workspace ${repoRoot} is not trusted. Run \`pugi init\` or \`pugi config trust .\` first.`,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
// Step 3: resolve the requested name. Three shapes:
|
|
96
|
-
// - PR shorthand `#1234`
|
|
97
|
-
// - operator-supplied slug
|
|
98
|
-
// - undefined => auto-generate
|
|
99
|
-
const nameResolution = resolveName(options.nameArg, options.generateName);
|
|
100
|
-
if (typeof nameResolution === 'string') {
|
|
101
|
-
return failure({
|
|
102
|
-
name: typeof options.nameArg === 'string' ? options.nameArg : '',
|
|
103
|
-
branch: '',
|
|
104
|
-
directory: '',
|
|
105
|
-
baseRef: options.baseRef ?? 'fresh',
|
|
106
|
-
resolvedBase: '',
|
|
107
|
-
reason: 'invalid_name',
|
|
108
|
-
message: nameResolution,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
const { name, branch, prNumber } = nameResolution;
|
|
112
|
-
const baseRefMode = options.baseRef ?? 'fresh';
|
|
113
|
-
// Step 4: compute the target directory + resolved base ref.
|
|
114
|
-
const defaultDirectory = resolve(repoRoot, USER_WORKTREES_PREFIX, name);
|
|
115
|
-
if (existsSync(defaultDirectory)) {
|
|
116
|
-
return failure({
|
|
117
|
-
name,
|
|
118
|
-
branch,
|
|
119
|
-
directory: defaultDirectory,
|
|
120
|
-
baseRef: baseRefMode,
|
|
121
|
-
resolvedBase: '',
|
|
122
|
-
reason: 'directory_exists',
|
|
123
|
-
message: `worktree already exists at ${defaultDirectory}`,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
const baseSha = resolveBaseSha(runGit, repoRoot, baseRefMode, prNumber);
|
|
127
|
-
if (typeof baseSha === 'string' && baseSha.startsWith('ERR:')) {
|
|
128
|
-
return failure({
|
|
129
|
-
name,
|
|
130
|
-
branch,
|
|
131
|
-
directory: defaultDirectory,
|
|
132
|
-
baseRef: baseRefMode,
|
|
133
|
-
resolvedBase: '',
|
|
134
|
-
reason: prNumber !== undefined ? 'pr_fetch_failed' : 'git_command_failed',
|
|
135
|
-
message: baseSha.slice('ERR:'.length),
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
// Step 5: fire the WorktreeCreate hook. Hooks may veto via non-zero
|
|
139
|
-
// exit (with `blocking: true`) or override the directory location.
|
|
140
|
-
const hooks = (options.loadHooks ?? loadHooksConfig)();
|
|
141
|
-
const createPayload = {
|
|
142
|
-
event: 'WorktreeCreate',
|
|
143
|
-
name,
|
|
144
|
-
defaultDirectory,
|
|
145
|
-
baseRef: typeof baseSha === 'string' ? baseSha : '',
|
|
146
|
-
repoRoot,
|
|
147
|
-
};
|
|
148
|
-
const createOutcome = fireWorktreeHooks(hooks, 'WorktreeCreate', createPayload);
|
|
149
|
-
if (createOutcome.anyBlocked) {
|
|
150
|
-
return failure({
|
|
151
|
-
name,
|
|
152
|
-
branch,
|
|
153
|
-
directory: defaultDirectory,
|
|
154
|
-
baseRef: baseRefMode,
|
|
155
|
-
resolvedBase: typeof baseSha === 'string' ? baseSha : '',
|
|
156
|
-
reason: 'hook_blocked',
|
|
157
|
-
message: `WorktreeCreate hook blocked the operation`,
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
// Override resolution: a hook stdout `{ "directory": "<abs-path>" }`
|
|
161
|
-
// wins ONLY when the path is inside the repo root. We refuse
|
|
162
|
-
// out-of-repo overrides defensively because the cleanup sweep would
|
|
163
|
-
// never reach them.
|
|
164
|
-
let directory = defaultDirectory;
|
|
165
|
-
if (createOutcome.directoryOverride) {
|
|
166
|
-
const override = resolve(repoRoot, createOutcome.directoryOverride);
|
|
167
|
-
if (override.startsWith(repoRoot + sep)) {
|
|
168
|
-
directory = override;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
// Step 6: git worktree add. We resolve the base SHA above so the
|
|
172
|
-
// branch ref does not depend on a runtime-mutable `origin/HEAD` at
|
|
173
|
-
// creation time.
|
|
174
|
-
mkdirSync(dirname(directory), { recursive: true });
|
|
175
|
-
const addArgs = [
|
|
176
|
-
'worktree',
|
|
177
|
-
'add',
|
|
178
|
-
'-b',
|
|
179
|
-
branch,
|
|
180
|
-
'--',
|
|
181
|
-
directory,
|
|
182
|
-
typeof baseSha === 'string' ? baseSha : 'HEAD',
|
|
183
|
-
];
|
|
184
|
-
const add = runGit(addArgs, repoRoot);
|
|
185
|
-
if (add.status !== 0) {
|
|
186
|
-
return failure({
|
|
187
|
-
name,
|
|
188
|
-
branch,
|
|
189
|
-
directory,
|
|
190
|
-
baseRef: baseRefMode,
|
|
191
|
-
resolvedBase: typeof baseSha === 'string' ? baseSha : '',
|
|
192
|
-
reason: 'git_command_failed',
|
|
193
|
-
message: (add.stderr || add.stdout || `exit=${add.status}`).trim(),
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
// Step 7: copy `.worktreeinclude` matches (e.g. .env). Tracked files
|
|
197
|
-
// are never duplicated - see the include-parser doc-block.
|
|
198
|
-
const includeResult = loadWorktreeInclude(repoRoot);
|
|
199
|
-
const collector = options.collectIncludes ?? collectWorktreeIncludeFiles;
|
|
200
|
-
const copyFile = options.copyFile ?? defaultCopyFile;
|
|
201
|
-
const copyOpts = {};
|
|
202
|
-
const matches = collector(repoRoot, includeResult.rules, copyOpts);
|
|
203
|
-
const copiedFiles = [];
|
|
204
|
-
for (const rel of matches) {
|
|
205
|
-
const src = resolve(repoRoot, rel);
|
|
206
|
-
const dest = resolve(directory, rel);
|
|
207
|
-
try {
|
|
208
|
-
mkdirSync(dirname(dest), { recursive: true });
|
|
209
|
-
copyFile(src, dest);
|
|
210
|
-
copiedFiles.push(rel);
|
|
211
|
-
}
|
|
212
|
-
catch {
|
|
213
|
-
// Best-effort copy. A single failure (e.g. permission denied on
|
|
214
|
-
// one entry) should not abort the bootstrap - surface via the
|
|
215
|
-
// envelope and let the operator decide.
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
return {
|
|
219
|
-
ok: true,
|
|
220
|
-
name,
|
|
221
|
-
branch,
|
|
222
|
-
directory,
|
|
223
|
-
baseRef: baseRefMode,
|
|
224
|
-
resolvedBase: typeof baseSha === 'string' ? baseSha : '',
|
|
225
|
-
copiedFiles,
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* Parse the operator-supplied name argument. Returns a `ResolvedName`
|
|
230
|
-
* on success or a human-readable error string on rejection. Exposed
|
|
231
|
-
* for spec coverage.
|
|
232
|
-
*/
|
|
233
|
-
export function resolveName(nameArg, generator) {
|
|
234
|
-
if (nameArg === undefined || nameArg.trim().length === 0) {
|
|
235
|
-
const generated = (generator ?? generateWhimsicalName)();
|
|
236
|
-
if (!WORKTREE_NAME_PATTERN.test(generated)) {
|
|
237
|
-
return `auto-generated name "${generated}" failed validation`;
|
|
238
|
-
}
|
|
239
|
-
return {
|
|
240
|
-
name: generated,
|
|
241
|
-
branch: `${USER_WORKTREE_BRANCH_PREFIX}${generated}`,
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
const trimmed = nameArg.trim();
|
|
245
|
-
const prMatch = PR_SHORTHAND_PATTERN.exec(trimmed);
|
|
246
|
-
if (prMatch && typeof prMatch[1] === 'string') {
|
|
247
|
-
const n = Number(prMatch[1]);
|
|
248
|
-
if (!Number.isInteger(n) || n <= 0) {
|
|
249
|
-
return `invalid PR number: ${trimmed}`;
|
|
250
|
-
}
|
|
251
|
-
return {
|
|
252
|
-
name: `pr-${n}`,
|
|
253
|
-
branch: `${USER_WORKTREE_BRANCH_PREFIX}pr-${n}`,
|
|
254
|
-
prNumber: n,
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
if (!WORKTREE_NAME_PATTERN.test(trimmed)) {
|
|
258
|
-
return `name must match ${WORKTREE_NAME_PATTERN}`;
|
|
259
|
-
}
|
|
260
|
-
return {
|
|
261
|
-
name: trimmed,
|
|
262
|
-
branch: `${USER_WORKTREE_BRANCH_PREFIX}${trimmed}`,
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
/* ------------------------------------------------------------------ */
|
|
266
|
-
/* Base ref resolution */
|
|
267
|
-
/* ------------------------------------------------------------------ */
|
|
268
|
-
/**
|
|
269
|
-
* Resolve the SHA the new branch should point at. PR-shorthand wins
|
|
270
|
-
* regardless of `baseRef` mode (the operator's clear intent). For the
|
|
271
|
-
* `fresh` mode we resolve `origin/<default-branch>` and fall back to
|
|
272
|
-
* local HEAD when no remote is configured. The `head` mode always
|
|
273
|
-
* carries local HEAD.
|
|
274
|
-
*
|
|
275
|
-
* Returns the resolved SHA on success or an `ERR:<detail>` string on
|
|
276
|
-
* failure so the caller can decide whether to surface
|
|
277
|
-
* `git_command_failed` or `pr_fetch_failed`.
|
|
278
|
-
*/
|
|
279
|
-
function resolveBaseSha(runGit, repoRoot, baseRef, prNumber) {
|
|
280
|
-
if (prNumber !== undefined) {
|
|
281
|
-
// `pull/N/head` is the GitHub convention. Fetch the PR head into
|
|
282
|
-
// the local refs cache before resolving so the worktree-add can
|
|
283
|
-
// find it.
|
|
284
|
-
const ref = `pull/${prNumber}/head`;
|
|
285
|
-
const fetch = runGit(['fetch', 'origin', ref], repoRoot);
|
|
286
|
-
if (fetch.status !== 0) {
|
|
287
|
-
return `ERR:failed to fetch ${ref} from origin: ${(fetch.stderr || fetch.stdout).trim()}`;
|
|
288
|
-
}
|
|
289
|
-
const sha = runGit(['rev-parse', 'FETCH_HEAD'], repoRoot);
|
|
290
|
-
if (sha.status !== 0) {
|
|
291
|
-
return `ERR:failed to resolve FETCH_HEAD after fetching ${ref}`;
|
|
292
|
-
}
|
|
293
|
-
return sha.stdout.trim();
|
|
294
|
-
}
|
|
295
|
-
if (baseRef === 'head') {
|
|
296
|
-
const sha = runGit(['rev-parse', 'HEAD'], repoRoot);
|
|
297
|
-
if (sha.status !== 0) {
|
|
298
|
-
return `ERR:failed to resolve HEAD: ${(sha.stderr || sha.stdout).trim()}`;
|
|
299
|
-
}
|
|
300
|
-
return sha.stdout.trim();
|
|
301
|
-
}
|
|
302
|
-
// baseRef === 'fresh': try origin/<default-branch>, fall back to HEAD.
|
|
303
|
-
const remoteBranch = detectOriginHead(runGit, repoRoot);
|
|
304
|
-
if (remoteBranch) {
|
|
305
|
-
const sha = runGit(['rev-parse', remoteBranch], repoRoot);
|
|
306
|
-
if (sha.status === 0) {
|
|
307
|
-
return sha.stdout.trim();
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
const headSha = runGit(['rev-parse', 'HEAD'], repoRoot);
|
|
311
|
-
if (headSha.status !== 0) {
|
|
312
|
-
return `ERR:failed to resolve any base ref (no origin/HEAD and no local HEAD)`;
|
|
313
|
-
}
|
|
314
|
-
return headSha.stdout.trim();
|
|
315
|
-
}
|
|
316
|
-
function detectOriginHead(runGit, repoRoot) {
|
|
317
|
-
const symRef = runGit(['symbolic-ref', '--quiet', '--short', 'refs/remotes/origin/HEAD'], repoRoot);
|
|
318
|
-
if (symRef.status === 0) {
|
|
319
|
-
const value = symRef.stdout.trim();
|
|
320
|
-
if (value.length > 0)
|
|
321
|
-
return value;
|
|
322
|
-
}
|
|
323
|
-
// Common default fallbacks. Try `origin/main`, then `origin/master`.
|
|
324
|
-
for (const candidate of ['origin/main', 'origin/master']) {
|
|
325
|
-
const verify = runGit(['rev-parse', '--verify', candidate], repoRoot);
|
|
326
|
-
if (verify.status === 0)
|
|
327
|
-
return candidate;
|
|
328
|
-
}
|
|
329
|
-
return undefined;
|
|
330
|
-
}
|
|
331
|
-
/* ------------------------------------------------------------------ */
|
|
332
|
-
/* Whimsical name generator */
|
|
333
|
-
/* ------------------------------------------------------------------ */
|
|
334
|
-
const WHIMSICAL_ADJECTIVES = [
|
|
335
|
-
'bright',
|
|
336
|
-
'brave',
|
|
337
|
-
'calm',
|
|
338
|
-
'clever',
|
|
339
|
-
'eager',
|
|
340
|
-
'fancy',
|
|
341
|
-
'gentle',
|
|
342
|
-
'happy',
|
|
343
|
-
'jolly',
|
|
344
|
-
'keen',
|
|
345
|
-
'lucky',
|
|
346
|
-
'mighty',
|
|
347
|
-
'noble',
|
|
348
|
-
'quick',
|
|
349
|
-
'royal',
|
|
350
|
-
'silent',
|
|
351
|
-
'swift',
|
|
352
|
-
'tidy',
|
|
353
|
-
'vivid',
|
|
354
|
-
'witty',
|
|
355
|
-
];
|
|
356
|
-
const WHIMSICAL_VERBS = [
|
|
357
|
-
'running',
|
|
358
|
-
'sleeping',
|
|
359
|
-
'flying',
|
|
360
|
-
'leaping',
|
|
361
|
-
'roaring',
|
|
362
|
-
'singing',
|
|
363
|
-
'climbing',
|
|
364
|
-
'gliding',
|
|
365
|
-
'dancing',
|
|
366
|
-
'soaring',
|
|
367
|
-
];
|
|
368
|
-
const WHIMSICAL_ANIMALS = [
|
|
369
|
-
'fox',
|
|
370
|
-
'otter',
|
|
371
|
-
'lynx',
|
|
372
|
-
'puma',
|
|
373
|
-
'eagle',
|
|
374
|
-
'hawk',
|
|
375
|
-
'whale',
|
|
376
|
-
'dolphin',
|
|
377
|
-
'badger',
|
|
378
|
-
'panda',
|
|
379
|
-
'tiger',
|
|
380
|
-
'wolf',
|
|
381
|
-
'bear',
|
|
382
|
-
'koala',
|
|
383
|
-
'raven',
|
|
384
|
-
'falcon',
|
|
385
|
-
'crow',
|
|
386
|
-
'sparrow',
|
|
387
|
-
];
|
|
388
|
-
/**
|
|
389
|
-
* Three-word adjective-verb-animal slug for the auto-name case.
|
|
390
|
-
* Uses `Math.random` because there is no security boundary at the
|
|
391
|
-
* naming layer - the WORKTREE_NAME_PATTERN regex below validates the
|
|
392
|
-
* shape after generation.
|
|
393
|
-
*/
|
|
394
|
-
export function generateWhimsicalName() {
|
|
395
|
-
const adj = pick(WHIMSICAL_ADJECTIVES);
|
|
396
|
-
const verb = pick(WHIMSICAL_VERBS);
|
|
397
|
-
const animal = pick(WHIMSICAL_ANIMALS);
|
|
398
|
-
return `${adj}-${verb}-${animal}`;
|
|
399
|
-
}
|
|
400
|
-
function pick(items) {
|
|
401
|
-
if (items.length === 0) {
|
|
402
|
-
throw new Error('cannot pick from empty list');
|
|
403
|
-
}
|
|
404
|
-
const idx = Math.floor(Math.random() * items.length);
|
|
405
|
-
// Bounded by the modulo above so the result is always defined.
|
|
406
|
-
return items[idx];
|
|
407
|
-
}
|
|
408
|
-
/* ------------------------------------------------------------------ */
|
|
409
|
-
/* Helpers */
|
|
410
|
-
/* ------------------------------------------------------------------ */
|
|
411
|
-
function defaultGitRunner(argv, cwd) {
|
|
412
|
-
const result = spawnSync('git', [...argv], {
|
|
413
|
-
cwd,
|
|
414
|
-
encoding: 'utf8',
|
|
415
|
-
shell: false,
|
|
416
|
-
timeout: 60_000,
|
|
417
|
-
maxBuffer: 16 * 1024 * 1024,
|
|
418
|
-
});
|
|
419
|
-
return {
|
|
420
|
-
status: result.status,
|
|
421
|
-
stdout: typeof result.stdout === 'string' ? result.stdout : '',
|
|
422
|
-
stderr: typeof result.stderr === 'string' ? result.stderr : '',
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
function defaultCopyFile(src, dest) {
|
|
426
|
-
copyFileSync(src, dest);
|
|
427
|
-
}
|
|
428
|
-
function failure(args) {
|
|
429
|
-
return {
|
|
430
|
-
ok: false,
|
|
431
|
-
name: args.name,
|
|
432
|
-
branch: args.branch,
|
|
433
|
-
directory: args.directory,
|
|
434
|
-
baseRef: args.baseRef,
|
|
435
|
-
resolvedBase: args.resolvedBase,
|
|
436
|
-
copiedFiles: [],
|
|
437
|
-
reason: args.reason,
|
|
438
|
-
message: args.message,
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
/**
|
|
442
|
-
* The daily cleanup sweep. Walks `.claude/worktrees/`, computes each
|
|
443
|
-
* entry's age (mtime), and removes ones older than `periodDays` that
|
|
444
|
-
* have no uncommitted/untracked/unpushed state. Crashed-looking trees
|
|
445
|
-
* with local work are preserved by design.
|
|
446
|
-
*/
|
|
447
|
-
export function runUserWorktreeCleanup(options) {
|
|
448
|
-
const repoRoot = resolve(options.repoRoot);
|
|
449
|
-
const now = options.now ?? Date.now;
|
|
450
|
-
const runGit = options.runGit ?? defaultGitRunner;
|
|
451
|
-
const root = resolve(repoRoot, USER_WORKTREES_PREFIX);
|
|
452
|
-
const lister = options.listWorktreeDir ?? defaultListWorktreeDir;
|
|
453
|
-
const remover = options.removeWorktree ?? defaultRemoveWorktree;
|
|
454
|
-
const hooks = (options.loadHooks ?? loadHooksConfig)();
|
|
455
|
-
if (!existsSync(root)) {
|
|
456
|
-
return { scanned: 0, removed: [], preserved: [] };
|
|
457
|
-
}
|
|
458
|
-
const entries = lister(root);
|
|
459
|
-
const removed = [];
|
|
460
|
-
const preserved = [];
|
|
461
|
-
const periodMs = options.periodDays * 24 * 60 * 60 * 1000;
|
|
462
|
-
for (const name of entries) {
|
|
463
|
-
if (!WORKTREE_NAME_PATTERN.test(name))
|
|
464
|
-
continue;
|
|
465
|
-
const dir = resolve(root, name);
|
|
466
|
-
let mtimeMs;
|
|
467
|
-
try {
|
|
468
|
-
mtimeMs = statSync(dir).mtimeMs;
|
|
469
|
-
}
|
|
470
|
-
catch {
|
|
471
|
-
continue;
|
|
472
|
-
}
|
|
473
|
-
const ageMs = now() - mtimeMs;
|
|
474
|
-
const ageDays = Math.floor(ageMs / (24 * 60 * 60 * 1000));
|
|
475
|
-
if (ageMs < periodMs) {
|
|
476
|
-
preserved.push({ name, directory: dir, ageDays, removed: false, preservedReason: 'too-young' });
|
|
477
|
-
continue;
|
|
478
|
-
}
|
|
479
|
-
if (hasUncommittedChanges(runGit, dir)) {
|
|
480
|
-
preserved.push({
|
|
481
|
-
name,
|
|
482
|
-
directory: dir,
|
|
483
|
-
ageDays,
|
|
484
|
-
removed: false,
|
|
485
|
-
preservedReason: 'has-uncommitted-changes',
|
|
486
|
-
});
|
|
487
|
-
continue;
|
|
488
|
-
}
|
|
489
|
-
if (hasUntrackedFiles(runGit, dir)) {
|
|
490
|
-
preserved.push({
|
|
491
|
-
name,
|
|
492
|
-
directory: dir,
|
|
493
|
-
ageDays,
|
|
494
|
-
removed: false,
|
|
495
|
-
preservedReason: 'has-untracked-files',
|
|
496
|
-
});
|
|
497
|
-
continue;
|
|
498
|
-
}
|
|
499
|
-
if (hasUnpushedCommits(runGit, dir)) {
|
|
500
|
-
preserved.push({
|
|
501
|
-
name,
|
|
502
|
-
directory: dir,
|
|
503
|
-
ageDays,
|
|
504
|
-
removed: false,
|
|
505
|
-
preservedReason: 'has-unpushed-commits',
|
|
506
|
-
});
|
|
507
|
-
continue;
|
|
508
|
-
}
|
|
509
|
-
// Fire WorktreeRemove hook before pulling the trigger.
|
|
510
|
-
const removePayload = {
|
|
511
|
-
event: 'WorktreeRemove',
|
|
512
|
-
name,
|
|
513
|
-
directory: dir,
|
|
514
|
-
reason: 'auto-cleanup',
|
|
515
|
-
repoRoot,
|
|
516
|
-
};
|
|
517
|
-
const outcome = fireWorktreeHooks(hooks, 'WorktreeRemove', removePayload);
|
|
518
|
-
if (outcome.anyBlocked) {
|
|
519
|
-
preserved.push({
|
|
520
|
-
name,
|
|
521
|
-
directory: dir,
|
|
522
|
-
ageDays,
|
|
523
|
-
removed: false,
|
|
524
|
-
preservedReason: 'hook-blocked',
|
|
525
|
-
});
|
|
526
|
-
continue;
|
|
527
|
-
}
|
|
528
|
-
if (options.dryRun) {
|
|
529
|
-
removed.push({ name, directory: dir, ageDays, removed: false });
|
|
530
|
-
continue;
|
|
531
|
-
}
|
|
532
|
-
const ok = remover(dir, runGit, repoRoot);
|
|
533
|
-
removed.push({ name, directory: dir, ageDays, removed: ok });
|
|
534
|
-
}
|
|
535
|
-
return {
|
|
536
|
-
scanned: entries.length,
|
|
537
|
-
removed,
|
|
538
|
-
preserved,
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
function defaultListWorktreeDir(dir) {
|
|
542
|
-
try {
|
|
543
|
-
return readdirSync(dir);
|
|
544
|
-
}
|
|
545
|
-
catch {
|
|
546
|
-
return [];
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
function defaultRemoveWorktree(path, runGit, repoRoot) {
|
|
550
|
-
const result = runGit(['worktree', 'remove', '--force', path], repoRoot);
|
|
551
|
-
return result.status === 0;
|
|
552
|
-
}
|
|
553
|
-
function hasUncommittedChanges(runGit, dir) {
|
|
554
|
-
// `git diff --quiet` only inspects the working tree against the
|
|
555
|
-
// index, so a `git add`-staged but uncommitted hunk passes that
|
|
556
|
-
// check. Catch the staged case explicitly via `--cached` so the
|
|
557
|
-
// cleanup sweep never drops a worktree with staged work.
|
|
558
|
-
const unstaged = runGit(['diff', '--quiet'], dir);
|
|
559
|
-
if (unstaged.status !== 0)
|
|
560
|
-
return true;
|
|
561
|
-
const staged = runGit(['diff', '--cached', '--quiet'], dir);
|
|
562
|
-
return staged.status !== 0;
|
|
563
|
-
}
|
|
564
|
-
function hasUntrackedFiles(runGit, dir) {
|
|
565
|
-
const r = runGit(['ls-files', '--others', '--exclude-standard'], dir);
|
|
566
|
-
return r.status === 0 && r.stdout.trim().length > 0;
|
|
567
|
-
}
|
|
568
|
-
function hasUnpushedCommits(runGit, dir) {
|
|
569
|
-
const r = runGit(['log', '--branches', '--not', '--remotes', '--oneline'], dir);
|
|
570
|
-
return r.status === 0 && r.stdout.trim().length > 0;
|
|
571
|
-
}
|
|
572
|
-
/** Re-export for symmetry with the dispatcher manager. */
|
|
573
|
-
export function userWorktreePathFor(repoRoot, name) {
|
|
574
|
-
// Posix-join the prefix so the slash separators stay correct on
|
|
575
|
-
// every host, then resolve to absolute on the local FS.
|
|
576
|
-
const rel = posix.join(USER_WORKTREES_PREFIX, name);
|
|
577
|
-
return isAbsolute(rel) ? rel : resolve(repoRoot, rel);
|
|
578
|
-
}
|
|
579
|
-
//# sourceMappingURL=worktree-bootstrap.js.map
|