@pugi/cli 0.1.0-beta.9 → 0.1.0-beta.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +132 -0
- package/LICENSE +1 -1
- package/assets/pugi-prozr2-mascot.ansi +9 -0
- package/bin/run.js +33 -1
- package/dist/commands/deploy.js +40 -40
- package/dist/commands/flatten.js +191 -0
- package/dist/commands/jobs-watch.js +201 -0
- package/dist/commands/jobs.js +42 -27
- package/dist/commands/smoke.js +133 -0
- package/dist/core/agent-progress/cleanup.js +134 -0
- package/dist/core/agent-progress/schema.js +144 -0
- package/dist/core/agent-progress/writer.js +101 -0
- package/dist/core/agents/adaptive-router.js +330 -0
- package/dist/core/agents/query-decomposer.js +297 -0
- package/dist/core/agents/registry.js +3 -3
- package/dist/core/approvals/shortcut-resolver.js +98 -0
- package/dist/core/artifact-chain/dispatcher.js +148 -0
- package/dist/core/artifact-chain/exporter.js +164 -0
- package/dist/core/artifact-chain/state.js +243 -0
- package/dist/core/artifact-chain/steps.js +169 -0
- package/dist/core/ask-user/question.js +92 -0
- package/dist/core/audit/audit-trail.js +275 -0
- package/dist/core/auth/ensure-authenticated.js +129 -0
- package/dist/core/auth/env-provider.js +238 -0
- package/dist/core/auto-open-browser.js +4 -4
- package/dist/core/auto-update/channels.js +122 -0
- package/dist/core/auto-update/checker.js +241 -0
- package/dist/core/auto-update/state.js +235 -0
- package/dist/core/bare-mode/index.js +107 -0
- package/dist/core/bash/redirect.js +281 -0
- package/dist/core/bash-classifier.js +436 -40
- package/dist/core/checkpoint/resumer.js +149 -0
- package/dist/core/checkpoint/rewinder.js +291 -0
- package/dist/core/checkpoints/shadow-git.js +670 -0
- package/dist/core/citations/parser.js +109 -0
- package/dist/core/classifier/yolo-classifier.js +88 -0
- package/dist/core/codegraph/decision-store.js +248 -0
- package/dist/core/codegraph/detect-repo.js +459 -0
- package/dist/core/codegraph/install.js +134 -0
- package/dist/core/codegraph/offer-hook.js +220 -0
- package/dist/core/compact/auto-trigger.js +96 -0
- package/dist/core/compact/buffer-rewriter.js +115 -0
- package/dist/core/compact/summarizer.js +208 -0
- package/dist/core/compact/token-counter.js +108 -0
- package/dist/core/consensus/anvil-fanout.js +25 -25
- package/dist/core/consensus/diff-capture.js +121 -12
- package/dist/core/consensus/rubric.js +21 -21
- package/dist/core/context/builder.js +6 -6
- package/dist/core/context/compaction-events.js +8 -8
- package/dist/core/context/compaction.js +31 -31
- package/dist/core/context/index.js +15 -8
- package/dist/core/context/invariants.js +51 -51
- package/dist/core/context/markdown-loader.js +28 -10
- package/dist/core/context/markdown-traverse.js +255 -0
- package/dist/core/context/pugiignore.js +41 -41
- package/dist/core/context/repo-skeleton.js +37 -37
- package/dist/core/context/tool-eviction.js +55 -0
- package/dist/core/context/watcher.js +32 -32
- package/dist/core/context/working-set.js +23 -23
- package/dist/core/coordinator/agent-tools.js +77 -0
- package/dist/core/coordinator/agent-toolset.js +65 -0
- package/dist/core/coordinator/fsm.js +73 -0
- package/dist/core/coordinator/mode-fsm.js +70 -0
- package/dist/core/cost/rate-card.js +129 -0
- package/dist/core/cost/tracker.js +221 -0
- package/dist/core/credentials.js +13 -13
- package/dist/core/cron/scheduler.js +138 -0
- package/dist/core/denial-tracking/index.js +8 -0
- package/dist/core/denial-tracking/state.js +264 -0
- package/dist/core/diagnostics/probe-runner.js +93 -0
- package/dist/core/diagnostics/probes/api.js +46 -0
- package/dist/core/diagnostics/probes/auth.js +93 -0
- package/dist/core/diagnostics/probes/bare-mode.js +42 -0
- package/dist/core/diagnostics/probes/cli-version.js +127 -0
- package/dist/core/diagnostics/probes/config.js +72 -0
- package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
- package/dist/core/diagnostics/probes/disk.js +81 -0
- package/dist/core/diagnostics/probes/engine-live.js +46 -0
- package/dist/core/diagnostics/probes/git.js +65 -0
- package/dist/core/diagnostics/probes/hooks.js +118 -0
- package/dist/core/diagnostics/probes/mcp.js +75 -0
- package/dist/core/diagnostics/probes/node.js +59 -0
- package/dist/core/diagnostics/probes/pnpm.js +36 -0
- package/dist/core/diagnostics/probes/pugi-md.js +89 -0
- package/dist/core/diagnostics/probes/sandbox.js +40 -0
- package/dist/core/diagnostics/probes/session.js +74 -0
- package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
- package/dist/core/diagnostics/probes/workspace.js +63 -0
- package/dist/core/diagnostics/types.js +70 -0
- package/dist/core/dispatch/cache-cleanup.js +197 -0
- package/dist/core/dispatch/cache-handoff.js +295 -0
- package/dist/core/edits/apply-patch-layer-e.js +189 -0
- package/dist/core/edits/dispatch.js +333 -7
- package/dist/core/edits/format-detector.js +260 -0
- package/dist/core/edits/format-matrix.js +26 -0
- package/dist/core/edits/fuzzy-ladder.js +650 -0
- package/dist/core/edits/index.js +5 -1
- package/dist/core/edits/journal.js +199 -0
- package/dist/core/edits/layer-a-apply.js +15 -15
- package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
- package/dist/core/edits/layer-b-apply.js +9 -9
- package/dist/core/edits/layer-c-apply.js +6 -6
- package/dist/core/edits/layer-d-ast.js +557 -14
- package/dist/core/edits/marker-parser.js +12 -12
- package/dist/core/edits/security-gate.js +27 -27
- package/dist/core/edits/verify-hook.js +273 -0
- package/dist/core/edits/worktree.js +29 -29
- package/dist/core/engine/anvil-client.js +214 -26
- package/dist/core/engine/auto-compact.js +179 -0
- package/dist/core/engine/budgets.js +186 -0
- package/dist/core/engine/context-prefix.js +155 -0
- package/dist/core/engine/index.js +1 -1
- package/dist/core/engine/intensity.js +158 -0
- package/dist/core/engine/intent.js +260 -0
- package/dist/core/engine/native-pugi.js +1295 -227
- package/dist/core/engine/prompts.js +129 -19
- package/dist/core/engine/strip-internal-fields.js +124 -0
- package/dist/core/engine/tool-bridge.js +1792 -59
- package/dist/core/evaluation/golden-dataset.js +293 -0
- package/dist/core/feedback/queue.js +177 -0
- package/dist/core/feedback/submitter.js +145 -0
- package/dist/core/file-cache.js +113 -1
- package/dist/core/flatten/flatten-repo.js +439 -0
- package/dist/core/format/osc8-link.js +28 -0
- package/dist/core/hook-chains.js +392 -0
- package/dist/core/hooks/citation-verify-hook.js +138 -0
- package/dist/core/hooks/citation-verify.js +112 -0
- package/dist/core/hooks/events.js +46 -0
- package/dist/core/hooks/index.js +15 -0
- package/dist/core/hooks/registry.js +216 -0
- package/dist/core/hooks/runner.js +236 -0
- package/dist/core/hooks/v2/event-emitter.js +115 -0
- package/dist/core/hooks/v2/executor.js +282 -0
- package/dist/core/hooks/v2/index.js +25 -0
- package/dist/core/hooks/v2/lifecycle.js +104 -0
- package/dist/core/hooks/v2/loader.js +216 -0
- package/dist/core/hooks/v2/matcher.js +125 -0
- package/dist/core/hooks/v2/trust.js +143 -0
- package/dist/core/hooks/v2/types.js +86 -0
- package/dist/core/hooks/worktree-events.js +158 -0
- package/dist/core/image/renderer.js +71 -0
- package/dist/core/init/detector.js +582 -0
- package/dist/core/init/template-renderer.js +242 -0
- package/dist/core/jobs/registry.js +18 -18
- package/dist/core/ledger/results-tsv.js +142 -0
- package/dist/core/log-discipline/stdout-redirect.js +51 -0
- package/dist/core/lsp/cache.js +105 -0
- package/dist/core/lsp/client.js +551 -41
- package/dist/core/lsp/language-detect.js +66 -0
- package/dist/core/lsp/post-edit-diagnostics.js +171 -0
- package/dist/core/lsp/server-detect.js +173 -0
- package/dist/core/lsp/symbol-cache.js +162 -0
- package/dist/core/lsp/symbol-tools.js +664 -0
- package/dist/core/mcp/client.js +97 -28
- package/dist/core/mcp/http-server.js +553 -0
- package/dist/core/mcp/orchestrator-tools.js +662 -0
- package/dist/core/mcp/permission.js +190 -0
- package/dist/core/mcp/registry.js +39 -17
- package/dist/core/mcp/server-tools.js +219 -0
- package/dist/core/mcp/server.js +397 -0
- package/dist/core/mcp/trust.js +10 -10
- package/dist/core/memory/dual-write.js +416 -0
- package/dist/core/memory/passive-extract.js +130 -0
- package/dist/core/memory/phase1-kinds.js +20 -0
- package/dist/core/memory/secret-scanner.js +304 -0
- package/dist/core/memory-sync/queue.js +170 -0
- package/dist/core/metrics/extract.js +113 -0
- package/dist/core/modes/roo-modes.js +68 -0
- package/dist/core/onboarding/ensure-initialized.js +133 -0
- package/dist/core/onboarding/marker.js +111 -0
- package/dist/core/onboarding/telemetry-state.js +108 -0
- package/dist/core/output-style/presets.js +176 -0
- package/dist/core/output-style/state.js +185 -0
- package/dist/core/path-security.js +287 -5
- package/dist/core/permission.js +82 -22
- package/dist/core/permissions/auto-classifier.js +124 -0
- package/dist/core/permissions/bash-parser.js +371 -0
- package/dist/core/permissions/circuit-breaker.js +83 -0
- package/dist/core/permissions/constrained-edit.js +91 -0
- package/dist/core/permissions/gate.js +278 -0
- package/dist/core/permissions/index.js +20 -0
- package/dist/core/permissions/mode.js +174 -0
- package/dist/core/permissions/network-egress.js +137 -0
- package/dist/core/permissions/state.js +241 -0
- package/dist/core/permissions/tool-class.js +93 -0
- package/dist/core/plan-mode/ui-state.js +51 -0
- package/dist/core/plans/plan-artifact.js +721 -0
- package/dist/core/policy-limits/etag-store.js +122 -0
- package/dist/core/prd-check/parser.js +215 -0
- package/dist/core/prd-check/reporter.js +127 -0
- package/dist/core/prd-check/session-review.js +557 -0
- package/dist/core/prd-check/verifiers.js +223 -0
- package/dist/core/prompt-cache/client-cache.js +99 -0
- package/dist/core/prompts/assembly.js +29 -0
- package/dist/core/prompts/registry.js +364 -0
- package/dist/core/pugi-md/cc-compat-rules.js +735 -0
- package/dist/core/pugi-md/context-injector.js +76 -0
- package/dist/core/pugi-md/walk-up.js +207 -0
- package/dist/core/python/uv-installer.js +270 -0
- package/dist/core/python/uv-resolver.js +83 -0
- package/dist/core/rate-limit/narrator.js +146 -0
- package/dist/core/recipes/cli-types.js +20 -0
- package/dist/core/recipes/loader.js +103 -0
- package/dist/core/recipes/runner.js +345 -0
- package/dist/core/recipes/schema.js +587 -0
- package/dist/core/release-notes/parser.js +241 -0
- package/dist/core/release-notes/state.js +116 -0
- package/dist/core/repl/ask.js +37 -37
- package/dist/core/repl/cancellation.js +26 -26
- package/dist/core/repl/cap-warning.js +4 -4
- package/dist/core/repl/clipboard-read.js +11 -11
- package/dist/core/repl/dispatch-fsm.js +12 -12
- package/dist/core/repl/history-search.js +15 -15
- package/dist/core/repl/history.js +28 -18
- package/dist/core/repl/kill-ring.js +5 -5
- package/dist/core/repl/model-pricing.js +135 -0
- package/dist/core/repl/privacy-banner.js +22 -22
- package/dist/core/repl/session.js +2148 -217
- package/dist/core/repl/slash-commands.js +501 -41
- package/dist/core/repl/store/index.js +1 -1
- package/dist/core/repl/store/jsonl-log.js +22 -22
- package/dist/core/repl/store/lockfile.js +10 -10
- package/dist/core/repl/store/session-store.js +136 -107
- package/dist/core/repl/store/types.js +15 -15
- package/dist/core/repl/store/uuid-v7.js +12 -12
- package/dist/core/repl/workspace-context.js +43 -21
- package/dist/core/repo-map/build.js +125 -0
- package/dist/core/repo-map/cache.js +185 -0
- package/dist/core/repo-map/extractor.js +254 -0
- package/dist/core/repo-map/formatter.js +145 -0
- package/dist/core/repo-map/page-rank.js +105 -0
- package/dist/core/repo-map/scanner.js +211 -0
- package/dist/core/retry-budget/budget.js +284 -0
- package/dist/core/retry-budget/index.js +5 -0
- package/dist/core/retry-budget/retry-cap.js +74 -0
- package/dist/core/routing/lead-worker.js +43 -0
- package/dist/core/routing/pre-flight-estimator.js +108 -0
- package/dist/core/runs/run-tree.js +103 -0
- package/dist/core/security/injection-scanner.js +367 -0
- package/dist/core/security/output-filter.js +418 -0
- package/dist/core/session/env-file.js +105 -0
- package/dist/core/session/section-budgets.js +140 -0
- package/dist/core/session.js +92 -0
- package/dist/core/settings.js +324 -5
- package/dist/core/share/formatter.js +271 -0
- package/dist/core/share/redactor.js +221 -0
- package/dist/core/share/uploader.js +267 -0
- package/dist/core/skills/defaults.js +30 -30
- package/dist/core/skills/loader.js +22 -22
- package/dist/core/skills/sources.js +27 -27
- package/dist/core/smoke/headless-driver.js +174 -0
- package/dist/core/smoke/orchestrator.js +194 -0
- package/dist/core/smoke/runner.js +238 -0
- package/dist/core/smoke/scenario-parser.js +316 -0
- package/dist/core/statusline.js +99 -0
- package/dist/core/subagents/dispatcher-real.js +600 -0
- package/dist/core/subagents/dispatcher.js +132 -43
- package/dist/core/subagents/index.js +19 -6
- package/dist/core/subagents/isolation-matrix.js +213 -0
- package/dist/core/subagents/spawn.js +19 -4
- package/dist/core/telemetry/emitter.js +229 -0
- package/dist/core/telemetry/queue.js +251 -0
- package/dist/core/theme/context.js +91 -0
- package/dist/core/theme/presets.js +228 -0
- package/dist/core/theme/state.js +181 -0
- package/dist/core/todos/invariant.js +10 -0
- package/dist/core/todos/state.js +177 -0
- package/dist/core/tool-schema/compressor.js +89 -0
- package/dist/core/transport/version-interceptor.js +166 -0
- package/dist/core/trust.js +2 -2
- package/dist/core/tui/thinking-block.js +64 -0
- package/dist/core/vim/keymap.js +288 -0
- package/dist/core/vim/state.js +92 -0
- package/dist/core/watch-markers/marker-watcher.js +133 -0
- package/dist/core/worktree/include-parser.js +249 -0
- package/dist/core/worktree-manager/cleanup.js +123 -0
- package/dist/core/worktree-manager/manager.js +303 -0
- package/dist/index.js +36 -0
- package/dist/runtime/bootstrap.js +190 -0
- package/dist/runtime/cli.js +4185 -549
- package/dist/runtime/commands/agents.js +31 -31
- package/dist/runtime/commands/budget.js +5 -5
- package/dist/runtime/commands/cancel.js +231 -0
- package/dist/runtime/commands/chain.js +489 -0
- package/dist/runtime/commands/codegraph-status.js +227 -0
- package/dist/runtime/commands/compact.js +297 -0
- package/dist/runtime/commands/config.js +73 -39
- package/dist/runtime/commands/cost.js +199 -0
- package/dist/runtime/commands/delegate.js +27 -4
- package/dist/runtime/commands/dispatch.js +126 -0
- package/dist/runtime/commands/doctor.js +579 -0
- package/dist/runtime/commands/feedback.js +184 -0
- package/dist/runtime/commands/hooks.js +187 -0
- package/dist/runtime/commands/init.js +254 -0
- package/dist/runtime/commands/lsp.js +200 -38
- package/dist/runtime/commands/mcp.js +879 -0
- package/dist/runtime/commands/memory.js +582 -0
- package/dist/runtime/commands/model.js +237 -0
- package/dist/runtime/commands/onboarding.js +275 -0
- package/dist/runtime/commands/patch.js +12 -12
- package/dist/runtime/commands/permissions.js +112 -0
- package/dist/runtime/commands/plan.js +143 -0
- package/dist/runtime/commands/prd-check.js +285 -0
- package/dist/runtime/commands/privacy.js +17 -17
- package/dist/runtime/commands/recipe.js +325 -0
- package/dist/runtime/commands/redo-blob-store.js +92 -0
- package/dist/runtime/commands/redo.js +361 -0
- package/dist/runtime/commands/release-notes.js +229 -0
- package/dist/runtime/commands/repo-map.js +95 -0
- package/dist/runtime/commands/report.js +299 -0
- package/dist/runtime/commands/resume.js +118 -0
- package/dist/runtime/commands/review-consensus.js +68 -53
- package/dist/runtime/commands/rewind.js +333 -0
- package/dist/runtime/commands/roster.js +14 -14
- package/dist/runtime/commands/sessions.js +163 -0
- package/dist/runtime/commands/share.js +316 -0
- package/dist/runtime/commands/skills.js +31 -31
- package/dist/runtime/commands/status.js +186 -0
- package/dist/runtime/commands/stickers.js +82 -0
- package/dist/runtime/commands/style.js +194 -0
- package/dist/runtime/commands/theme.js +196 -0
- package/dist/runtime/commands/undo.js +54 -22
- package/dist/runtime/commands/update.js +289 -0
- package/dist/runtime/commands/vim.js +140 -0
- package/dist/runtime/commands/worktree.js +8 -8
- package/dist/runtime/commands/worktrees.js +155 -0
- package/dist/runtime/headless-repl.js +195 -0
- package/dist/runtime/headless.js +543 -0
- package/dist/runtime/load-hooks-or-exit.js +71 -0
- package/dist/runtime/plan-decompose.js +22 -22
- package/dist/runtime/sigint-guard.js +272 -0
- package/dist/runtime/update-check.js +28 -28
- package/dist/runtime/version.js +65 -0
- package/dist/runtime/worktree-bootstrap.js +579 -0
- package/dist/skills/bundled/batch.js +617 -0
- package/dist/skills/bundled/index.js +45 -0
- package/dist/skills/bundled/loop.js +358 -0
- package/dist/skills/bundled/remember.js +383 -0
- package/dist/skills/bundled/simplify.js +289 -0
- package/dist/skills/bundled/skillify.js +373 -0
- package/dist/skills/bundled/stuck.js +558 -0
- package/dist/skills/bundled/verify.js +439 -0
- package/dist/testing/vcr.js +486 -0
- package/dist/tools/agent-tool.js +229 -0
- package/dist/tools/apply-patch.js +89 -28
- package/dist/tools/ask-user-question.js +337 -0
- package/dist/tools/ask-user.js +115 -0
- package/dist/tools/bash.js +624 -46
- package/dist/tools/brief.js +224 -0
- package/dist/tools/cron.js +433 -0
- package/dist/tools/enter-worktree.js +250 -0
- package/dist/tools/exit-worktree.js +147 -0
- package/dist/tools/file-tools.js +161 -44
- package/dist/tools/lsp-tools.js +377 -1
- package/dist/tools/mcp-tool.js +260 -0
- package/dist/tools/multi-edit.js +361 -0
- package/dist/tools/powershell.js +268 -0
- package/dist/tools/registry.js +99 -4
- package/dist/tools/skill-tool.js +96 -0
- package/dist/tools/sleep.js +99 -0
- package/dist/tools/synthetic-output.js +133 -0
- package/dist/tools/tasks.js +208 -0
- package/dist/tools/todo-write.js +184 -0
- package/dist/tools/verify-plan-execution.js +295 -0
- package/dist/tools/web-fetch-injection-scanner.js +207 -0
- package/dist/tools/web-fetch.js +195 -10
- package/dist/tools/web-search.js +458 -0
- package/dist/tui/agent-progress-card.js +111 -0
- package/dist/tui/agent-tree.js +11 -1
- package/dist/tui/ask-modal.js +14 -14
- package/dist/tui/ask-user-question-chips.js +315 -0
- package/dist/tui/ask-user-question-prompt.js +203 -0
- package/dist/tui/compact-banner.js +81 -0
- package/dist/tui/conversation-pane.js +85 -11
- package/dist/tui/cost-table.js +111 -0
- package/dist/tui/device-flow.js +2 -2
- package/dist/tui/doctor-table.js +46 -0
- package/dist/tui/feedback-prompt.js +156 -0
- package/dist/tui/input-box.js +247 -32
- package/dist/tui/login-picker.js +3 -3
- package/dist/tui/markdown-render.js +6 -6
- package/dist/tui/multi-file-diff-approval.js +375 -0
- package/dist/tui/onboarding-wizard.js +240 -0
- package/dist/tui/permissions-picker.js +86 -0
- package/dist/tui/render.js +36 -1
- package/dist/tui/repl-render.js +176 -25
- package/dist/tui/repl-splash-art.js +16 -16
- package/dist/tui/repl-splash-mascot.js +48 -24
- package/dist/tui/repl-splash.js +22 -22
- package/dist/tui/repl.js +125 -45
- package/dist/tui/slash-palette.js +6 -6
- package/dist/tui/splash.js +2 -2
- package/dist/tui/status-bar.js +109 -31
- package/dist/tui/status-table.js +7 -0
- package/dist/tui/stickers-art.js +136 -0
- package/dist/tui/style-table.js +28 -0
- package/dist/tui/theme-table.js +29 -0
- package/dist/tui/thinking-spinner.js +123 -0
- package/dist/tui/tool-stream-pane.js +53 -4
- package/dist/tui/update-banner.js +27 -2
- package/dist/tui/vim-input.js +267 -0
- package/dist/tui/welcome-banner.js +107 -0
- package/dist/tui/welcome-data.js +293 -0
- package/dist/tui/workspace-context.js +2 -2
- package/package.json +31 -16
- package/test/scenarios/codegen-create-file.scenario.txt +13 -0
- package/test/scenarios/compact-force.scenario.txt +12 -0
- package/test/scenarios/identity.scenario.txt +12 -0
- package/test/scenarios/persona-handoff.scenario.txt +12 -0
- package/test/scenarios/walkback.scenario.txt +12 -0
- package/dist/core/engine/compaction-hook.js +0 -154
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watch-mode inline marker scanner — Aider AI?/AI! pattern.
|
|
3
|
+
*
|
|
4
|
+
* Watches a directory tree via chokidar (already в deps — used by
|
|
5
|
+
* VS Code и most JS file-watchers). When a file changes, scans the
|
|
6
|
+
* delta для inline markers и emits structured events. Pugi-flavoured
|
|
7
|
+
* marker pair:
|
|
8
|
+
*
|
|
9
|
+
* // PUGI? → operator asks Pugi a question right here
|
|
10
|
+
* // PUGI! → operator instructs Pugi to act right here
|
|
11
|
+
*
|
|
12
|
+
* Also accepts Aider-compatible `// AI?` / `// AI!` for muscle-memory
|
|
13
|
+
* parity. Block comments and `# PUGI?` (Python / shell) are recognised
|
|
14
|
+
* too — language is detected from extension, NOT parsed properly. The
|
|
15
|
+
* goal is opportunistic ergonomics, not a precise parser.
|
|
16
|
+
*
|
|
17
|
+
* No tool dispatch happens here — this module emits events; downstream
|
|
18
|
+
* code (engine integration follow-up) decides what to do.
|
|
19
|
+
*/
|
|
20
|
+
import chokidar from 'chokidar';
|
|
21
|
+
import { readFile } from 'node:fs/promises';
|
|
22
|
+
import { EventEmitter } from 'node:events';
|
|
23
|
+
import path from 'node:path';
|
|
24
|
+
const DEFAULT_IGNORED = [
|
|
25
|
+
'**/node_modules/**',
|
|
26
|
+
'**/.git/**',
|
|
27
|
+
'**/dist/**',
|
|
28
|
+
'**/build/**',
|
|
29
|
+
'**/coverage/**',
|
|
30
|
+
'**/.next/**',
|
|
31
|
+
'**/.pugi/**',
|
|
32
|
+
];
|
|
33
|
+
const DEFAULT_MAX_FILE_BYTES = 256 * 1024;
|
|
34
|
+
// Recognises `// PUGI?`, `# PUGI!`, `/* AI? ... */`, `<!-- PUGI! ... -->` and equivalents.
|
|
35
|
+
// Capture group 1 = comment prefix, 2 = tag (PUGI | AI), 3 = symbol (? | !), 4 = trailing text.
|
|
36
|
+
const MARKER_RE = /(?:\/\/|#|\/\*|<!--)\s*(PUGI|AI)([?!])\s*([^\n*-]*?)(?:\*\/|-->|$)/g;
|
|
37
|
+
export class MarkerWatcher extends EventEmitter {
|
|
38
|
+
options;
|
|
39
|
+
watcher = null;
|
|
40
|
+
maxFileBytes;
|
|
41
|
+
constructor(options = {}) {
|
|
42
|
+
super();
|
|
43
|
+
this.options = options;
|
|
44
|
+
this.maxFileBytes = options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Start watching `patterns`. Multiple calls without close() throw —
|
|
48
|
+
* one instance, one active watcher.
|
|
49
|
+
*/
|
|
50
|
+
start(patterns) {
|
|
51
|
+
if (this.watcher) {
|
|
52
|
+
throw new Error('MarkerWatcher already started — call close() first');
|
|
53
|
+
}
|
|
54
|
+
const ignored = [...(this.options.ignored ?? DEFAULT_IGNORED)];
|
|
55
|
+
const opts = {
|
|
56
|
+
ignored,
|
|
57
|
+
ignoreInitial: this.options.ignoreInitial ?? true,
|
|
58
|
+
persistent: true,
|
|
59
|
+
};
|
|
60
|
+
if (this.options.cwd !== undefined) {
|
|
61
|
+
opts.cwd = this.options.cwd;
|
|
62
|
+
}
|
|
63
|
+
this.watcher = chokidar.watch(patterns, opts);
|
|
64
|
+
this.watcher.on('add', p => void this.scan(p));
|
|
65
|
+
this.watcher.on('change', p => void this.scan(p));
|
|
66
|
+
this.watcher.on('error', err => this.emit('error', err));
|
|
67
|
+
}
|
|
68
|
+
/** Stop watching и release file handles. Safe to call multiple times. */
|
|
69
|
+
async close() {
|
|
70
|
+
if (!this.watcher)
|
|
71
|
+
return;
|
|
72
|
+
await this.watcher.close();
|
|
73
|
+
this.watcher = null;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Scan a single file synchronously — bypasses the watcher entirely.
|
|
77
|
+
* Useful для one-shot scans / tests / initial-load passes.
|
|
78
|
+
*/
|
|
79
|
+
async scanOnce(file) {
|
|
80
|
+
return this.collectFromFile(file);
|
|
81
|
+
}
|
|
82
|
+
async scan(file) {
|
|
83
|
+
const hits = await this.collectFromFile(file);
|
|
84
|
+
for (const hit of hits) {
|
|
85
|
+
this.emit('marker', hit);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async collectFromFile(file) {
|
|
89
|
+
let content;
|
|
90
|
+
// chokidar emits paths relative to its `cwd` option when set. Resolve
|
|
91
|
+
// so readFile doesn't fall back to the process CWD и miss the file.
|
|
92
|
+
const resolved = this.options.cwd && !path.isAbsolute(file)
|
|
93
|
+
? path.join(this.options.cwd, file)
|
|
94
|
+
: file;
|
|
95
|
+
try {
|
|
96
|
+
const buf = await readFile(resolved);
|
|
97
|
+
if (buf.byteLength > this.maxFileBytes)
|
|
98
|
+
return [];
|
|
99
|
+
content = buf.toString('utf8');
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
// File deleted between event and read, permissions, etc.
|
|
103
|
+
this.emit('error', err);
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
const hits = [];
|
|
107
|
+
const lines = content.split('\n');
|
|
108
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
109
|
+
const lineText = lines[i] ?? '';
|
|
110
|
+
MARKER_RE.lastIndex = 0;
|
|
111
|
+
let match;
|
|
112
|
+
while ((match = MARKER_RE.exec(lineText)) !== null) {
|
|
113
|
+
const tag = match[1] ?? 'PUGI';
|
|
114
|
+
const symbol = match[2] ?? '?';
|
|
115
|
+
const text = (match[3] ?? '').trim();
|
|
116
|
+
hits.push({
|
|
117
|
+
file,
|
|
118
|
+
line: i + 1,
|
|
119
|
+
kind: symbol === '!' ? 'instruction' : 'question',
|
|
120
|
+
tag,
|
|
121
|
+
text,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return hits;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/** Standalone scan helper для callers that don't want a long-lived watcher. */
|
|
129
|
+
export async function scanFileForMarkers(file, options = {}) {
|
|
130
|
+
const watcher = new MarkerWatcher({ maxFileBytes: options.maxFileBytes });
|
|
131
|
+
return watcher.scanOnce(file);
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=marker-watcher.js.map
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `.worktreeinclude` parser - PUGI-487.
|
|
3
|
+
*
|
|
4
|
+
* The `.worktreeinclude` file lives at the repository root and carries
|
|
5
|
+
* gitignore-syntax patterns. Files that:
|
|
6
|
+
*
|
|
7
|
+
* 1. Match at least one pattern in `.worktreeinclude`, AND
|
|
8
|
+
* 2. Are git-ignored (untracked-and-ignored OR explicitly ignored),
|
|
9
|
+
*
|
|
10
|
+
* get COPIED into a fresh `pugi --worktree <name>` tree right after
|
|
11
|
+
* `git worktree add` finishes. Tracked files are NEVER copied because
|
|
12
|
+
* git already brings them along on its own, and double-writing would
|
|
13
|
+
* corrupt the worktree index.
|
|
14
|
+
*
|
|
15
|
+
* Why a separate include file instead of `.gitignore` negation
|
|
16
|
+
* patterns?
|
|
17
|
+
*
|
|
18
|
+
* - The common case is "I have an untracked `.env` with API keys that
|
|
19
|
+
* every parallel session needs". Listing it in `.gitignore` with
|
|
20
|
+
* a negation would force-add it to the repo, defeating the point.
|
|
21
|
+
* - Operators want a single-file, copy-pasteable manifest of WHAT
|
|
22
|
+
* leaves the main tree, not a derived view across `.gitignore`
|
|
23
|
+
* semantics.
|
|
24
|
+
*
|
|
25
|
+
* Pattern grammar (subset of gitignore):
|
|
26
|
+
*
|
|
27
|
+
* - Lines starting with `#` are comments. Blank lines are skipped.
|
|
28
|
+
* - Trailing `/` means directory-only and applies recursively to
|
|
29
|
+
* every file under the directory.
|
|
30
|
+
* - Leading `!` is a negation pattern that REMOVES matches from
|
|
31
|
+
* prior includes. Multiple negations are processed in order.
|
|
32
|
+
* - `*` matches any character except `/`.
|
|
33
|
+
* - `**` matches any number of path segments.
|
|
34
|
+
* - `?` matches a single non-`/` character.
|
|
35
|
+
* - Patterns with NO `/` match anywhere in the tree (e.g. `.env`
|
|
36
|
+
* matches both `./.env` and `./apps/foo/.env`).
|
|
37
|
+
* - Patterns starting with `/` are anchored to the repository root.
|
|
38
|
+
*
|
|
39
|
+
* Brand voice: ASCII only, no emoji, no banned words.
|
|
40
|
+
*/
|
|
41
|
+
import { spawnSync } from 'node:child_process';
|
|
42
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
43
|
+
import { resolve, sep, posix } from 'node:path';
|
|
44
|
+
/** Default file name. */
|
|
45
|
+
export const WORKTREE_INCLUDE_FILENAME = '.worktreeinclude';
|
|
46
|
+
/**
|
|
47
|
+
* Parse the textual contents of a `.worktreeinclude` file into a rule
|
|
48
|
+
* list. Pure function - does not touch the filesystem. Exposed for
|
|
49
|
+
* spec coverage and the cleanup/dry-run path.
|
|
50
|
+
*/
|
|
51
|
+
export function parseWorktreeIncludeText(text, source = WORKTREE_INCLUDE_FILENAME) {
|
|
52
|
+
const rules = [];
|
|
53
|
+
const lines = text.split(/\r?\n/);
|
|
54
|
+
for (const raw of lines) {
|
|
55
|
+
const line = raw.trim();
|
|
56
|
+
if (line.length === 0)
|
|
57
|
+
continue;
|
|
58
|
+
if (line.startsWith('#'))
|
|
59
|
+
continue;
|
|
60
|
+
let body = line;
|
|
61
|
+
let negate = false;
|
|
62
|
+
if (body.startsWith('!')) {
|
|
63
|
+
negate = true;
|
|
64
|
+
body = body.slice(1);
|
|
65
|
+
}
|
|
66
|
+
if (body.length === 0)
|
|
67
|
+
continue;
|
|
68
|
+
let directoryOnly = false;
|
|
69
|
+
if (body.endsWith('/')) {
|
|
70
|
+
directoryOnly = true;
|
|
71
|
+
body = body.slice(0, -1);
|
|
72
|
+
}
|
|
73
|
+
let anchored = false;
|
|
74
|
+
if (body.startsWith('/')) {
|
|
75
|
+
anchored = true;
|
|
76
|
+
body = body.slice(1);
|
|
77
|
+
}
|
|
78
|
+
else if (body.includes('/')) {
|
|
79
|
+
// a non-leading slash also anchors per gitignore semantics
|
|
80
|
+
anchored = true;
|
|
81
|
+
}
|
|
82
|
+
if (body.length === 0)
|
|
83
|
+
continue;
|
|
84
|
+
rules.push({
|
|
85
|
+
raw: line,
|
|
86
|
+
pattern: body,
|
|
87
|
+
negate,
|
|
88
|
+
directoryOnly,
|
|
89
|
+
anchored,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return { rules, source };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Load + parse `.worktreeinclude` from a repository root. Returns an
|
|
96
|
+
* empty rule set when the file is absent or unreadable - a missing
|
|
97
|
+
* include file is the normal case (no extras to copy).
|
|
98
|
+
*/
|
|
99
|
+
export function loadWorktreeInclude(repoRoot) {
|
|
100
|
+
const path = resolve(repoRoot, WORKTREE_INCLUDE_FILENAME);
|
|
101
|
+
if (!existsSync(path)) {
|
|
102
|
+
return { rules: [], source: path };
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const text = readFileSync(path, 'utf8');
|
|
106
|
+
return parseWorktreeIncludeText(text, path);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return { rules: [], source: path };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const REGEX_ESCAPE = /[.+^${}()|[\]\\]/;
|
|
113
|
+
/**
|
|
114
|
+
* Convert a gitignore-style glob pattern into a regex source string.
|
|
115
|
+
* Subset implementation: covers `*`, `**`, `?`, and escapes the rest.
|
|
116
|
+
*
|
|
117
|
+
* Uses index-based scanning so `**` is detected and consumed before a
|
|
118
|
+
* single `*` substitution could eat the doubled form.
|
|
119
|
+
*/
|
|
120
|
+
function globToRegexSource(pattern) {
|
|
121
|
+
let out = '';
|
|
122
|
+
for (let i = 0; i < pattern.length; i += 1) {
|
|
123
|
+
const ch = pattern.charAt(i);
|
|
124
|
+
if (ch === '*') {
|
|
125
|
+
if (pattern.charAt(i + 1) === '*') {
|
|
126
|
+
out += '.*';
|
|
127
|
+
i += 1;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
out += '[^/]*';
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (ch === '?') {
|
|
134
|
+
out += '[^/]';
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (REGEX_ESCAPE.test(ch)) {
|
|
138
|
+
out += '\\' + ch;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
out += ch;
|
|
142
|
+
}
|
|
143
|
+
return out;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Decide whether `relPath` (a forward-slash, no-leading-slash path
|
|
147
|
+
* relative to the repo root) is matched by the given rule list. The
|
|
148
|
+
* last matching rule wins so a negation after an include drops the
|
|
149
|
+
* entry and an include after a negation re-adds it.
|
|
150
|
+
*/
|
|
151
|
+
export function matchesWorktreeInclude(relPath, rules, isDirectory = false) {
|
|
152
|
+
const normalized = relPath.replace(/\\+/g, '/');
|
|
153
|
+
let matched = false;
|
|
154
|
+
for (const rule of rules) {
|
|
155
|
+
if (rule.directoryOnly && !isDirectory) {
|
|
156
|
+
// directory-only rule still matches files UNDER the dir (handled
|
|
157
|
+
// below). Falls through to the file-match attempt.
|
|
158
|
+
}
|
|
159
|
+
const source = buildRegex(rule);
|
|
160
|
+
const re = new RegExp('^' + source + '$');
|
|
161
|
+
if (re.test(normalized)) {
|
|
162
|
+
matched = !rule.negate;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (rule.directoryOnly) {
|
|
166
|
+
const reUnder = new RegExp('^' + source + '/.*$');
|
|
167
|
+
if (reUnder.test(normalized)) {
|
|
168
|
+
matched = !rule.negate;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return matched;
|
|
173
|
+
}
|
|
174
|
+
function buildRegex(rule) {
|
|
175
|
+
const body = globToRegexSource(rule.pattern);
|
|
176
|
+
if (rule.anchored) {
|
|
177
|
+
return body;
|
|
178
|
+
}
|
|
179
|
+
// Unanchored pattern matches anywhere in the tree. Allow a
|
|
180
|
+
// segment-aligned match by accepting either start-of-string or a
|
|
181
|
+
// `/` boundary before the pattern body.
|
|
182
|
+
return '(?:.*/)?' + body;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Walk `repoRoot` and return every git-ignored file path that matches
|
|
186
|
+
* the include rules. Tracked files are excluded because they ride
|
|
187
|
+
* along with git automatically.
|
|
188
|
+
*
|
|
189
|
+
* Implementation note: rather than re-implement gitignore evaluation
|
|
190
|
+
* the default lister shells out to
|
|
191
|
+
* `git ls-files --others --ignored --exclude-standard` to get the
|
|
192
|
+
* authoritative ignored-untracked list. Tracked files never appear in
|
|
193
|
+
* that output, so the result is exactly the safe-to-copy candidate
|
|
194
|
+
* pool.
|
|
195
|
+
*/
|
|
196
|
+
export function collectWorktreeIncludeFiles(repoRoot, rules, options = {}) {
|
|
197
|
+
if (rules.length === 0)
|
|
198
|
+
return [];
|
|
199
|
+
const lister = options.listIgnored ?? defaultListIgnored;
|
|
200
|
+
const stat = options.isDirectory ?? defaultIsDirectory;
|
|
201
|
+
const candidates = lister(resolve(repoRoot));
|
|
202
|
+
const out = [];
|
|
203
|
+
for (const candidate of candidates) {
|
|
204
|
+
const rel = candidate.replace(/\\+/g, '/').replace(/^\.\//, '');
|
|
205
|
+
if (rel.length === 0)
|
|
206
|
+
continue;
|
|
207
|
+
if (rel.startsWith('.pugi/') || rel.startsWith('.claude/')) {
|
|
208
|
+
// Never copy Pugi or .claude state into the worktree. Those
|
|
209
|
+
// dirs are owned by the runtime and the include file should
|
|
210
|
+
// not be able to leak audit logs or stale session state.
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
const absPath = resolve(repoRoot, rel);
|
|
214
|
+
const isDir = stat(absPath);
|
|
215
|
+
if (matchesWorktreeInclude(rel, rules, isDir)) {
|
|
216
|
+
out.push(rel);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return out;
|
|
220
|
+
}
|
|
221
|
+
function defaultListIgnored(repoRoot) {
|
|
222
|
+
// `-z` emits NUL-separated paths, the only safe form when filenames
|
|
223
|
+
// may contain spaces or newlines.
|
|
224
|
+
const result = spawnSync('git', ['ls-files', '--others', '--ignored', '--exclude-standard', '-z'], {
|
|
225
|
+
cwd: repoRoot,
|
|
226
|
+
encoding: 'buffer',
|
|
227
|
+
shell: false,
|
|
228
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
229
|
+
});
|
|
230
|
+
if (result.status !== 0 || !result.stdout)
|
|
231
|
+
return [];
|
|
232
|
+
const text = result.stdout.toString('utf8');
|
|
233
|
+
return text.split('\u0000').filter((s) => s.length > 0);
|
|
234
|
+
}
|
|
235
|
+
function defaultIsDirectory(absPath) {
|
|
236
|
+
try {
|
|
237
|
+
return statSync(absPath).isDirectory();
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/** Cross-platform path-join in posix form (for matching semantics). */
|
|
244
|
+
export function joinRel(parent, child) {
|
|
245
|
+
return posix.join(parent.replace(/\\+/g, '/'), child);
|
|
246
|
+
}
|
|
247
|
+
/** Re-export the system separator so consumers do not have to import it. */
|
|
248
|
+
export const PATH_SEP = sep;
|
|
249
|
+
//# sourceMappingURL=include-parser.js.map
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stale-worktree sweep — .
|
|
3
|
+
*
|
|
4
|
+
* Pairs with `WorktreeManager` to find worktrees whose agent has either:
|
|
5
|
+
*
|
|
6
|
+
* 1. No corresponding progress JSON under `.pugi/agent-progress/` (the
|
|
7
|
+
* writer never wrote one, OR the file was archived away), OR
|
|
8
|
+
* 2. A progress JSON whose `status` is `completed` or `failed` (the
|
|
9
|
+
* agent finished — the worktree is no longer load-bearing).
|
|
10
|
+
*
|
|
11
|
+
* Crashed agents (status `running` with a stale lastUpdate) are NOT
|
|
12
|
+
* swept; the leak-research contract says we LEAVE worktrees of crashed
|
|
13
|
+
* agents in place for forensics. The operator can `pugi worktrees
|
|
14
|
+
* cleanup <id>` manually after the post-mortem.
|
|
15
|
+
*
|
|
16
|
+
* Implementation notes:
|
|
17
|
+
*
|
|
18
|
+
* - The cleanup is read-then-write: list the manager state, classify
|
|
19
|
+
* each entry, then call `manager.cleanup()` per stale id. Errors per
|
|
20
|
+
* entry never abort the loop — they accumulate in the report.
|
|
21
|
+
* - The progress directory we look at is the SAME `.pugi/agent-progress/`
|
|
22
|
+
* root the writer uses (resolved via the writer's env-aware helper).
|
|
23
|
+
* - `runStaleCleanup` is the only public entry point. The internal
|
|
24
|
+
* classifier is exported as `classifyStale` for spec coverage.
|
|
25
|
+
*
|
|
26
|
+
* Brand voice: ASCII only, no emoji, no banned words.
|
|
27
|
+
*/
|
|
28
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
29
|
+
import { join } from 'node:path';
|
|
30
|
+
import { resolveProgressDir } from '../agent-progress/writer.js';
|
|
31
|
+
import { validateAgentProgress } from '../agent-progress/schema.js';
|
|
32
|
+
import { WorktreeManager, } from './manager.js';
|
|
33
|
+
/**
|
|
34
|
+
* Classify a single worktree listing against the on-disk progress JSON.
|
|
35
|
+
* Pure-ish — only reads from the progress file when one exists. Exposed
|
|
36
|
+
* for the spec; production callers go through `runStaleCleanup`.
|
|
37
|
+
*/
|
|
38
|
+
export function classifyStale(listing, progressDir, options = {}) {
|
|
39
|
+
const grace = options.crashedGraceMs ?? 30 * 60 * 1000;
|
|
40
|
+
const now = options.now ?? Date.now;
|
|
41
|
+
const progressPath = join(progressDir, `${listing.agentId}.json`);
|
|
42
|
+
if (!existsSync(progressPath)) {
|
|
43
|
+
return 'orphan_no_progress';
|
|
44
|
+
}
|
|
45
|
+
let raw;
|
|
46
|
+
try {
|
|
47
|
+
raw = readFileSync(progressPath, 'utf8');
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Unreadable progress file — treat as orphan so the operator can
|
|
51
|
+
// see it in the cleanup report. We never act on it silently.
|
|
52
|
+
return 'orphan_no_progress';
|
|
53
|
+
}
|
|
54
|
+
let parsed;
|
|
55
|
+
try {
|
|
56
|
+
parsed = JSON.parse(raw);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return 'orphan_no_progress';
|
|
60
|
+
}
|
|
61
|
+
const validation = validateAgentProgress(parsed);
|
|
62
|
+
if (!validation.ok)
|
|
63
|
+
return 'orphan_no_progress';
|
|
64
|
+
const progress = validation.value;
|
|
65
|
+
if (progress.status === 'completed')
|
|
66
|
+
return 'stale_completed';
|
|
67
|
+
if (progress.status === 'failed')
|
|
68
|
+
return 'stale_failed';
|
|
69
|
+
// status === 'running' — check for crashed (stale lastUpdate)
|
|
70
|
+
const lastTs = Date.parse(progress.lastUpdate);
|
|
71
|
+
if (Number.isFinite(lastTs) && now() - lastTs > grace) {
|
|
72
|
+
return 'crashed_running';
|
|
73
|
+
}
|
|
74
|
+
return 'active';
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Run a single stale-worktree sweep. Removes only `stale_completed`,
|
|
78
|
+
* `stale_failed`, and `orphan_no_progress` entries. Crashed-running
|
|
79
|
+
* entries are LEFT IN PLACE per the L23 forensics contract, but they
|
|
80
|
+
* appear in the report so the operator can act on them manually.
|
|
81
|
+
*/
|
|
82
|
+
export function runStaleCleanup(managerOpts, options = {}) {
|
|
83
|
+
const manager = new WorktreeManager(managerOpts);
|
|
84
|
+
const report = {
|
|
85
|
+
scanned: 0,
|
|
86
|
+
classified: [],
|
|
87
|
+
removed: [],
|
|
88
|
+
preserved: [],
|
|
89
|
+
errors: [],
|
|
90
|
+
};
|
|
91
|
+
const listResult = manager.list();
|
|
92
|
+
if (!listResult.ok) {
|
|
93
|
+
report.errors.push({ agentId: '', detail: `list failed: ${listResult.detail}` });
|
|
94
|
+
return report;
|
|
95
|
+
}
|
|
96
|
+
const progressDir = options.progressDir ?? resolveProgressDir();
|
|
97
|
+
const listings = listResult.value;
|
|
98
|
+
report.scanned = listings.length;
|
|
99
|
+
for (const entry of listings) {
|
|
100
|
+
const cls = classifyStale(entry, progressDir, options);
|
|
101
|
+
report.classified.push({ agentId: entry.agentId, path: entry.path, class: cls });
|
|
102
|
+
if (cls === 'active' || cls === 'crashed_running') {
|
|
103
|
+
report.preserved.push({ agentId: entry.agentId, reason: cls });
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (options.dryRun) {
|
|
107
|
+
// Dry-run reports the classification only — no cleanup, no remove.
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const cleanup = manager.cleanup(entry.agentId);
|
|
111
|
+
if (cleanup.ok) {
|
|
112
|
+
report.removed.push(entry.path);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
report.errors.push({
|
|
116
|
+
agentId: entry.agentId,
|
|
117
|
+
detail: `${cleanup.reason}: ${cleanup.detail}`,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return report;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=cleanup.js.map
|