@opengsd/gsd-core 1.10.0 → 1.12.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +12 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-doc-synthesizer.md +2 -4
- package/agents/gsd-dom-verifier.md +169 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +22 -14
- package/agents/gsd-framework-selector.md +1 -3
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +5 -3
- package/agents/gsd-pattern-mapper.md +11 -0
- package/agents/gsd-phase-researcher.md +23 -2
- package/agents/gsd-plan-checker.md +50 -53
- package/agents/gsd-planner.md +50 -50
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.md +2 -2
- package/agents/gsd-roadmapper.md +15 -11
- package/agents/gsd-ui-checker.md +63 -4
- package/agents/gsd-ui-researcher.md +41 -3
- package/agents/gsd-user-profiler.md +3 -0
- package/agents/gsd-verifier.md +13 -4
- package/bin/install.js +1448 -1103
- package/commands/gsd/code-review.md +1 -1
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/import.md +1 -1
- package/commands/gsd/map-codebase.md +1 -1
- package/commands/gsd/mempalace-capture.md +1 -1
- package/commands/gsd/mempalace-recall.md +1 -1
- package/commands/gsd/new-milestone.md +1 -1
- package/commands/gsd/quick.md +9 -5
- package/commands/gsd/review-backlog.md +2 -1
- package/commands/gsd/verify-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +1035 -138
- package/gsd-core/bin/lib/active-workstream-store.cjs +146 -22
- package/gsd-core/bin/lib/adr-parser.cjs +13 -7
- package/gsd-core/bin/lib/agent-install-check.cjs +392 -32
- package/gsd-core/bin/lib/api-coverage.cjs +33 -14
- package/gsd-core/bin/lib/artifacts.cjs +5 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +32 -15
- package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
- package/gsd-core/bin/lib/audit.cjs +1026 -268
- package/gsd-core/bin/lib/broken-windows.cjs +306 -28
- package/gsd-core/bin/lib/capability-consent.cjs +149 -15
- package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
- package/gsd-core/bin/lib/capability-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +845 -130
- package/gsd-core/bin/lib/capability-source.cjs +92 -0
- package/gsd-core/bin/lib/capability-state.cjs +18 -3
- package/gsd-core/bin/lib/capability-trust.cjs +444 -25
- package/gsd-core/bin/lib/capability-validator.cjs +700 -40
- package/gsd-core/bin/lib/capability-writer.cjs +3 -2
- package/gsd-core/bin/lib/check-command-router.cjs +216 -42
- package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +735 -0
- package/gsd-core/bin/lib/command-aliases.cjs +22 -0
- package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
- package/gsd-core/bin/lib/command-roster.cjs +44 -1
- package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
- package/gsd-core/bin/lib/commands.cjs +1172 -108
- package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
- package/gsd-core/bin/lib/complexity-trigger.cjs +1192 -0
- package/gsd-core/bin/lib/config-loader.cjs +187 -23
- package/gsd-core/bin/lib/config.cjs +102 -3
- package/gsd-core/bin/lib/configuration.cjs +129 -37
- package/gsd-core/bin/lib/core-utils.cjs +208 -33
- package/gsd-core/bin/lib/decisions.cjs +23 -0
- package/gsd-core/bin/lib/edge-probe.cjs +9 -1
- package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
- package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
- package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
- package/gsd-core/bin/lib/frontmatter.cjs +899 -229
- package/gsd-core/bin/lib/gap-checker.cjs +95 -10
- package/gsd-core/bin/lib/git-base-branch.cjs +276 -39
- package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
- package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +149 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +268 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +187 -0
- package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
- package/gsd-core/bin/lib/health-diagnostic.cjs +451 -0
- package/gsd-core/bin/lib/host-integration.cjs +39 -6
- package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
- package/gsd-core/bin/lib/init-command-router.cjs +118 -21
- package/gsd-core/bin/lib/init.cjs +439 -168
- package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
- package/gsd-core/bin/lib/install-engine.cjs +811 -259
- package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +235 -0
- package/gsd-core/bin/lib/install-profiles.cjs +212 -61
- package/gsd-core/bin/lib/install-scope.cjs +270 -0
- package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
- package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
- package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
- package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +148 -38
- package/gsd-core/bin/lib/intel.cjs +101 -26
- package/gsd-core/bin/lib/io.cjs +170 -15
- package/gsd-core/bin/lib/learnings.cjs +85 -14
- package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
- package/gsd-core/bin/lib/markdown-table.cjs +183 -22
- package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
- package/gsd-core/bin/lib/milestone.cjs +842 -73
- package/gsd-core/bin/lib/model-catalog.cjs +232 -16
- package/gsd-core/bin/lib/model-resolver.cjs +193 -68
- package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
- package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
- package/gsd-core/bin/lib/pattern.cjs +122 -0
- package/gsd-core/bin/lib/phase-estimation.cjs +18 -9
- package/gsd-core/bin/lib/phase-id.cjs +514 -40
- package/gsd-core/bin/lib/phase-lifecycle.cjs +52 -19
- package/gsd-core/bin/lib/phase-locator.cjs +262 -34
- package/gsd-core/bin/lib/phase.cjs +1038 -214
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
- package/gsd-core/bin/lib/plan-document.cjs +263 -0
- package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
- package/gsd-core/bin/lib/plan-scan.cjs +98 -3
- package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
- package/gsd-core/bin/lib/planning-scope.cjs +31 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +894 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +112 -6
- package/gsd-core/bin/lib/probe-core.cjs +5 -2
- package/gsd-core/bin/lib/profile-output.cjs +1 -1
- package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
- package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
- package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +766 -0
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +22 -13
- package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
- package/gsd-core/bin/lib/roadmap-command-router.cjs +59 -11
- package/gsd-core/bin/lib/roadmap-parser.cjs +1006 -184
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
- package/gsd-core/bin/lib/roadmap.cjs +442 -96
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +702 -52
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +459 -55
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
- package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +402 -58
- package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
- package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
- package/gsd-core/bin/lib/runtime-slash.cjs +96 -8
- package/gsd-core/bin/lib/security.cjs +104 -5
- package/gsd-core/bin/lib/shell-command-projection.cjs +342 -7
- package/gsd-core/bin/lib/smart-entry.cjs +133 -23
- package/gsd-core/bin/lib/spec-section.cjs +12 -7
- package/gsd-core/bin/lib/state-command-router.cjs +52 -19
- package/gsd-core/bin/lib/state-contract.cjs +359 -0
- package/gsd-core/bin/lib/state-document.cjs +338 -8
- package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
- package/gsd-core/bin/lib/state-transition.cjs +846 -176
- package/gsd-core/bin/lib/state.cjs +2589 -369
- package/gsd-core/bin/lib/surface.cjs +33 -11
- package/gsd-core/bin/lib/task-command-router.cjs +111 -1
- package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
- package/gsd-core/bin/lib/teams-status.cjs +4 -1
- package/gsd-core/bin/lib/text-lines.cjs +80 -0
- package/gsd-core/bin/lib/token-scanner.cjs +76 -0
- package/gsd-core/bin/lib/uat-predicate.cjs +67 -23
- package/gsd-core/bin/lib/uat.cjs +1761 -167
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
- package/gsd-core/bin/lib/ui-safety-gate.cjs +51 -12
- package/gsd-core/bin/lib/unusable-input.cjs +37 -0
- package/gsd-core/bin/lib/update-context.cjs +8 -2
- package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
- package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
- package/gsd-core/bin/lib/validate.cjs +20 -6
- package/gsd-core/bin/lib/vendor/README.md +75 -0
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
- package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
- package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +272 -9
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify.cjs +453 -918
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
- package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
- package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
- package/gsd-core/bin/lib/workstream.cjs +2 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
- package/gsd-core/bin/lib/worktree-safety.cjs +341 -18
- package/gsd-core/bin/shared/config-defaults.manifest.json +8 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +12 -1
- package/gsd-core/bin/shared/exit-codes.json +8 -0
- package/gsd-core/bin/shared/exit-codes.sh +20 -0
- package/gsd-core/bin/shared/model-catalog.json +8 -1
- package/gsd-core/references/agent-contracts.md +44 -26
- package/gsd-core/references/api-coverage.md +24 -2
- package/gsd-core/references/autonomous-smart-discuss.md +3 -3
- package/gsd-core/references/checkpoints.md +39 -21
- package/gsd-core/references/context-budget.md +1 -1
- package/gsd-core/references/decimal-phase-calculation.md +5 -5
- package/gsd-core/references/dispatch-isolation-gate.md +138 -0
- package/gsd-core/references/doc-conflict-engine.md +1 -1
- package/gsd-core/references/edge-probe.md +8 -0
- package/gsd-core/references/execute-mvp-tdd.md +4 -6
- package/gsd-core/references/execute-phase-between-wave-reset.md +15 -14
- package/gsd-core/references/execute-phase-context-guard.md +1 -1
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/execute-phase-wave-guard.md +17 -11
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-integration.md +5 -5
- package/gsd-core/references/git-planning-commit.md +5 -4
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +61 -2
- package/gsd-core/references/model-profiles.md +12 -4
- package/gsd-core/references/mvp-concepts.md +9 -9
- package/gsd-core/references/nyquist-compliance.md +74 -0
- package/gsd-core/references/offer-next.md +3 -5
- package/gsd-core/references/phase-argument-parsing.md +3 -3
- package/gsd-core/references/planner-failing-direction.md +53 -0
- package/gsd-core/references/planner-guidance.md +3 -9
- package/gsd-core/references/planner-human-verify-mode.md +15 -1
- package/gsd-core/references/planner-preconditions.md +1 -1
- package/gsd-core/references/planner-reviews.md +1 -1
- package/gsd-core/references/planner-revision.md +1 -1
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +44 -13
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/revision-loop.md +1 -1
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/specless-probe-fallback.md +1 -1
- package/gsd-core/references/tdd.md +1 -3
- package/gsd-core/references/ui-brand.md +65 -21
- package/gsd-core/references/ui-consideration-probe.md +1 -1
- package/gsd-core/references/universal-anti-patterns.md +5 -5
- package/gsd-core/references/verifier-phase-gates.md +192 -0
- package/gsd-core/references/verify-command-path-resolvability.md +42 -0
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -17
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/SECURITY.md +3 -3
- package/gsd-core/templates/UI-SPEC.md +25 -3
- package/gsd-core/templates/VALIDATION.md +3 -3
- package/gsd-core/templates/discussion-log.md +1 -1
- package/gsd-core/templates/phase-prompt.md +5 -4
- package/gsd-core/templates/state.md +11 -4
- package/gsd-core/templates/verification-report.md +9 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +3 -3
- package/gsd-core/workflows/add-tests.md +3 -8
- package/gsd-core/workflows/add-todo.md +1 -1
- package/gsd-core/workflows/ai-integration-phase.md +13 -20
- package/gsd-core/workflows/audit-fix.md +12 -3
- package/gsd-core/workflows/audit-milestone.md +9 -9
- package/gsd-core/workflows/audit-uat.md +17 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +11 -27
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +64 -5
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +14 -4
- package/gsd-core/workflows/code-review-fix.md +38 -11
- package/gsd-core/workflows/code-review.md +159 -52
- package/gsd-core/workflows/complete-milestone.md +151 -23
- package/gsd-core/workflows/debug.md +12 -8
- package/gsd-core/workflows/diagnose-issues.md +47 -15
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -8
- package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +3 -6
- package/gsd-core/workflows/docs-update.md +5 -4
- package/gsd-core/workflows/edit-phase.md +27 -2
- package/gsd-core/workflows/eval-review.md +7 -14
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +142 -15
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +24 -4
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase.md +72 -100
- package/gsd-core/workflows/execute-plan.md +52 -15
- package/gsd-core/workflows/explore.md +131 -4
- package/gsd-core/workflows/extract-learnings.md +1 -1
- package/gsd-core/workflows/fast.md +10 -2
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +5 -5
- package/gsd-core/workflows/health.md +76 -10
- package/gsd-core/workflows/import.md +18 -15
- package/gsd-core/workflows/inbox.md +4 -5
- package/gsd-core/workflows/ingest-docs.md +49 -16
- package/gsd-core/workflows/insert-phase.md +5 -5
- package/gsd-core/workflows/list-seeds.md +5 -3
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +12 -23
- package/gsd-core/workflows/map-codebase.md +1 -1
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +8 -5
- package/gsd-core/workflows/new-milestone.md +22 -29
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +26 -40
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +14 -2
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
- package/gsd-core/workflows/plan-phase.md +162 -59
- package/gsd-core/workflows/plan-review-convergence.md +96 -11
- package/gsd-core/workflows/plant-seed.md +2 -2
- package/gsd-core/workflows/pr-branch.md +187 -51
- package/gsd-core/workflows/profile-user.md +16 -14
- package/gsd-core/workflows/progress.md +61 -18
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +5 -7
- package/gsd-core/workflows/quick/steps/quick-verification.md +28 -9
- package/gsd-core/workflows/quick/steps/research-phase.md +4 -6
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick.md +55 -44
- package/gsd-core/workflows/remove-phase.md +4 -4
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +8 -12
- package/gsd-core/workflows/review.md +219 -20
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +3 -3
- package/gsd-core/workflows/session-report.md +2 -1
- package/gsd-core/workflows/settings-advanced.md +7 -9
- package/gsd-core/workflows/settings-integrations.md +64 -31
- package/gsd-core/workflows/settings.md +69 -7
- package/gsd-core/workflows/ship.md +116 -50
- package/gsd-core/workflows/sketch-wrap-up.md +11 -17
- package/gsd-core/workflows/sketch.md +12 -18
- package/gsd-core/workflows/smart-entry.md +3 -5
- package/gsd-core/workflows/spec-phase.md +53 -13
- package/gsd-core/workflows/spike-wrap-up.md +7 -11
- package/gsd-core/workflows/spike.md +20 -31
- package/gsd-core/workflows/stats.md +2 -2
- package/gsd-core/workflows/sync-skills.md +64 -9
- package/gsd-core/workflows/thread.md +11 -7
- package/gsd-core/workflows/transition.md +49 -14
- package/gsd-core/workflows/ui-phase.md +15 -21
- package/gsd-core/workflows/ui-review.md +8 -12
- package/gsd-core/workflows/ultraplan-phase.md +5 -13
- package/gsd-core/workflows/undo.md +8 -16
- package/gsd-core/workflows/update.md +7 -11
- package/gsd-core/workflows/validate-phase.md +3 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +66 -25
- package/hooks/dist/gsd-agent-isolation-guard.js +158 -30
- package/hooks/dist/gsd-check-update-worker.js +56 -13
- package/hooks/dist/gsd-check-update.js +19 -1
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +19 -10
- package/hooks/dist/gsd-cursor-post-tool.js +3 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +2 -3
- package/hooks/dist/gsd-cursor-session-start.js +2 -1
- package/hooks/dist/gsd-cursor-stop.js +2 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +83 -3
- package/hooks/dist/gsd-cursor-subagent-stop.js +6 -3
- package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
- package/hooks/dist/gsd-graphify-update.sh +22 -18
- package/hooks/dist/gsd-node-runner.sh +76 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +37 -27
- package/hooks/dist/gsd-read-guard.js +16 -7
- package/hooks/dist/gsd-read-injection-scanner.js +55 -32
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +231 -24
- package/hooks/dist/gsd-update-banner.js +22 -1
- package/hooks/dist/gsd-validate-commit.sh +80 -6
- package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
- package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
- package/hooks/dist/gsd-workflow-guard.js +162 -46
- package/hooks/dist/gsd-worktree-path-guard.js +36 -21
- package/hooks/dist/gsd-write-guard.js +35 -25
- package/hooks/dist/lib/cli-exit.js +560 -0
- package/hooks/dist/lib/exit-code-registry.js +98 -0
- package/hooks/dist/lib/git-cmd.js +92 -59
- package/hooks/dist/lib/git-probe.js +84 -0
- package/hooks/dist/lib/hook-exit.js +81 -0
- package/hooks/dist/lib/injection-patterns.js +45 -0
- package/hooks/dist/lib/isolation-deny-reason.js +39 -0
- package/hooks/dist/lib/isolation-sentinel.js +9 -0
- package/hooks/dist/managed-hooks-registry.cjs +3 -0
- package/hooks/gsd-agent-isolation-guard.js +158 -30
- package/hooks/gsd-check-update-worker.js +56 -13
- package/hooks/gsd-check-update.js +19 -1
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +19 -10
- package/hooks/gsd-cursor-post-tool.js +3 -1
- package/hooks/gsd-cursor-pre-tool.js +2 -3
- package/hooks/gsd-cursor-session-start.js +2 -1
- package/hooks/gsd-cursor-stop.js +2 -1
- package/hooks/gsd-cursor-subagent-start.js +83 -3
- package/hooks/gsd-cursor-subagent-stop.js +6 -3
- package/hooks/gsd-ensure-canonical-path.js +2 -1
- package/hooks/gsd-graphify-update.sh +22 -18
- package/hooks/gsd-node-runner.sh +76 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +37 -27
- package/hooks/gsd-read-guard.js +16 -7
- package/hooks/gsd-read-injection-scanner.js +55 -32
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +231 -24
- package/hooks/gsd-update-banner.js +22 -1
- package/hooks/gsd-validate-commit.sh +80 -6
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +162 -46
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-cmd.js +92 -59
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/lib/injection-patterns.js +45 -0
- package/hooks/lib/isolation-deny-reason.js +39 -0
- package/hooks/lib/isolation-sentinel.js +9 -0
- package/hooks/managed-hooks-registry.cjs +3 -0
- package/package.json +28 -11
- package/pi/gsd.cjs +19 -5
- package/scripts/base64-scan.sh +74 -12
- package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
- package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
- package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
- package/scripts/build-hooks.js +5 -0
- package/scripts/changeset/lint.cjs +60 -5
- package/scripts/check-alias-drift.cjs +7 -43
- package/scripts/check-contract-drift.cjs +297 -0
- package/scripts/check-glossary-refs.cjs +77 -15
- package/scripts/check-mutation-score-ratchet.cjs +156 -0
- package/scripts/ci-check-job-near-cap.cjs +49 -0
- package/scripts/ci-pr-mergeability.cjs +262 -0
- package/scripts/ci-test-scope.cjs +64 -14
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/command-contract-helpers.cjs +903 -1
- package/scripts/docs-guard-registry.cjs +396 -0
- package/scripts/gen-adr-index.cjs +728 -38
- package/scripts/gen-capability-registry.cjs +11 -21
- package/scripts/gen-context-index.cjs +2 -11
- package/scripts/gen-exit-code-docs.cjs +318 -0
- package/scripts/gen-exit-code-registry.cjs +891 -0
- package/scripts/gen-features.cjs +836 -0
- package/scripts/gen-health-docs.cjs +390 -0
- package/scripts/gen-hooks-cli-exit.cjs +239 -0
- package/scripts/gen-install-tree-fixtures.cjs +2 -2
- package/scripts/gen-inventory-manifest.cjs +50 -4
- package/scripts/gen-loop-host-contract.cjs +138 -25
- package/scripts/gen-registry.cjs +3 -14
- package/scripts/gen-scripts-cli-exit.cjs +185 -0
- package/scripts/gen-state-md-docs.cjs +727 -0
- package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
- package/scripts/lib/alias-drift-families.cjs +46 -0
- package/scripts/lib/ci-job-timing.cjs +72 -0
- package/scripts/lib/cli-exit.cjs +546 -44
- package/scripts/lib/drift-scan.cjs +308 -0
- package/scripts/lib/exit-code-registry.cjs +98 -0
- package/scripts/lib/ndjson-reporter.cjs +119 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
- package/scripts/lint-canary-version-leak.cjs +73 -0
- package/scripts/lint-command-contract.cjs +96 -13
- package/scripts/lint-completion-predicate-drift.cjs +933 -0
- package/scripts/lint-completion-ratio-drift.cjs +214 -0
- package/scripts/lint-default-flip-documentation.cjs +193 -0
- package/scripts/lint-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +38 -0
- package/scripts/lint-eslint-glob-coverage.cjs +340 -0
- package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
- package/scripts/lint-health-diagnostic-rule-table.cjs +461 -0
- package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
- package/scripts/lint-milestone-window-drift.cjs +468 -0
- package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
- package/scripts/lint-phase-enumeration-drift.cjs +492 -0
- package/scripts/lint-plan-count-drift.cjs +318 -0
- package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
- package/scripts/lint-planning-prompt-drift.cjs +471 -0
- package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
- package/scripts/lint-regression-test-names.cjs +15 -13
- package/scripts/lint-removed-but-needed.cjs +488 -0
- package/scripts/lint-seam-enforcement.cjs +182 -0
- package/scripts/lint-slug-derivation-drift.cjs +921 -0
- package/scripts/lint-source-test-name-collision.cjs +241 -0
- package/scripts/lint-state-field-drift.cjs +805 -0
- package/scripts/lint-state-write-path-drift.cjs +950 -0
- package/scripts/lint-test-file-count.allowlist.json +137 -8
- package/scripts/lint-test-file-count.cjs +25 -3
- package/scripts/lint-unreachable-guard-drift.cjs +830 -0
- package/scripts/lint-vendored-deps.cjs +297 -0
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/pr-changed-files.cjs +63 -0
- package/scripts/pr-template-policy.cjs +14 -4
- package/scripts/prompt-injection-scan.sh +100 -14
- package/scripts/require-issue-link-policy.cjs +192 -0
- package/scripts/secret-scan.sh +75 -13
- package/scripts/select-docs-guards.cjs +56 -0
- package/scripts/sync-runtime-launcher.cjs +24 -7
- package/skills/gsd-autonomous/SKILL.md +0 -1
- package/skills/gsd-code-review/SKILL.md +1 -1
- package/skills/gsd-discuss-phase/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -2
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-map-codebase/SKILL.md +1 -1
- package/skills/gsd-mempalace-capture/SKILL.md +1 -1
- package/skills/gsd-mempalace-recall/SKILL.md +1 -1
- package/skills/gsd-new-milestone/SKILL.md +1 -1
- package/skills/gsd-next/SKILL.md +0 -1
- package/skills/gsd-plan-phase/SKILL.md +0 -1
- package/skills/gsd-progress/SKILL.md +0 -1
- package/skills/gsd-quick/SKILL.md +9 -5
- package/skills/gsd-review-backlog/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +0 -1
- package/skills/gsd-verify-work/SKILL.md +1 -1
- package/vscode/package.json +1 -1
- package/bin/lib/ui-safety-gate.cjs +0 -107
- package/gsd-core/workflows/discovery-phase.md +0 -298
- package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
- package/gsd-core/workflows/verify-phase.md +0 -574
- package/scripts/affected-tests-lib.cjs +0 -554
- package/scripts/lint-allow-test-rule-refs.cjs +0 -162
- package/scripts/lint-emitted-drift-ack.cjs +0 -344
- package/scripts/run-affected-tests.cjs +0 -7
- package/scripts/run-tests.cjs +0 -1051
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* installed-surface-resolver.cts — Installed Surface Resolver Module (#2872,
|
|
4
|
+
* ADR-2866, Phase 3 — governed by
|
|
5
|
+
* `.gsd/phase/feat-2872-manifest-scope-runtime/40-design.md`).
|
|
6
|
+
*
|
|
7
|
+
* `resolveInstalledSurfaces(runtime?, opts?)` answers a question no existing
|
|
8
|
+
* module answers: "which surfaces actually exist on THIS machine, across
|
|
9
|
+
* BOTH install scopes, right now?" `capability-state.cts` answers "which
|
|
10
|
+
* capabilities are enabled in THIS config dir" — a different question at a
|
|
11
|
+
* different altitude. This module is a new leaf: it reads two scopes at
|
|
12
|
+
* once, composing three already-shipped, already-tested pieces
|
|
13
|
+
* (`resolveScope`, Phase 1; `resolveTriggerSurface`, Phase 2; and
|
|
14
|
+
* `readInstallManifest`, widened in this same phase) rather than
|
|
15
|
+
* re-implementing any of their rules.
|
|
16
|
+
*
|
|
17
|
+
* ── Probe-not-declared keying ───────────────────────────────────────────────
|
|
18
|
+
* A scope RECORD is keyed by the PROBED scope (`resolveScope`'s own `id`),
|
|
19
|
+
* never by what the manifest itself claims. `manifestVersion`/`runtime`/
|
|
20
|
+
* `scope` recorded inside a v2 manifest are corroboration, not identity: a
|
|
21
|
+
* manifest copied between config dirs, or a `--config-dir` install, must
|
|
22
|
+
* still be reported at the scope this machine actually resolves to.
|
|
23
|
+
*
|
|
24
|
+
* ── Report, don't correct ───────────────────────────────────────────────────
|
|
25
|
+
* When a declared `runtime`/`scope` disagrees with the probe, this module
|
|
26
|
+
* reports the mismatch (`declaredScopeMatchesProbe: false` /
|
|
27
|
+
* `declaredRuntimeMatchesProbe: false`) and otherwise proceeds exactly as it
|
|
28
|
+
* would for an undeclared (v1) manifest. It never silently substitutes the
|
|
29
|
+
* declared value for the probed one, and never throws on a mismatch — see
|
|
30
|
+
* B-row "Postel's Law" discussion in the design doc.
|
|
31
|
+
*
|
|
32
|
+
* ── One trigger call over installed scopes only ─────────────────────────────
|
|
33
|
+
* `resolveTriggerSurface` is called AT MOST once per runtime, with the
|
|
34
|
+
* scopes that are actually installed on this machine (per manifest
|
|
35
|
+
* presence — never per the new corroboration fields, which is what keeps a
|
|
36
|
+
* v1-only install fully functional with no reinstall required). A
|
|
37
|
+
* hypothetical "what if both scopes were installed" answer is deliberately
|
|
38
|
+
* not offered; #2218 is a fact about THIS machine, not a simulation.
|
|
39
|
+
*
|
|
40
|
+
* ── Installed-ness is manifest PRESENCE, never the new fields ───────────────
|
|
41
|
+
* `installed` is `manifestVersion !== null`. A v1 manifest (no
|
|
42
|
+
* `manifestVersion` key at all, `manifestVersion: 1` after normalization) is
|
|
43
|
+
* a correct manifest written by an older GSD — never a broken one, never
|
|
44
|
+
* grounds for `installed: false`, a warning, or a reinstall prompt.
|
|
45
|
+
*
|
|
46
|
+
* ── Stems come from the manifest, not the source tree ───────────────────────
|
|
47
|
+
* `resolveTriggerSurface` needs `stems`. They are derived from the
|
|
48
|
+
* INSTALLED manifest's own `files` keys (see the private stem-derivation
|
|
49
|
+
* helpers below), never from a roster read of the source tree — a global
|
|
50
|
+
* `claude` install ships no `commands/gsd` source, so a roster read would
|
|
51
|
+
* return `[]` in exactly the configuration #2218 is about. The derivation
|
|
52
|
+
* consumes `isNamespacedByDir` and `composeCommandFilename`
|
|
53
|
+
* (`runtime-artifact-layout.cjs`, #2871 Phase 2's exported helpers) rather
|
|
54
|
+
* than re-deriving either rule as a fourth independent copy.
|
|
55
|
+
*/
|
|
56
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
57
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
58
|
+
};
|
|
59
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
60
|
+
exports.resolveInstalledSurfaces = resolveInstalledSurfaces;
|
|
61
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
62
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
63
|
+
const install_scope_cjs_1 = require("./install-scope.cjs");
|
|
64
|
+
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
66
|
+
const runtimeArtifactLayoutMod = require("./runtime-artifact-layout.cjs");
|
|
67
|
+
const { resolveRuntimeArtifactLayout, resolveRuntimeArtifactLayoutFromRegistry, resolveTriggerSurface, isNamespacedByDir, composeCommandFilename, } = runtimeArtifactLayoutMod;
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
69
|
+
const installerMigrationsMod = require("./installer-migrations.cjs");
|
|
70
|
+
const { readInstallManifest, MANIFEST_NAME } = installerMigrationsMod;
|
|
71
|
+
// In .cts (CommonJS output) files, `require` is available as a global.
|
|
72
|
+
const _require = require;
|
|
73
|
+
// ── Internals ───────────────────────────────────────────────────────────
|
|
74
|
+
/**
|
|
75
|
+
* A trigger stem is a kebab-case token and nothing else. Verified against the
|
|
76
|
+
* real roster: all 71 `commands/gsd/*.md` stems match this, so it has no false
|
|
77
|
+
* negatives today.
|
|
78
|
+
*
|
|
79
|
+
* This is a SECURITY boundary, not cosmetics. A stem is derived from a manifest
|
|
80
|
+
* key — attacker-influenceable text, since a project-local
|
|
81
|
+
* `gsd-file-manifest.json` lives inside a repository a user may merely have
|
|
82
|
+
* cloned — and Phase 4 (#2873) renders it back to the user as `/gsd-<stem>`.
|
|
83
|
+
* Without this, `skills/gsd-../../../x` yields the stem `..`, and control
|
|
84
|
+
* characters, newlines, ANSI escapes and RTL-override codepoints all survive
|
|
85
|
+
* into that rendered trigger. The `commands` branch happened to be protected by
|
|
86
|
+
* its `composeCommandFilename` round-trip; the `skills` branch had no
|
|
87
|
+
* equivalent, so the rule is stated once here and applied to both.
|
|
88
|
+
*/
|
|
89
|
+
const SAFE_STEM = /^[a-z0-9][a-z0-9-]*$/;
|
|
90
|
+
function getDefaultRuntimeRegistry() {
|
|
91
|
+
return _require('./capability-registry.cjs');
|
|
92
|
+
}
|
|
93
|
+
/** C7: every registered runtime id, sorted for determinism. Reads
|
|
94
|
+
* `opts.registry` when provided (so a sweep is assertable without the real
|
|
95
|
+
* capability registry), else the real one. */
|
|
96
|
+
function listRegisteredRuntimeIds(opts) {
|
|
97
|
+
const registry = opts.registry ?? getDefaultRuntimeRegistry();
|
|
98
|
+
return Object.keys(registry.runtimes).sort();
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* D — the stem inverse for one `commands`/`skills` layout kind entry.
|
|
102
|
+
* Consumes `isNamespacedByDir` (to decide the commands filename shape) and
|
|
103
|
+
* `composeCommandFilename` (to VERIFY a candidate stem round-trips to the
|
|
104
|
+
* exact filename seen) rather than re-deriving either rule locally (design
|
|
105
|
+
* "Rejected" #6). Manifest keys are normalized with an unconditional
|
|
106
|
+
* `.replace(/\\/g, '/')` (D7) — never gated on `path.sep` — matching this
|
|
107
|
+
* repo's recorded path-separator-normalization defect class.
|
|
108
|
+
*/
|
|
109
|
+
function deriveStemsForKindEntry(kind, destSubpath, prefix, fileKeys) {
|
|
110
|
+
const destSubpathNorm = (0, shell_command_projection_cjs_1.posixNormalize)(destSubpath);
|
|
111
|
+
const boundary = `${destSubpathNorm}/`;
|
|
112
|
+
const namespacedByDir = isNamespacedByDir(kind, destSubpath, prefix);
|
|
113
|
+
const stems = new Set();
|
|
114
|
+
for (const rawKey of fileKeys) {
|
|
115
|
+
const key = rawKey.replace(/\\/g, '/');
|
|
116
|
+
if (!key.startsWith(boundary))
|
|
117
|
+
continue; // D4: key not under this declared subpath
|
|
118
|
+
const rest = key.slice(boundary.length);
|
|
119
|
+
if (rest === '')
|
|
120
|
+
continue;
|
|
121
|
+
const segments = rest.split('/');
|
|
122
|
+
if (kind === 'skills') {
|
|
123
|
+
const dirSegment = segments[0];
|
|
124
|
+
if (!dirSegment.startsWith(prefix))
|
|
125
|
+
continue; // D5: subpath matches, prefix does not
|
|
126
|
+
const stem = dirSegment.slice(prefix.length);
|
|
127
|
+
if (stem === '')
|
|
128
|
+
continue; // D7/B7: empty stem never emitted
|
|
129
|
+
if (!SAFE_STEM.test(stem))
|
|
130
|
+
continue; // security: reject anything but a kebab-case token
|
|
131
|
+
stems.add(stem); // D6: several files under one dir -> one stem
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
// commands: exactly one path segment — the composed filename itself.
|
|
135
|
+
if (segments.length !== 1)
|
|
136
|
+
continue;
|
|
137
|
+
const filename = segments[0];
|
|
138
|
+
if (!filename.endsWith('.md'))
|
|
139
|
+
continue;
|
|
140
|
+
const base = filename.slice(0, -3);
|
|
141
|
+
const candidateStem = namespacedByDir
|
|
142
|
+
? base
|
|
143
|
+
: (base.startsWith(prefix) ? base.slice(prefix.length) : '');
|
|
144
|
+
if (candidateStem === '')
|
|
145
|
+
continue;
|
|
146
|
+
if (composeCommandFilename(namespacedByDir, prefix, candidateStem) !== filename)
|
|
147
|
+
continue;
|
|
148
|
+
if (!SAFE_STEM.test(candidateStem))
|
|
149
|
+
continue; // security: reject anything but a kebab-case token
|
|
150
|
+
stems.add(candidateStem);
|
|
151
|
+
}
|
|
152
|
+
return [...stems];
|
|
153
|
+
}
|
|
154
|
+
/** D — full stem set for one scope: every `commands`/`skills` layout kind
|
|
155
|
+
* entry, unioned. Empty `files` (C13) short-circuits to `[]` without
|
|
156
|
+
* resolving a layout at all. */
|
|
157
|
+
function deriveStemsFromManifest(runtime, scopeId, configHome, files, opts) {
|
|
158
|
+
const fileKeys = Object.keys(files);
|
|
159
|
+
if (fileKeys.length === 0)
|
|
160
|
+
return [];
|
|
161
|
+
const layout = opts.registry !== undefined
|
|
162
|
+
? resolveRuntimeArtifactLayoutFromRegistry(opts.registry, runtime, configHome, scopeId)
|
|
163
|
+
: resolveRuntimeArtifactLayout(runtime, configHome, scopeId);
|
|
164
|
+
const stems = new Set();
|
|
165
|
+
for (const kindEntry of layout.kinds) {
|
|
166
|
+
if (kindEntry.kind !== 'commands' && kindEntry.kind !== 'skills')
|
|
167
|
+
continue; // excludes agents/kimi-agents (D4)
|
|
168
|
+
for (const stem of deriveStemsForKindEntry(kindEntry.kind, kindEntry.destSubpath, kindEntry.prefix, fileKeys)) {
|
|
169
|
+
stems.add(stem);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return [...stems].sort();
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* True when `p` is a symlink. An `lstatSync` throw (ENOENT — nothing at this
|
|
176
|
+
* path) is NOT evidence of a symlink; it is treated as "not a symlink" here
|
|
177
|
+
* and left for `readManifest` to classify (it already owns the absent-file
|
|
178
|
+
* case, per C14 above).
|
|
179
|
+
*/
|
|
180
|
+
function isSymlinkPath(p, lstatSync) {
|
|
181
|
+
try {
|
|
182
|
+
return lstatSync(p).isSymbolicLink();
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* The shared "not installed" degraded shape (C14's EACCES path, and this
|
|
190
|
+
* phase's new symlink-guard path). A FACTORY, not a module-level constant
|
|
191
|
+
* object: a `const` object spread at each return site would still share the
|
|
192
|
+
* same `stems` ARRAY reference across every call (`...` shallow-copies the
|
|
193
|
+
* object but not the array a property points at), which would violate this
|
|
194
|
+
* module's own "builds fresh arrays/objects on every call" contract (C15) —
|
|
195
|
+
* a caller mutating one degraded record's `stems` must never be visible on
|
|
196
|
+
* another's.
|
|
197
|
+
*/
|
|
198
|
+
function notInstalledScopeRecordFields() {
|
|
199
|
+
return {
|
|
200
|
+
installed: false,
|
|
201
|
+
manifestVersion: null,
|
|
202
|
+
declaredRuntime: null,
|
|
203
|
+
declaredScope: null,
|
|
204
|
+
declaredScopeMatchesProbe: null,
|
|
205
|
+
declaredRuntimeMatchesProbe: null,
|
|
206
|
+
stems: [],
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Build one scope's record. `resolvedConfigHome` has already been probed
|
|
211
|
+
* successfully by the time this is called (a `resolveScope` `TypeError` is
|
|
212
|
+
* handled by the caller, per C8 — it is never this function's concern).
|
|
213
|
+
*
|
|
214
|
+
* Two distinct failure modes are handled with two distinct, narrowly-scoped
|
|
215
|
+
* try/catches — NOT one catch-all around the whole function:
|
|
216
|
+
*
|
|
217
|
+
* - The manifest READ (`readManifest`) is the only thing that can justify
|
|
218
|
+
* `installed: false` (C14, e.g. EACCES). A failure here means "we could
|
|
219
|
+
* not even tell whether this scope is installed".
|
|
220
|
+
* - The stem DERIVATION (`deriveStemsFromManifest`, which resolves a
|
|
221
|
+
* runtime artifact layout) is a separate concern. A `TypeError` thrown
|
|
222
|
+
* while deriving stems is a layout-lookup failure, not evidence the
|
|
223
|
+
* manifest is absent — the manifest was already read successfully, so
|
|
224
|
+
* `installed` and every declared/*MatchesProbe field stay exactly as the
|
|
225
|
+
* manifest reported. Conflating the two would report a genuinely
|
|
226
|
+
* installed runtime as `installed: false`, hiding a real install from
|
|
227
|
+
* #2218 shadow detection precisely when this module exists to surface it.
|
|
228
|
+
*/
|
|
229
|
+
function buildScopeRecord(runtime, scopeId, resolvedConfigHome, opts) {
|
|
230
|
+
// Hardening requirement 2 (#2873 design doc, "Hardening requirements
|
|
231
|
+
// claimed from #2873's comment"): `readInstallManifest` -> `readJsonIfPresent`
|
|
232
|
+
// uses `existsSync` + `readFileSync` and therefore FOLLOWS symlinks, and
|
|
233
|
+
// this module resolves `local` against `process.cwd()` — a directory that,
|
|
234
|
+
// as of this phase, becomes reachable from an arbitrary cloned repository
|
|
235
|
+
// (this is the same phase that makes the local scope's manifest a first
|
|
236
|
+
// read target, not merely a write target). The in-tree precedent is
|
|
237
|
+
// `getAgentsDir` (`agent-install-check.cts`), which probes with
|
|
238
|
+
// `fs.lstatSync(...).isDirectory()`/`.isFile()` and deliberately does not
|
|
239
|
+
// follow. #2872 left this resolver's read un-guarded only because the path
|
|
240
|
+
// had zero callers at the time; refusing to follow a symlinked config dir
|
|
241
|
+
// or manifest here closes that asymmetry rather than carrying it forward.
|
|
242
|
+
// Degrading to `installed: false` reuses the SAME shape the EACCES catch
|
|
243
|
+
// below already returns — no new failure shape is introduced.
|
|
244
|
+
const lstatSync = opts.lstatSync ?? node_fs_1.default.lstatSync;
|
|
245
|
+
const manifestPath = node_path_1.default.join(resolvedConfigHome, MANIFEST_NAME);
|
|
246
|
+
if (isSymlinkPath(resolvedConfigHome, lstatSync) || isSymlinkPath(manifestPath, lstatSync)) {
|
|
247
|
+
return { scope: scopeId, configHome: resolvedConfigHome, ...notInstalledScopeRecordFields() };
|
|
248
|
+
}
|
|
249
|
+
let manifest;
|
|
250
|
+
try {
|
|
251
|
+
const readManifest = opts.readManifest ?? readInstallManifest;
|
|
252
|
+
manifest = readManifest(resolvedConfigHome);
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
// C14: manifest read/probe failure degrades to not-installed, never throws.
|
|
256
|
+
// Deliberately a BARE catch, unlike the stem-derivation catch below: any
|
|
257
|
+
// read failure at all (EACCES, ENOENT-after-race, a corrupt filesystem)
|
|
258
|
+
// legitimately means "cannot tell whether installed" (design row C14), so
|
|
259
|
+
// there is no error TYPE here that should instead propagate.
|
|
260
|
+
return { scope: scopeId, configHome: resolvedConfigHome, ...notInstalledScopeRecordFields() };
|
|
261
|
+
}
|
|
262
|
+
const installed = manifest.manifestVersion !== null; // C9: presence, never the new fields
|
|
263
|
+
const declaredRuntime = manifest.runtime;
|
|
264
|
+
const declaredScope = manifest.scope;
|
|
265
|
+
const declaredRuntimeMatchesProbe = declaredRuntime === null ? null : declaredRuntime === runtime;
|
|
266
|
+
const declaredScopeMatchesProbe = declaredScope === null ? null : declaredScope === scopeId;
|
|
267
|
+
let stems = [];
|
|
268
|
+
if (installed) {
|
|
269
|
+
try {
|
|
270
|
+
stems = deriveStemsFromManifest(runtime, scopeId, resolvedConfigHome, manifest.files, opts);
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
if (!(error instanceof TypeError))
|
|
274
|
+
throw error;
|
|
275
|
+
// A layout-lookup failure means "we could not enumerate this scope's
|
|
276
|
+
// triggers", NOT "this scope is not installed" — the manifest read
|
|
277
|
+
// already succeeded above, so `installed` and the declared fields
|
|
278
|
+
// stay as reported. Conflating the two would hide a real install.
|
|
279
|
+
// Anything that is not the expected `TypeError` (a genuine bug in
|
|
280
|
+
// `deriveStemsFromManifest`/`resolveRuntimeArtifactLayout`) is
|
|
281
|
+
// rethrown rather than silently degrading to `stems: []` — this
|
|
282
|
+
// matches `resolveInstalledSurfaces`'s own `TypeError` narrowing
|
|
283
|
+
// below, so the two catches cannot drift apart.
|
|
284
|
+
stems = [];
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
scope: scopeId,
|
|
289
|
+
configHome: resolvedConfigHome,
|
|
290
|
+
installed,
|
|
291
|
+
manifestVersion: manifest.manifestVersion,
|
|
292
|
+
declaredRuntime,
|
|
293
|
+
declaredScope,
|
|
294
|
+
declaredScopeMatchesProbe,
|
|
295
|
+
declaredRuntimeMatchesProbe,
|
|
296
|
+
stems,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Resolve one runtime's full installed surface. A `resolveScope` `TypeError`
|
|
301
|
+
* (unknown runtime, or `configHome.kind === 'none'`, e.g. vscode) propagates
|
|
302
|
+
* from here uncaught — `resolveInstalledSurfaces` decides whether that
|
|
303
|
+
* means "skip" (C7 sweep) or "propagate" (explicit single-runtime ask, C8).
|
|
304
|
+
*/
|
|
305
|
+
function resolveOneRuntime(runtime, opts) {
|
|
306
|
+
const scopes = install_scope_cjs_1.SCOPE_ORDER.map((scopeId) => {
|
|
307
|
+
const resolved = (0, install_scope_cjs_1.resolveScope)({
|
|
308
|
+
id: scopeId,
|
|
309
|
+
runtime,
|
|
310
|
+
env: opts.env,
|
|
311
|
+
home: opts.home,
|
|
312
|
+
existsSync: opts.existsSync,
|
|
313
|
+
cwd: opts.cwd,
|
|
314
|
+
});
|
|
315
|
+
return buildScopeRecord(runtime, scopeId, resolved.configHome, opts);
|
|
316
|
+
});
|
|
317
|
+
// C12: dedupe by resolved configHome BEFORE the trigger call, keeping the
|
|
318
|
+
// higher-ranked (global, first in SCOPE_ORDER) scope. Both scope RECORDS
|
|
319
|
+
// above are unaffected — only the scope list handed to resolveTriggerSurface
|
|
320
|
+
// is deduped.
|
|
321
|
+
const seenConfigHomes = new Set();
|
|
322
|
+
const triggerScopeIds = [];
|
|
323
|
+
for (const record of scopes) {
|
|
324
|
+
if (!record.installed)
|
|
325
|
+
continue;
|
|
326
|
+
if (seenConfigHomes.has(record.configHome))
|
|
327
|
+
continue;
|
|
328
|
+
seenConfigHomes.add(record.configHome);
|
|
329
|
+
triggerScopeIds.push(record.scope);
|
|
330
|
+
}
|
|
331
|
+
const stemUnion = new Set();
|
|
332
|
+
for (const scopeId of triggerScopeIds) {
|
|
333
|
+
const record = scopes.find((s) => s.scope === scopeId);
|
|
334
|
+
for (const stem of record?.stems ?? [])
|
|
335
|
+
stemUnion.add(stem);
|
|
336
|
+
}
|
|
337
|
+
const triggers = triggerScopeIds.length > 0
|
|
338
|
+
? resolveTriggerSurface(runtime, triggerScopeIds, {
|
|
339
|
+
stems: [...stemUnion].sort(),
|
|
340
|
+
registry: opts.registry,
|
|
341
|
+
})
|
|
342
|
+
: [];
|
|
343
|
+
return { runtime, scopes, triggers };
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Read-only. Probes both install scopes for a runtime (or every registered
|
|
347
|
+
* runtime, sorted by id, when `runtime` is omitted), reads each scope's
|
|
348
|
+
* manifest, and resolves the trigger surface across the scopes that are
|
|
349
|
+
* actually installed on this machine. See the module-level comment for the
|
|
350
|
+
* non-obvious choices (probe-not-declared keying, report-don't-correct, one
|
|
351
|
+
* trigger call over installed scopes only).
|
|
352
|
+
*
|
|
353
|
+
* Pure with respect to caller-visible state: builds fresh arrays/objects on
|
|
354
|
+
* every call (C15) and performs no writes. Filesystem reads happen only via
|
|
355
|
+
* `resolveScope`'s injected `existsSync`/`env`/`home`/`cwd` and via
|
|
356
|
+
* `readManifest` (default: `readInstallManifest`).
|
|
357
|
+
*
|
|
358
|
+
* @throws {TypeError} when `runtime` is given explicitly and it is unknown,
|
|
359
|
+
* or has no installable config directory (`configHome.kind === 'none'`,
|
|
360
|
+
* e.g. vscode) — same contract `resolveScope` throws. In the all-runtimes
|
|
361
|
+
* sweep (`runtime` omitted), a runtime that would throw this same
|
|
362
|
+
* `TypeError` is skipped instead, so one non-installable runtime cannot
|
|
363
|
+
* kill the sweep (C7/C8). Any other error type is never swallowed here.
|
|
364
|
+
*/
|
|
365
|
+
function resolveInstalledSurfaces(runtime, opts = {}) {
|
|
366
|
+
if (typeof runtime === 'string') {
|
|
367
|
+
return [resolveOneRuntime(runtime, opts)];
|
|
368
|
+
}
|
|
369
|
+
const results = [];
|
|
370
|
+
for (const runtimeId of listRegisteredRuntimeIds(opts)) {
|
|
371
|
+
try {
|
|
372
|
+
results.push(resolveOneRuntime(runtimeId, opts));
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
if (error instanceof TypeError)
|
|
376
|
+
continue; // C8: sweep skips, never dies
|
|
377
|
+
throw error;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return results;
|
|
381
|
+
}
|
|
@@ -48,6 +48,9 @@ exports.BUNDLED_GSD_HOOK_FILES = Object.freeze(new Set([
|
|
|
48
48
|
'hooks/gsd-windsurf-pre-command.js',
|
|
49
49
|
'hooks/gsd-ensure-canonical-path.js',
|
|
50
50
|
'hooks/gsd-graphify-update.sh',
|
|
51
|
+
// #3662: portable node resolver staged into hooks/ (not itself a lifecycle
|
|
52
|
+
// hook — managed JS hook commands route through it under --portable-hooks).
|
|
53
|
+
'hooks/gsd-node-runner.sh',
|
|
51
54
|
'hooks/gsd-phase-boundary.sh',
|
|
52
55
|
'hooks/gsd-prompt-guard.js',
|
|
53
56
|
'hooks/gsd-read-guard.js',
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Installer migration: retire Antigravity's configHome skills/agents surfaces (#3738).
|
|
4
|
+
*
|
|
5
|
+
* Antigravity's machine-local discovery scans ~/.gemini/config/{skills,agents}
|
|
6
|
+
* (antigravity.google/docs/skills, /docs/subagents); the configHome
|
|
7
|
+
* (~/.gemini/antigravity{,-ide,-cli}) is not scanned for global artifacts, so
|
|
8
|
+
* skills installed there are silently ignored. Since #3738 the global layout
|
|
9
|
+
* declares a `home: ".gemini/config"` override for both kinds, and new
|
|
10
|
+
* installs land in the scanned dir. This migration converges an existing
|
|
11
|
+
* installation: manifest-managed files under the configHome's skills/ and
|
|
12
|
+
* agents/ subtrees are removed (modified ones backed up first), unmanifested
|
|
13
|
+
* files are preserved, and now-empty container directories are retired.
|
|
14
|
+
*
|
|
15
|
+
* Scope is GLOBAL only: the local workspace layout (.agents/skills,
|
|
16
|
+
* .agents/agents) is the documented, still-live surface and is never touched.
|
|
17
|
+
*
|
|
18
|
+
* Is the action safe in non-interactive install?
|
|
19
|
+
* Yes. Every emitted action type (`remove-managed`, `backup-and-remove`,
|
|
20
|
+
* `remove-empty-dir`) is non-interactive and journaled; none requires a
|
|
21
|
+
* user choice, and unknown files never produce an action.
|
|
22
|
+
*
|
|
23
|
+
* See docs/installer-migrations.md#shipped-migrations and the antigravity row
|
|
24
|
+
* of docs/installer-migrations.md#runtime-configuration-contract-registry.
|
|
25
|
+
*/
|
|
26
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
27
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
28
|
+
};
|
|
29
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
30
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
31
|
+
/** The two retired configHome-relative artifact subpaths. */
|
|
32
|
+
const RETIRED_SUBPATHS = ['skills', 'agents'];
|
|
33
|
+
/** Only GSD-owned top-level entries are retirement candidates. */
|
|
34
|
+
const GSD_PREFIX = 'gsd-';
|
|
35
|
+
const FILE_REASON = 'Antigravity scans ~/.gemini/config/{skills,agents} for global discovery and does not scan the ' +
|
|
36
|
+
'configHome, so these artifacts are invisible to the runtime (#3738); the same artifacts reinstall ' +
|
|
37
|
+
"under the global layout's .gemini/config home override";
|
|
38
|
+
const FILE_OWNERSHIP_EVIDENCE = 'pre-#3738 antigravity installs record skills/gsd-*/** and agents/gsd-*.md keys in ' +
|
|
39
|
+
'gsd-file-manifest.json at the configHome root; the migration only walks gsd--prefixed entries ' +
|
|
40
|
+
'under the two retired subpaths';
|
|
41
|
+
const DIR_REASON = 'the retired container directory is removed only once every GSD-owned entry inside it is gone; a ' +
|
|
42
|
+
'directory that still holds an unmanifested user file — or any file-level action that failed to ' +
|
|
43
|
+
'apply — is left in place by the emptiness re-check';
|
|
44
|
+
const DIR_OWNERSHIP_EVIDENCE = 'skills/ and agents/ at the antigravity configHome root are GSD-installed container directories ' +
|
|
45
|
+
'from the pre-#3738 layout; removal is gated on emptiness by the shared remove-empty-dir action';
|
|
46
|
+
/**
|
|
47
|
+
* Recursively collect files and directories under `root/relDir`, never
|
|
48
|
+
* following a symlink (whether it names a file or a directory) and never
|
|
49
|
+
* emitting a path that resolves outside `baseResolved`. Mirrors the traversal
|
|
50
|
+
* guard in migration 009 (`walkPiHooksTree`).
|
|
51
|
+
*/
|
|
52
|
+
function walkTree(root, relDir, baseResolved, files, dirs) {
|
|
53
|
+
const dir = node_path_1.default.join(root, relDir);
|
|
54
|
+
const entries = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
55
|
+
for (const entry of entries) {
|
|
56
|
+
// Never follow a symlink into or through: it must not be traversed,
|
|
57
|
+
// hashed, or removed, regardless of what it points at.
|
|
58
|
+
if (entry.isSymbolicLink())
|
|
59
|
+
continue;
|
|
60
|
+
const relPath = node_path_1.default.posix.join(relDir, entry.name);
|
|
61
|
+
const resolved = node_path_1.default.resolve(root, relPath);
|
|
62
|
+
if (resolved !== baseResolved && !resolved.startsWith(baseResolved + node_path_1.default.sep))
|
|
63
|
+
continue;
|
|
64
|
+
if (entry.isDirectory()) {
|
|
65
|
+
dirs.push(relPath);
|
|
66
|
+
walkTree(root, relPath, baseResolved, files, dirs);
|
|
67
|
+
}
|
|
68
|
+
else if (entry.isFile()) {
|
|
69
|
+
files.push(relPath);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const migration = {
|
|
74
|
+
id: '2026-08-26-antigravity-retire-confighome-artifacts',
|
|
75
|
+
title: "Retire Antigravity's configHome skills/agents surfaces",
|
|
76
|
+
description: 'Remove manifest-managed skills/gsd-*/ and agents/gsd-*.md under the Antigravity configHome ' +
|
|
77
|
+
'(~/.gemini/antigravity{,-ide,-cli}) — a location Antigravity does not scan for global discovery — ' +
|
|
78
|
+
'and retire the now-empty container directories, now that the global layout installs both kinds ' +
|
|
79
|
+
'under the ~/.gemini/config home override (#3738). Global scope only; the local .agents workspace ' +
|
|
80
|
+
'surface is untouched.',
|
|
81
|
+
introducedIn: '1.11.0',
|
|
82
|
+
runtimes: ['antigravity'],
|
|
83
|
+
scopes: ['global'],
|
|
84
|
+
destructive: true,
|
|
85
|
+
plan: (ctx) => {
|
|
86
|
+
// Defense in depth ahead of the framework's own runtimes/scope filters:
|
|
87
|
+
// a claude/kimi/etc. skills/ or agents/ directory is a live install
|
|
88
|
+
// surface, never a retirement target, and so is antigravity's LOCAL
|
|
89
|
+
// .agents/skills layout.
|
|
90
|
+
if (ctx.runtime !== 'antigravity')
|
|
91
|
+
return [];
|
|
92
|
+
if (ctx.scope !== 'global')
|
|
93
|
+
return [];
|
|
94
|
+
const actions = [];
|
|
95
|
+
for (const subpath of RETIRED_SUBPATHS) {
|
|
96
|
+
const surfaceRoot = node_path_1.default.join(ctx.configDir, subpath);
|
|
97
|
+
let rootLstat;
|
|
98
|
+
try {
|
|
99
|
+
rootLstat = node_fs_1.default.lstatSync(surfaceRoot);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
continue; // absent -> nothing to retire for this surface, idempotent
|
|
103
|
+
}
|
|
104
|
+
// Never follow a symlinked surface root: walking through it could plan
|
|
105
|
+
// actions against paths outside the config directory entirely.
|
|
106
|
+
if (rootLstat.isSymbolicLink() || !rootLstat.isDirectory())
|
|
107
|
+
continue;
|
|
108
|
+
const baseResolved = node_path_1.default.resolve(ctx.configDir);
|
|
109
|
+
const files = [];
|
|
110
|
+
const dirs = [];
|
|
111
|
+
try {
|
|
112
|
+
const entries = node_fs_1.default.readdirSync(surfaceRoot, { withFileTypes: true });
|
|
113
|
+
for (const entry of entries) {
|
|
114
|
+
if (entry.isSymbolicLink() || !entry.name.startsWith(GSD_PREFIX))
|
|
115
|
+
continue;
|
|
116
|
+
const relPath = node_path_1.default.posix.join(subpath, entry.name);
|
|
117
|
+
if (entry.isDirectory()) {
|
|
118
|
+
dirs.push(relPath);
|
|
119
|
+
walkTree(ctx.configDir, relPath, baseResolved, files, dirs);
|
|
120
|
+
}
|
|
121
|
+
else if (entry.isFile()) {
|
|
122
|
+
files.push(relPath);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Unreadable directory: nothing safe to plan for this surface.
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
for (const relPath of files) {
|
|
131
|
+
const { classification } = ctx.classifyArtifact(relPath);
|
|
132
|
+
if (classification === 'managed-pristine') {
|
|
133
|
+
actions.push({ type: 'remove-managed', relPath, reason: FILE_REASON, ownershipEvidence: FILE_OWNERSHIP_EVIDENCE });
|
|
134
|
+
}
|
|
135
|
+
else if (classification === 'managed-modified') {
|
|
136
|
+
actions.push({ type: 'backup-and-remove', relPath, reason: FILE_REASON, ownershipEvidence: FILE_OWNERSHIP_EVIDENCE });
|
|
137
|
+
}
|
|
138
|
+
// 'unknown' (user-added, not manifest-recorded): no action, preserved.
|
|
139
|
+
// 'missing' / 'managed-missing': impossible here — relPath was just
|
|
140
|
+
// discovered by walking the live filesystem, so it currently exists.
|
|
141
|
+
}
|
|
142
|
+
// Deepest directories first, so a child has already been evaluated
|
|
143
|
+
// (and possibly removed) before its parent's own emptiness is
|
|
144
|
+
// re-checked by the executor. The surface root itself is appended
|
|
145
|
+
// last, unconditionally: the executor's own emptiness re-check is what
|
|
146
|
+
// actually decides whether it goes, not this ordering.
|
|
147
|
+
const orderedDirs = [...dirs].sort((a, b) => b.split('/').length - a.split('/').length);
|
|
148
|
+
orderedDirs.push(subpath);
|
|
149
|
+
for (const relPath of orderedDirs) {
|
|
150
|
+
actions.push({
|
|
151
|
+
type: 'remove-empty-dir',
|
|
152
|
+
relPath,
|
|
153
|
+
reason: DIR_REASON,
|
|
154
|
+
ownershipEvidence: DIR_OWNERSHIP_EVIDENCE,
|
|
155
|
+
// Declared, not derived: classifyArtifact() hashes file contents
|
|
156
|
+
// via sha256File(), which throws EISDIR against a directory path.
|
|
157
|
+
// These relPaths name directories, so classification is stated
|
|
158
|
+
// directly (never 'unknown', so the planner's
|
|
159
|
+
// unknown-classification block never fires for them).
|
|
160
|
+
classification: 'managed-pristine',
|
|
161
|
+
originalHash: null,
|
|
162
|
+
currentHash: null,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return actions;
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
module.exports = migration;
|