@opengsd/gsd-core 1.11.0 → 1.13.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/.opencode/plugins/gsd-core.js +12 -0
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +1 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-dom-verifier.md +169 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +78 -42
- package/agents/gsd-framework-selector.md +1 -3
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +0 -1
- package/agents/gsd-pattern-mapper.md +11 -0
- package/agents/gsd-phase-researcher.md +3 -1
- package/agents/gsd-plan-checker.md +91 -112
- package/agents/gsd-planner.md +20 -4
- 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 +82 -7
- package/agents/gsd-ui-researcher.md +70 -3
- package/agents/gsd-verifier.md +24 -2
- package/bin/install.js +847 -200
- 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/ns-workflow.md +2 -1
- package/commands/gsd/phase.md +1 -1
- package/commands/gsd/quick-batch.md +105 -0
- package/commands/gsd/quick.md +8 -4
- package/commands/gsd/surface.md +18 -8
- package/gsd-core/bin/gsd-tools.cjs +761 -100
- package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
- package/gsd-core/bin/lib/adr-parser.cjs +13 -7
- package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
- package/gsd-core/bin/lib/api-coverage.cjs +30 -9
- package/gsd-core/bin/lib/artifacts.cjs +2 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
- package/gsd-core/bin/lib/audit.cjs +163 -41
- package/gsd-core/bin/lib/broken-windows.cjs +306 -28
- package/gsd-core/bin/lib/capability-activation.cjs +27 -0
- package/gsd-core/bin/lib/capability-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +785 -144
- package/gsd-core/bin/lib/capability-state.cjs +25 -4
- package/gsd-core/bin/lib/capability-validator.cjs +321 -18
- package/gsd-core/bin/lib/capability-writer.cjs +14 -4
- package/gsd-core/bin/lib/check-command-router.cjs +229 -6
- package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/clusters.cjs +1 -0
- package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
- package/gsd-core/bin/lib/command-aliases.cjs +16 -0
- package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
- package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
- package/gsd-core/bin/lib/commands.cjs +877 -54
- package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
- package/gsd-core/bin/lib/config-loader.cjs +121 -29
- package/gsd-core/bin/lib/config.cjs +92 -2
- package/gsd-core/bin/lib/configuration.cjs +129 -37
- package/gsd-core/bin/lib/core-utils.cjs +118 -14
- package/gsd-core/bin/lib/decisions.cjs +213 -1
- package/gsd-core/bin/lib/edge-probe.cjs +23 -2
- 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/file-overlap-partitioner.cjs +74 -0
- package/gsd-core/bin/lib/frontmatter.cjs +975 -326
- package/gsd-core/bin/lib/gap-checker.cjs +41 -8
- package/gsd-core/bin/lib/git-base-branch.cjs +182 -39
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +60 -14
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +75 -22
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
- package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
- package/gsd-core/bin/lib/host-integration.cjs +96 -11
- package/gsd-core/bin/lib/init-command-router.cjs +132 -21
- package/gsd-core/bin/lib/init.cjs +252 -56
- package/gsd-core/bin/lib/install-engine.cjs +252 -15
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +78 -1
- package/gsd-core/bin/lib/install-profiles.cjs +100 -18
- package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
- package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
- package/gsd-core/bin/lib/intel.cjs +101 -26
- package/gsd-core/bin/lib/io.cjs +195 -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/loop-resolver.cjs +14 -8
- package/gsd-core/bin/lib/markdown-table.cjs +175 -4
- package/gsd-core/bin/lib/milestone.cjs +112 -7
- package/gsd-core/bin/lib/model-catalog.cjs +177 -19
- package/gsd-core/bin/lib/model-resolver.cjs +10 -28
- package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
- package/gsd-core/bin/lib/phase-command-router.cjs +13 -6
- package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
- package/gsd-core/bin/lib/phase-id.cjs +321 -13
- package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
- package/gsd-core/bin/lib/phase-locator.cjs +138 -17
- package/gsd-core/bin/lib/phase.cjs +1175 -115
- package/gsd-core/bin/lib/plan-document.cjs +273 -0
- package/gsd-core/bin/lib/plan-scan.cjs +13 -2
- 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-snapshot.cjs +165 -34
- package/gsd-core/bin/lib/planning-workspace.cjs +159 -28
- package/gsd-core/bin/lib/probe-core.cjs +4 -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/quick-batch-command-router.cjs +285 -0
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
- package/gsd-core/bin/lib/quick-batch.cjs +840 -0
- package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +62 -14
- package/gsd-core/bin/lib/review-lane-invocation.cjs +73 -1
- package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
- package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
- package/gsd-core/bin/lib/roadmap-parser.cjs +577 -41
- package/gsd-core/bin/lib/roadmap.cjs +248 -64
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +329 -41
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +16 -17
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +320 -109
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +487 -83
- package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
- package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
- package/gsd-core/bin/lib/shell-command-projection.cjs +75 -8
- package/gsd-core/bin/lib/smart-entry.cjs +19 -31
- package/gsd-core/bin/lib/spec-section.cjs +12 -7
- package/gsd-core/bin/lib/state-command-router.cjs +47 -18
- package/gsd-core/bin/lib/state-contract.cjs +359 -0
- package/gsd-core/bin/lib/state-document.cjs +216 -5
- package/gsd-core/bin/lib/state-md-schema.cjs +231 -0
- package/gsd-core/bin/lib/state-transition.cjs +850 -145
- package/gsd-core/bin/lib/state.cjs +1629 -287
- package/gsd-core/bin/lib/surface.cjs +33 -10
- 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/tdd-red-evidence.cjs +133 -0
- package/gsd-core/bin/lib/teams-status.cjs +4 -1
- package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
- package/gsd-core/bin/lib/uat.cjs +2542 -387
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
- package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
- package/gsd-core/bin/lib/unusable-input.cjs +13 -0
- package/gsd-core/bin/lib/update-context.cjs +6 -2
- package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
- package/gsd-core/bin/lib/validate.cjs +230 -12
- package/gsd-core/bin/lib/vendor/README.md +43 -5
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +287 -13
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
- package/gsd-core/bin/lib/verify.cjs +441 -56
- package/gsd-core/bin/lib/workstream-inventory.cjs +20 -2
- package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
- package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
- package/gsd-core/bin/lib/worktree-safety.cjs +185 -21
- package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
- 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/bin/verify-reapply-patches.cjs +70 -3
- package/gsd-core/references/agent-contracts.md +6 -5
- 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 +37 -19
- package/gsd-core/references/decimal-phase-calculation.md +5 -5
- package/gsd-core/references/edge-probe.md +17 -5
- package/gsd-core/references/execute-mvp-tdd.md +18 -18
- package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
- package/gsd-core/references/execute-phase-response-language.md +6 -0
- package/gsd-core/references/execute-phase-wave-guard.md +11 -9
- package/gsd-core/references/executor-examples.md +42 -0
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
- 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 +3 -3
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +22 -0
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/mvp-concepts.md +2 -2
- 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/plan-checker-examples.md +41 -0
- package/gsd-core/references/planner-antipatterns.md +25 -0
- package/gsd-core/references/planner-chunked.md +5 -1
- package/gsd-core/references/planner-coupling.md +42 -0
- package/gsd-core/references/planner-failing-direction.md +53 -0
- package/gsd-core/references/planner-human-verify-mode.md +15 -1
- package/gsd-core/references/planner-quick-batch.md +71 -0
- package/gsd-core/references/planner-reviews.md +47 -0
- package/gsd-core/references/planner-revision.md +76 -3
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +39 -9
- package/gsd-core/references/response-language-directive.md +9 -0
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/revision-loop.md +118 -11
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/tdd.md +15 -12
- 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 +2 -2
- package/gsd-core/references/verifier-evidence-gate.md +160 -0
- package/gsd-core/references/verify-command-path-resolvability.md +42 -0
- package/gsd-core/references/verify-mvp-mode.md +1 -1
- package/gsd-core/references/workstream-flag.md +11 -11
- 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/phase-prompt.md +7 -0
- package/gsd-core/templates/state.md +7 -0
- package/gsd-core/templates/verification-report.md +5 -0
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +3 -1
- package/gsd-core/workflows/add-phase.md +5 -3
- package/gsd-core/workflows/add-tests.md +4 -9
- package/gsd-core/workflows/add-todo.md +2 -2
- package/gsd-core/workflows/ai-integration-phase.md +5 -10
- package/gsd-core/workflows/analyze-dependencies.md +2 -0
- package/gsd-core/workflows/audit-fix.md +14 -3
- package/gsd-core/workflows/audit-milestone.md +11 -9
- package/gsd-core/workflows/audit-uat.md +19 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +12 -26
- package/gsd-core/workflows/check-todos.md +2 -2
- package/gsd-core/workflows/cleanup.md +3 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +16 -14
- package/gsd-core/workflows/code-review-fix.md +3 -1
- package/gsd-core/workflows/code-review.md +192 -69
- package/gsd-core/workflows/complete-milestone.md +28 -14
- package/gsd-core/workflows/debug.md +6 -4
- package/gsd-core/workflows/diagnose-issues.md +17 -7
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +3 -1
- package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -7
- package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/text.md +3 -1
- package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
- package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +3 -3
- package/gsd-core/workflows/discuss-phase-power.md +2 -0
- package/gsd-core/workflows/discuss-phase.md +2 -2
- package/gsd-core/workflows/do.md +46 -19
- package/gsd-core/workflows/docs-update.md +6 -5
- package/gsd-core/workflows/edit-phase.md +3 -1
- package/gsd-core/workflows/eval-review.md +5 -10
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +129 -11
- 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 +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +29 -5
- 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 +4 -2
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
- package/gsd-core/workflows/execute-phase.md +68 -66
- package/gsd-core/workflows/execute-plan.md +25 -20
- package/gsd-core/workflows/explore.md +3 -1
- package/gsd-core/workflows/extract-learnings.md +3 -1
- package/gsd-core/workflows/fast.md +8 -2
- package/gsd-core/workflows/forensics.md +3 -1
- package/gsd-core/workflows/graduation.md +6 -6
- package/gsd-core/workflows/health.md +4 -7
- package/gsd-core/workflows/help/modes/brief.md +2 -0
- package/gsd-core/workflows/help/modes/default.md +2 -0
- package/gsd-core/workflows/help/modes/full.md +12 -0
- package/gsd-core/workflows/help/modes/topic.md +2 -0
- package/gsd-core/workflows/help.md +2 -0
- package/gsd-core/workflows/import.md +17 -14
- package/gsd-core/workflows/inbox.md +5 -6
- package/gsd-core/workflows/ingest-docs.md +45 -12
- package/gsd-core/workflows/insert-phase.md +7 -5
- package/gsd-core/workflows/list-phase-assumptions.md +2 -0
- package/gsd-core/workflows/list-seeds.md +7 -3
- package/gsd-core/workflows/list-workspaces.md +3 -1
- package/gsd-core/workflows/manager.md +15 -26
- package/gsd-core/workflows/map-codebase.md +3 -1
- package/gsd-core/workflows/milestone-summary.md +3 -1
- package/gsd-core/workflows/mvp-phase.md +3 -3
- package/gsd-core/workflows/new-milestone.md +10 -22
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +17 -29
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +4 -2
- package/gsd-core/workflows/node-repair.md +2 -0
- package/gsd-core/workflows/note.md +2 -0
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +20 -5
- 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 +100 -18
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +4 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +12 -3
- package/gsd-core/workflows/plan-phase.md +251 -54
- package/gsd-core/workflows/plan-review-convergence.md +148 -19
- package/gsd-core/workflows/plant-seed.md +3 -3
- package/gsd-core/workflows/pr-branch.md +195 -51
- package/gsd-core/workflows/profile-user.md +17 -15
- package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
- package/gsd-core/workflows/progress.md +52 -15
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +38 -5
- package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
- package/gsd-core/workflows/quick/steps/research-phase.md +5 -7
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
- package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
- package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
- package/gsd-core/workflows/quick-batch.md +203 -0
- package/gsd-core/workflows/quick.md +33 -32
- package/gsd-core/workflows/reapply-patches.md +2 -0
- package/gsd-core/workflows/remove-phase.md +6 -4
- package/gsd-core/workflows/remove-workspace.md +3 -3
- package/gsd-core/workflows/resume-project.md +14 -14
- package/gsd-core/workflows/review.md +404 -21
- package/gsd-core/workflows/scan.md +3 -1
- package/gsd-core/workflows/section-manifest.json +12 -0
- package/gsd-core/workflows/secure-phase.md +3 -3
- package/gsd-core/workflows/session-report.md +2 -0
- package/gsd-core/workflows/settings-advanced.md +9 -9
- package/gsd-core/workflows/settings-integrations.md +66 -32
- package/gsd-core/workflows/settings.md +4 -6
- package/gsd-core/workflows/ship.md +22 -16
- package/gsd-core/workflows/sketch-wrap-up.md +13 -17
- package/gsd-core/workflows/sketch.md +13 -19
- package/gsd-core/workflows/smart-entry.md +4 -6
- package/gsd-core/workflows/spec-phase.md +31 -4
- package/gsd-core/workflows/spike-wrap-up.md +9 -11
- package/gsd-core/workflows/spike.md +21 -32
- package/gsd-core/workflows/stats.md +4 -2
- package/gsd-core/workflows/sync-skills.md +13 -5
- package/gsd-core/workflows/thread.md +13 -7
- package/gsd-core/workflows/transition.md +7 -5
- package/gsd-core/workflows/ui-phase.md +36 -21
- package/gsd-core/workflows/ui-review.md +7 -11
- package/gsd-core/workflows/ultraplan-phase.md +7 -13
- package/gsd-core/workflows/undo.md +9 -17
- package/gsd-core/workflows/update.md +47 -48
- 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 +106 -21
- package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
- package/hooks/dist/gsd-check-update-worker.js +19 -2
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +302 -22
- package/hooks/dist/gsd-cursor-post-tool.js +3 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
- 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 +28 -23
- package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
- 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 +77 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +46 -12
- package/hooks/dist/gsd-read-guard.js +18 -7
- package/hooks/dist/gsd-read-injection-scanner.js +22 -13
- package/hooks/dist/gsd-secret-read-guard.js +1079 -0
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +222 -29
- package/hooks/dist/gsd-validate-commit.sh +523 -12
- 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 +36 -17
- 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 +210 -1
- 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 +36 -6
- package/hooks/dist/managed-hooks-registry.cjs +4 -0
- package/hooks/gsd-agent-isolation-guard.js +77 -38
- package/hooks/gsd-check-update-worker.js +19 -2
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +302 -22
- package/hooks/gsd-cursor-post-tool.js +3 -1
- package/hooks/gsd-cursor-pre-tool.js +3 -1
- package/hooks/gsd-cursor-session-start.js +2 -1
- package/hooks/gsd-cursor-stop.js +2 -1
- package/hooks/gsd-cursor-subagent-start.js +28 -23
- package/hooks/gsd-cursor-subagent-stop.js +3 -1
- package/hooks/gsd-ensure-canonical-path.js +2 -1
- package/hooks/gsd-graphify-update.sh +22 -18
- package/hooks/gsd-node-runner.sh +77 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +46 -12
- package/hooks/gsd-read-guard.js +18 -7
- package/hooks/gsd-read-injection-scanner.js +22 -13
- package/hooks/gsd-secret-read-guard.js +1079 -0
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +222 -29
- package/hooks/gsd-validate-commit.sh +523 -12
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +36 -17
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/hooks.json +6 -0
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-cmd.js +210 -1
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/lib/injection-patterns.js +36 -6
- package/hooks/managed-hooks-registry.cjs +4 -0
- package/package.json +14 -9
- package/scripts/base64-scan.sh +74 -12
- package/scripts/build-hooks.js +12 -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 +52 -12
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/docs-guard-registry.cjs +406 -0
- package/scripts/gen-capability-registry.cjs +8 -6
- 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-hooks-cli-exit.cjs +239 -0
- package/scripts/gen-install-tree-fixtures.cjs +2 -2
- package/scripts/gen-loop-host-contract.cjs +189 -4
- 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/ci-job-timing.cjs +72 -0
- package/scripts/lib/cli-exit.cjs +546 -44
- package/scripts/lib/drift-scan.cjs +32 -2
- package/scripts/lib/exit-code-registry.cjs +98 -0
- package/scripts/lib/ndjson-reporter.cjs +119 -0
- package/scripts/lib/shellcheck-fetch.cjs +247 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +198 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
- package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
- package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
- package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
- package/scripts/lint-phase-enumeration-drift.cjs +45 -14
- package/scripts/lint-phase-id-drift.cjs +133 -8
- package/scripts/lint-planning-prompt-drift.cjs +38 -1
- package/scripts/lint-portable-grep.cjs +176 -0
- package/scripts/lint-removed-but-needed.cjs +184 -16
- package/scripts/lint-response-language-coverage.cjs +524 -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-write-path-drift.cjs +337 -432
- package/scripts/lint-test-file-count.allowlist.json +124 -4
- package/scripts/lint-test-file-count.cjs +25 -3
- package/scripts/lint-unreachable-guard-drift.cjs +51 -64
- package/scripts/lint-vendored-deps.cjs +208 -35
- package/scripts/lint-workflow-shellcheck-baseline.json +1027 -0
- package/scripts/lint-workflow-shellcheck.cjs +614 -0
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/npm-audit-baseline.cjs +376 -0
- package/scripts/prompt-injection-scan.sh +83 -14
- package/scripts/require-issue-link-policy.cjs +16 -1
- package/scripts/secret-scan.sh +75 -13
- package/scripts/select-docs-guards.cjs +56 -0
- package/scripts/sync-runtime-launcher.cjs +22 -3
- package/skills/gsd-discuss-phase/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -1
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-ns-workflow/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +1 -1
- package/skills/gsd-quick/SKILL.md +8 -4
- package/skills/gsd-quick-batch/SKILL.md +105 -0
- package/skills/gsd-surface/SKILL.md +18 -8
- package/vscode/package.json +1 -1
- package/bin/lib/ui-safety-gate.cjs +0 -109
- package/scripts/lint-emitted-drift-ack.cjs +0 -344
- package/scripts/state-write-path-drift-baseline.json +0 -19
|
@@ -32,7 +32,7 @@ const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs")
|
|
|
32
32
|
const external_descriptor_trust_cjs_1 = require("./external-descriptor-trust.cjs");
|
|
33
33
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
34
34
|
const conversionModule = require("./runtime-artifact-conversion.cjs");
|
|
35
|
-
const { applyAgentPathRewrites: _applyAgentPathRewrites, processAttribution: _processAttribution, normalizeAgentBodyForRuntime: _normalizeAgentBodyForRuntime, readGsdCommandNames: _readGsdCommandNames, deriveAgentName: _deriveAgentName, applyAgentFrontmatterExtensions: _applyAgentFrontmatterExtensions, } = conversionModule;
|
|
35
|
+
const { applyAgentPathRewrites: _applyAgentPathRewrites, processAttribution: _processAttribution, normalizeAgentBodyForRuntime: _normalizeAgentBodyForRuntime, readGsdCommandNames: _readGsdCommandNames, deriveAgentName: _deriveAgentName, applyAgentFrontmatterExtensions: _applyAgentFrontmatterExtensions, appendAgentTools: _appendAgentTools, } = conversionModule;
|
|
36
36
|
// #2995 (epic #1671 Phase 6.4): agent bodies join the fragment model. Markers are
|
|
37
37
|
// stripped at emit BEFORE any path rewrite or converter runs, so a `.claude/` ->
|
|
38
38
|
// `.windsurf/` regex can never reach inside a marker attribute and corrupt it —
|
|
@@ -40,6 +40,8 @@ const { applyAgentPathRewrites: _applyAgentPathRewrites, processAttribution: _pr
|
|
|
40
40
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
41
41
|
const workflowFragmentsModule = require("./workflow-fragments.cjs");
|
|
42
42
|
const { composeWorkflow: _composeWorkflow } = workflowFragmentsModule;
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
44
|
+
const installModelOverrideResolver = require("./install-model-override-resolver.cjs");
|
|
43
45
|
// ---------------------------------------------------------------------------
|
|
44
46
|
// Profile definitions
|
|
45
47
|
// ---------------------------------------------------------------------------
|
|
@@ -137,9 +139,79 @@ function parseCallsAgents(content) {
|
|
|
137
139
|
* Also derives calls_agents for each skill by scanning the body text for
|
|
138
140
|
* `gsd-*` agent name references. Agent stems are stored under the special
|
|
139
141
|
* key `_calls_agents_<stem>` so they don't conflict with skill stems.
|
|
142
|
+
*
|
|
143
|
+
* #3798: command bodies are thin delegators — the actual `subagent_type=`
|
|
144
|
+
* spawns live in the workflow files each command references
|
|
145
|
+
* (`@…/workflows/<name>.md`). The agent derivation therefore ALSO reads every
|
|
146
|
+
* workflow file the command references (plus the workflow's steps/ fragments
|
|
147
|
+
* when it is split per the progressive-disclosure pattern) and unions their
|
|
148
|
+
* `gsd-*` tokens into the same `_calls_agents_<stem>` set. Without this,
|
|
149
|
+
* tiered profiles omitted agents their own installed skills spawn
|
|
150
|
+
* (gsd-verifier at execute-phase's verify_phase_goal was the filed repro).
|
|
151
|
+
* Over-inclusion fails safe: a tiered profile installing one extra agent
|
|
152
|
+
* costs bytes, never a broken spawn.
|
|
140
153
|
*/
|
|
141
154
|
const DEFAULT_COMMANDS_DIR = node_path_1.default.resolve(__dirname, '..', '..', '..', 'commands', 'gsd');
|
|
142
|
-
|
|
155
|
+
const DEFAULT_WORKFLOWS_DIR = node_path_1.default.resolve(__dirname, '..', '..', 'workflows');
|
|
156
|
+
/**
|
|
157
|
+
* Collect the `gsd-*` agent tokens from every workflow file a command body
|
|
158
|
+
* references. References are the `workflows/<name>.md` path suffixes the
|
|
159
|
+
* delegating commands embed (`@…/gsd-core/workflows/<name>.md`). A
|
|
160
|
+
* split workflow (workflows/<name>/steps/*.md) contributes its fragments as
|
|
161
|
+
* well, because the parent dispatches into them and the spawns live there.
|
|
162
|
+
*/
|
|
163
|
+
function workflowAgentRefs(content, workflowsDir) {
|
|
164
|
+
const refs = content.match(/workflows\/([a-z0-9][a-z0-9-]*)\.md/g) || [];
|
|
165
|
+
const names = [...new Set(refs.map((r) => r.slice('workflows/'.length)))];
|
|
166
|
+
const tokens = new Set();
|
|
167
|
+
for (const name of names) {
|
|
168
|
+
const direct = node_path_1.default.join(workflowsDir, name);
|
|
169
|
+
let body = null;
|
|
170
|
+
try {
|
|
171
|
+
body = node_fs_1.default.readFileSync(direct, 'utf8');
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
body = null;
|
|
175
|
+
}
|
|
176
|
+
if (body === null)
|
|
177
|
+
continue;
|
|
178
|
+
for (const tok of parseCallsAgents(body))
|
|
179
|
+
tokens.add(tok);
|
|
180
|
+
// Split workflow: union EVERY fragment under the workflow's directory
|
|
181
|
+
// (steps/, modes/, templates/…) — the parent dispatches into these per
|
|
182
|
+
// the progressive-disclosure pattern, and spawns live in all of them
|
|
183
|
+
// (#3798 review: modes/ carried gsd-advisor-researcher's spawn while the
|
|
184
|
+
// parent only named it incidentally in prose).
|
|
185
|
+
const fragDir = node_path_1.default.join(workflowsDir, name.slice(0, -3));
|
|
186
|
+
const walk = (dir) => {
|
|
187
|
+
let frags;
|
|
188
|
+
try {
|
|
189
|
+
frags = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
for (const frag of frags) {
|
|
195
|
+
const full = node_path_1.default.join(dir, frag.name);
|
|
196
|
+
if (frag.isDirectory()) {
|
|
197
|
+
walk(full);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (!frag.isFile() || !frag.name.endsWith('.md'))
|
|
201
|
+
continue;
|
|
202
|
+
try {
|
|
203
|
+
const fragBody = node_fs_1.default.readFileSync(full, 'utf8');
|
|
204
|
+
for (const tok of parseCallsAgents(fragBody))
|
|
205
|
+
tokens.add(tok);
|
|
206
|
+
}
|
|
207
|
+
catch { /* unreadable fragment — skip */ }
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
walk(fragDir);
|
|
211
|
+
}
|
|
212
|
+
return [...tokens];
|
|
213
|
+
}
|
|
214
|
+
function loadSkillsManifest(commandsDir = DEFAULT_COMMANDS_DIR, workflowsDir = DEFAULT_WORKFLOWS_DIR) {
|
|
143
215
|
const manifest = new Map();
|
|
144
216
|
if (!node_fs_1.default.existsSync(commandsDir))
|
|
145
217
|
return manifest;
|
|
@@ -153,9 +225,12 @@ function loadSkillsManifest(commandsDir = DEFAULT_COMMANDS_DIR) {
|
|
|
153
225
|
try {
|
|
154
226
|
const content = node_fs_1.default.readFileSync(node_path_1.default.join(commandsDir, entry.name), 'utf8');
|
|
155
227
|
manifest.set(stem, parseRequires(content));
|
|
156
|
-
// Derive agent references from body text
|
|
157
|
-
const agentRefs =
|
|
158
|
-
|
|
228
|
+
// Derive agent references from body text + the workflows it delegates to
|
|
229
|
+
const agentRefs = [
|
|
230
|
+
...parseCallsAgents(content),
|
|
231
|
+
...workflowAgentRefs(content, workflowsDir),
|
|
232
|
+
];
|
|
233
|
+
manifest.set(`_calls_agents_${stem}`, [...new Set(agentRefs)]);
|
|
159
234
|
}
|
|
160
235
|
catch {
|
|
161
236
|
manifest.set(stem, []);
|
|
@@ -823,14 +898,14 @@ function stageSkillsForRuntimeAsSkills(srcCommandsDir, resolvedProfile, converte
|
|
|
823
898
|
* agent loop in bin/install.js exactly:
|
|
824
899
|
* 1. applyAgentPathRewrites (4 base ~/.claude/ regexes; skipped for copilot/antigravity)
|
|
825
900
|
* 2. processAttribution (Co-Authored-By policy)
|
|
826
|
-
* 3.
|
|
827
|
-
* 4.
|
|
901
|
+
* 3. appendAgentTools (#4032: validated agent_tools grants, before host conversion)
|
|
902
|
+
* 4. converter (runtime-specific frontmatter/body transform)
|
|
903
|
+
* 5. applyAgentFrontmatterExtensions (#2875 Part 2: effort/disallowedTools,
|
|
828
904
|
* gated by hostBehaviors.agentFrontmatterExtensions — no-op for a runtime
|
|
829
905
|
* that declares nothing, e.g. every non-Claude runtime today)
|
|
830
|
-
*
|
|
831
|
-
* When `agentCtx` is absent, only
|
|
832
|
-
*
|
|
833
|
-
* that handle cross-cutting inside their converters).
|
|
906
|
+
* 6. normalizeAgentBodyForRuntime (colon→hyphen refs; no-op for trivial group)
|
|
907
|
+
* When `agentCtx` is absent, only global `agent_tools` augmentation and the
|
|
908
|
+
* converter run; other cross-cutting remains absent for backward compatibility.
|
|
834
909
|
*
|
|
835
910
|
* @param srcAgentsDir source agents directory (e.g. agents/)
|
|
836
911
|
* @param resolvedProfile profile filter from resolveProfile()
|
|
@@ -844,8 +919,7 @@ function stageSkillsForRuntimeAsSkills(srcCommandsDir, resolvedProfile, converte
|
|
|
844
919
|
* every OTHER converter's contract — converters that don't declare a
|
|
845
920
|
* 3rd parameter simply never read it.
|
|
846
921
|
* @param isGlobal install scope passed through to the converter
|
|
847
|
-
* @param agentCtx optional cross-cutting context (ADR-1235 §1)
|
|
848
|
-
* only the converter is applied (backward compat)
|
|
922
|
+
* @param agentCtx optional cross-cutting context (ADR-1235 §1)
|
|
849
923
|
*/
|
|
850
924
|
function stageAgentsForRuntimeWithConverter(srcAgentsDir, resolvedProfile, converter, isGlobal = false, agentCtx) {
|
|
851
925
|
if (!installFs().existsSync(srcAgentsDir))
|
|
@@ -877,6 +951,7 @@ function stageAgentsForRuntimeWithConverter(srcAgentsDir, resolvedProfile, conve
|
|
|
877
951
|
try {
|
|
878
952
|
// Resolve cmdNames once per staging call (not per file) for performance.
|
|
879
953
|
const cmdNames = agentCtx ? _readGsdCommandNames() : [];
|
|
954
|
+
const agentTools = installModelOverrideResolver.readGsdEffectiveAgentTools(agentCtx?.projectDir ?? agentCtx?.targetDir ?? null);
|
|
880
955
|
for (const entry of entries) {
|
|
881
956
|
if (!entry.isFile())
|
|
882
957
|
continue;
|
|
@@ -896,25 +971,31 @@ function stageAgentsForRuntimeWithConverter(srcAgentsDir, resolvedProfile, conve
|
|
|
896
971
|
// throws loudly naming the file for a malformed marker, never emitting a
|
|
897
972
|
// half-composed agent.
|
|
898
973
|
content = _composeWorkflow(content, { sourcePath: agentSourcePath });
|
|
974
|
+
const agentName = _deriveAgentName(entry.name);
|
|
975
|
+
const grants = [
|
|
976
|
+
...(agentTools?.['*'] || []),
|
|
977
|
+
...(agentTools?.[agentName] || []),
|
|
978
|
+
];
|
|
899
979
|
if (agentCtx) {
|
|
900
980
|
// #2875 Part 2 / row I3: derived exactly as the inline loop does —
|
|
901
981
|
// single-sourced via deriveAgentName (runtime-artifact-conversion.cts).
|
|
902
|
-
const agentName = _deriveAgentName(entry.name);
|
|
903
982
|
// ADR-1235 §1: pre-converter cross-cutting (matches inline loop order exactly)
|
|
904
983
|
// Step 1: path rewrites (4 base ~/.claude/ regexes; skipped for copilot/antigravity)
|
|
905
984
|
content = _applyAgentPathRewrites(content, agentCtx.runtime, agentCtx.pathPrefix);
|
|
906
985
|
// Step 2: attribution
|
|
907
986
|
content = _processAttribution(content, agentCtx.attribution);
|
|
908
|
-
// Step 3:
|
|
987
|
+
// Step 3: validated canonical grants, before host conversion.
|
|
988
|
+
content = _appendAgentTools(content, grants);
|
|
989
|
+
// Step 4: converter (runtime-specific frontmatter/body transform)
|
|
909
990
|
content = converter(content, isGlobal, { agentName });
|
|
910
|
-
// Step
|
|
991
|
+
// Step 5: frontmatter extensions (effort/disallowedTools; no-op unless
|
|
911
992
|
// the runtime declares hostBehaviors.agentFrontmatterExtensions)
|
|
912
993
|
content = _applyAgentFrontmatterExtensions(content, { runtime: agentCtx.runtime, agentName, targetDir: agentCtx.targetDir });
|
|
913
|
-
// Step
|
|
994
|
+
// Step 6: normalize colon→hyphen refs (no-op for trivial group)
|
|
914
995
|
content = _normalizeAgentBodyForRuntime(content, agentCtx.runtime, cmdNames);
|
|
915
996
|
}
|
|
916
997
|
else {
|
|
917
|
-
|
|
998
|
+
content = _appendAgentTools(content, grants);
|
|
918
999
|
content = converter(content, isGlobal);
|
|
919
1000
|
}
|
|
920
1001
|
installFs().writeFileSync(node_path_1.default.join(stageDir, entry.name), content, 'utf8');
|
|
@@ -1156,6 +1237,7 @@ module.exports = {
|
|
|
1156
1237
|
// Shared internals
|
|
1157
1238
|
parseRequires,
|
|
1158
1239
|
parseCallsAgents,
|
|
1240
|
+
workflowAgentRefs,
|
|
1159
1241
|
cleanupStagedSkills,
|
|
1160
1242
|
// #2322: capability-skill security seams — exported for direct unit-testing
|
|
1161
1243
|
// and for surface.cts's prune pass (CAPABILITY_SKILL_MARKER parity).
|
|
@@ -48,10 +48,14 @@ 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',
|
|
54
57
|
'hooks/gsd-read-injection-scanner.js',
|
|
58
|
+
'hooks/gsd-secret-read-guard.js',
|
|
55
59
|
'hooks/gsd-session-state.sh',
|
|
56
60
|
'hooks/gsd-statusline.js',
|
|
57
61
|
'hooks/gsd-update-banner.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;
|
|
@@ -505,17 +505,20 @@ function acquireInstallMigrationLock(configDir, { timeoutMs = DEFAULT_LOCK_TIMEO
|
|
|
505
505
|
let lockCreatedByUs = false;
|
|
506
506
|
try {
|
|
507
507
|
fd = node_fs_1.default.openSync(lockPath, 'wx');
|
|
508
|
-
// Close the open descriptor before writing so the file handle is
|
|
509
|
-
// released on Windows before the release closure unlinks it.
|
|
510
|
-
// Write payload via writeFileSync with the path (not the fd) so we
|
|
511
|
-
// don't hold an open fd across the lifetime of the lock.
|
|
512
|
-
node_fs_1.default.closeSync(fd);
|
|
513
|
-
fd = null;
|
|
514
508
|
lockCreatedByUs = true; // we own the file; clean it up on any subsequent error
|
|
515
|
-
|
|
509
|
+
// Write the payload through the exclusively-created descriptor: a
|
|
510
|
+
// second open-by-path here would be a TOCTOU window (CWE-367) where a
|
|
511
|
+
// co-writer of the directory could symlink-swap the just-created empty
|
|
512
|
+
// lock file before the payload lands.
|
|
513
|
+
node_fs_1.default.writeFileSync(fd, JSON.stringify({
|
|
516
514
|
pid: process.pid,
|
|
517
515
|
acquiredAt: new Date().toISOString(),
|
|
518
516
|
}) + '\n');
|
|
517
|
+
// Close before returning so no handle stays open across the lock's
|
|
518
|
+
// lifetime — Windows cannot unlink a file with an open handle when the
|
|
519
|
+
// release closure runs.
|
|
520
|
+
node_fs_1.default.closeSync(fd);
|
|
521
|
+
fd = null;
|
|
519
522
|
lockCreatedByUs = false; // release closure owns cleanup from here
|
|
520
523
|
return () => {
|
|
521
524
|
const failures = [];
|
|
@@ -23,6 +23,9 @@ const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs")
|
|
|
23
23
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
24
24
|
const capabilityStateMod = require("./capability-state.cjs");
|
|
25
25
|
const { isCapabilityActive } = capabilityStateMod;
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
27
|
+
const ioMod = require("./io.cjs");
|
|
28
|
+
const { formatDiagnosticToken } = ioMod;
|
|
26
29
|
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
27
30
|
const INTEL_DIR = '.planning/intel';
|
|
28
31
|
const INTEL_FILES = {
|
|
@@ -32,6 +35,16 @@ const INTEL_FILES = {
|
|
|
32
35
|
arch: 'arch-decisions.json',
|
|
33
36
|
stack: 'stack.json',
|
|
34
37
|
};
|
|
38
|
+
/**
|
|
39
|
+
* ADR-3473 §8.5 / #3885: recursion bound for the intel JSON search walk.
|
|
40
|
+
* Restored from the retired SDK lineage (`sdk/src/query/intel.ts` at `11918dcc3^`),
|
|
41
|
+
* lost in the ADR-0174 consolidation. Unlike the original, hitting the ceiling is
|
|
42
|
+
* NOT reported as a silent "no match" — the walk that stops early sets a
|
|
43
|
+
* `truncated` flag threaded back up to `intelQuery`'s result (Decision 4: a
|
|
44
|
+
* routine that discards an input says so). The bound is on DEPTH only; breadth
|
|
45
|
+
* (sibling count at any given depth) is unaffected.
|
|
46
|
+
*/
|
|
47
|
+
const MAX_JSON_SEARCH_DEPTH = 48;
|
|
35
48
|
// ─── Internal helpers ────────────────────────────────────────────────────────
|
|
36
49
|
/**
|
|
37
50
|
* Ensure the intel directory exists under the given planning dir.
|
|
@@ -71,16 +84,42 @@ function intelFilePath(planningDir, filename) {
|
|
|
71
84
|
}
|
|
72
85
|
/**
|
|
73
86
|
* Safely read and parse a JSON intel file.
|
|
74
|
-
*
|
|
87
|
+
*
|
|
88
|
+
* Returns null for THREE distinct on-disk states, only one of which is a
|
|
89
|
+
* "quiet" case (#3885, ADR-3473 §8.5):
|
|
90
|
+
* - ABSENT (ENOENT, via platformReadSync returning null): silent — not
|
|
91
|
+
* every project has every intel file, and callers already loop over
|
|
92
|
+
* the full INTEL_FILES set expecting misses. Never pushed to `errors`.
|
|
93
|
+
* - UNREADABLE (EACCES/EIO/... — platformReadSync rethrows anything that
|
|
94
|
+
* isn't ENOENT): surfaced, naming the file, when `errors` is supplied.
|
|
95
|
+
* - MALFORMED (JSON.parse throws on a file that WAS read successfully):
|
|
96
|
+
* also surfaced, naming the file — a corrupt intel file used to read
|
|
97
|
+
* identically to "no matches", which is the same defect one layer down.
|
|
98
|
+
*
|
|
99
|
+
* `errors` is an optional accumulator so callers that want to thread the
|
|
100
|
+
* outcome into their own result shape can pass an array and read it back
|
|
101
|
+
* after the call; callers that omit it keep the prior fold-to-null shape.
|
|
75
102
|
*/
|
|
76
|
-
function safeReadJson(filePath) {
|
|
103
|
+
function safeReadJson(filePath, errors) {
|
|
104
|
+
let raw;
|
|
105
|
+
try {
|
|
106
|
+
raw = (0, shell_command_projection_cjs_1.platformReadSync)(filePath);
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
if (errors) {
|
|
110
|
+
errors.push(`Could not read ${formatDiagnosticToken(filePath)}: ${formatDiagnosticToken(err?.message ?? String(err))}`);
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
if (raw === null)
|
|
115
|
+
return null; // ENOENT — genuinely absent, not an error.
|
|
77
116
|
try {
|
|
78
|
-
const raw = (0, shell_command_projection_cjs_1.platformReadSync)(filePath);
|
|
79
|
-
if (raw === null)
|
|
80
|
-
return null;
|
|
81
117
|
return JSON.parse(raw);
|
|
82
118
|
}
|
|
83
|
-
catch {
|
|
119
|
+
catch (err) {
|
|
120
|
+
if (errors) {
|
|
121
|
+
errors.push(`Could not parse ${formatDiagnosticToken(filePath)}: ${formatDiagnosticToken(err?.message ?? String(err))}`);
|
|
122
|
+
}
|
|
84
123
|
return null;
|
|
85
124
|
}
|
|
86
125
|
}
|
|
@@ -101,16 +140,17 @@ function hashFile(filePath) {
|
|
|
101
140
|
}
|
|
102
141
|
/**
|
|
103
142
|
* Search for a term (case-insensitive) in a JSON object's keys and string values.
|
|
104
|
-
* Returns
|
|
143
|
+
* Returns matching entries plus whether the walk hit MAX_JSON_SEARCH_DEPTH.
|
|
105
144
|
*/
|
|
106
145
|
function searchJsonEntries(data, term) {
|
|
107
146
|
if (!data || typeof data !== 'object')
|
|
108
|
-
return [];
|
|
147
|
+
return { matches: [], truncated: false };
|
|
109
148
|
const entries = data.entries || data;
|
|
110
149
|
if (!entries || typeof entries !== 'object')
|
|
111
|
-
return [];
|
|
150
|
+
return { matches: [], truncated: false };
|
|
112
151
|
const lowerTerm = term.toLowerCase();
|
|
113
152
|
const matches = [];
|
|
153
|
+
const state = { truncated: false };
|
|
114
154
|
for (const [key, value] of Object.entries(entries)) {
|
|
115
155
|
if (key === '_meta')
|
|
116
156
|
continue;
|
|
@@ -119,25 +159,40 @@ function searchJsonEntries(data, term) {
|
|
|
119
159
|
matches.push({ key, value });
|
|
120
160
|
continue;
|
|
121
161
|
}
|
|
122
|
-
// Check string value match (recursive for objects)
|
|
123
|
-
if (matchesInValue(value, lowerTerm)) {
|
|
162
|
+
// Check string value match (recursive for objects/arrays, bounded by depth)
|
|
163
|
+
if (matchesInValue(value, lowerTerm, 0, state)) {
|
|
124
164
|
matches.push({ key, value });
|
|
125
165
|
}
|
|
126
166
|
}
|
|
127
|
-
return matches;
|
|
167
|
+
return { matches, truncated: state.truncated };
|
|
128
168
|
}
|
|
129
169
|
/**
|
|
130
170
|
* Recursively check if a term appears in any string value.
|
|
171
|
+
*
|
|
172
|
+
* `depth` counts container unwraps already performed (starts at 0 for the
|
|
173
|
+
* entry's own value). Strings never fail the ceiling check themselves — only
|
|
174
|
+
* a container (object/array) refuses to recurse one level deeper once
|
|
175
|
+
* `depth > MAX_JSON_SEARCH_DEPTH`, at which point `state.truncated` is set so
|
|
176
|
+
* the caller can report "I stopped looking" rather than a bare "no match".
|
|
177
|
+
* The bound is on nesting depth only, never on sibling breadth.
|
|
131
178
|
*/
|
|
132
|
-
function matchesInValue(value, lowerTerm) {
|
|
179
|
+
function matchesInValue(value, lowerTerm, depth, state) {
|
|
133
180
|
if (typeof value === 'string') {
|
|
134
181
|
return value.toLowerCase().includes(lowerTerm);
|
|
135
182
|
}
|
|
136
183
|
if (Array.isArray(value)) {
|
|
137
|
-
|
|
184
|
+
if (depth > MAX_JSON_SEARCH_DEPTH) {
|
|
185
|
+
state.truncated = true;
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
return value.some(v => matchesInValue(v, lowerTerm, depth + 1, state));
|
|
138
189
|
}
|
|
139
190
|
if (value && typeof value === 'object') {
|
|
140
|
-
|
|
191
|
+
if (depth > MAX_JSON_SEARCH_DEPTH) {
|
|
192
|
+
state.truncated = true;
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
return Object.values(value).some(v => matchesInValue(v, lowerTerm, depth + 1, state));
|
|
141
196
|
}
|
|
142
197
|
return false;
|
|
143
198
|
}
|
|
@@ -150,19 +205,23 @@ function intelQuery(term, planningDir) {
|
|
|
150
205
|
return disabledResponse();
|
|
151
206
|
const matches = [];
|
|
152
207
|
let total = 0;
|
|
208
|
+
let truncated = false;
|
|
209
|
+
const readErrors = [];
|
|
153
210
|
// Search all JSON intel files
|
|
154
211
|
for (const [_key, filename] of Object.entries(INTEL_FILES)) {
|
|
155
212
|
const filePath = intelFilePath(planningDir, filename);
|
|
156
|
-
const data = safeReadJson(filePath);
|
|
213
|
+
const data = safeReadJson(filePath, readErrors);
|
|
157
214
|
if (!data)
|
|
158
215
|
continue;
|
|
159
|
-
const found = searchJsonEntries(data, term);
|
|
216
|
+
const { matches: found, truncated: fileTruncated } = searchJsonEntries(data, term);
|
|
217
|
+
if (fileTruncated)
|
|
218
|
+
truncated = true;
|
|
160
219
|
if (found.length > 0) {
|
|
161
220
|
matches.push({ source: filename, entries: found });
|
|
162
221
|
total += found.length;
|
|
163
222
|
}
|
|
164
223
|
}
|
|
165
|
-
return { matches, term, total };
|
|
224
|
+
return { matches, term, total, truncated, read_errors: readErrors };
|
|
166
225
|
}
|
|
167
226
|
/**
|
|
168
227
|
* Report status and staleness of each intel file.
|
|
@@ -179,13 +238,14 @@ function intelStatus(planningDir) {
|
|
|
179
238
|
const filePath = intelFilePath(planningDir, filename);
|
|
180
239
|
const exists = node_fs_1.default.existsSync(filePath);
|
|
181
240
|
if (!exists) {
|
|
182
|
-
files[filename] = { exists: false, updated_at: null, stale: true };
|
|
241
|
+
files[filename] = { exists: false, updated_at: null, stale: true, read_error: null };
|
|
183
242
|
overallStale = true;
|
|
184
243
|
continue;
|
|
185
244
|
}
|
|
186
245
|
let updatedAt = null;
|
|
187
246
|
// All intel files are JSON — read _meta.updated_at
|
|
188
|
-
const
|
|
247
|
+
const readErrors = [];
|
|
248
|
+
const data = safeReadJson(filePath, readErrors);
|
|
189
249
|
if (data && data._meta && data._meta.updated_at) {
|
|
190
250
|
updatedAt = data._meta.updated_at;
|
|
191
251
|
}
|
|
@@ -196,7 +256,7 @@ function intelStatus(planningDir) {
|
|
|
196
256
|
}
|
|
197
257
|
if (stale)
|
|
198
258
|
overallStale = true;
|
|
199
|
-
files[filename] = { exists: true, updated_at: updatedAt, stale };
|
|
259
|
+
files[filename] = { exists: true, updated_at: updatedAt, stale, read_error: readErrors[0] ?? null };
|
|
200
260
|
}
|
|
201
261
|
return { files, overall_stale: overallStale };
|
|
202
262
|
}
|
|
@@ -207,9 +267,15 @@ function intelDiff(planningDir) {
|
|
|
207
267
|
if (!isIntelCapabilityActive(planningDir))
|
|
208
268
|
return disabledResponse();
|
|
209
269
|
const snapshotPath = intelFilePath(planningDir, '.last-refresh.json');
|
|
210
|
-
const
|
|
270
|
+
const readErrors = [];
|
|
271
|
+
const snapshot = safeReadJson(snapshotPath, readErrors);
|
|
211
272
|
if (!snapshot) {
|
|
212
|
-
|
|
273
|
+
// #3885 (ADR-3473 §8.5): `no_baseline: true` stays true for BOTH a
|
|
274
|
+
// genuinely-never-snapshotted project (ENOENT, read_error stays null)
|
|
275
|
+
// AND a present-but-unreadable/malformed snapshot — collapsing those
|
|
276
|
+
// two into a bare `no_baseline: true` would manufacture "you've never
|
|
277
|
+
// run a refresh" out of a read failure. `read_error` names which one.
|
|
278
|
+
return { no_baseline: true, read_error: readErrors[0] ?? null };
|
|
213
279
|
}
|
|
214
280
|
const prevHashes = snapshot.hashes || {};
|
|
215
281
|
const changed = [];
|
|
@@ -374,7 +440,9 @@ function intelApiSurface(planningDir) {
|
|
|
374
440
|
const intelPath = ensureIntelDir(planningDir);
|
|
375
441
|
const apiMapPath = node_path_1.default.join(intelPath, INTEL_FILES.apis);
|
|
376
442
|
const outputPath = node_path_1.default.join(intelPath, 'API-SURFACE.md');
|
|
377
|
-
const
|
|
443
|
+
const readErrors = [];
|
|
444
|
+
const data = safeReadJson(apiMapPath, readErrors);
|
|
445
|
+
const readError = readErrors[0] ?? null;
|
|
378
446
|
const entries = (data && data.entries && typeof data.entries === 'object')
|
|
379
447
|
? Object.entries(data.entries)
|
|
380
448
|
: [];
|
|
@@ -392,7 +460,14 @@ function intelApiSurface(planningDir) {
|
|
|
392
460
|
lines.push('> Generated from `.planning/intel/api-map.json`. Do not edit by hand.');
|
|
393
461
|
lines.push('');
|
|
394
462
|
if (symbolCount === 0) {
|
|
395
|
-
|
|
463
|
+
if (readError) {
|
|
464
|
+
// #3885: a read/parse failure is NOT "not yet populated" — say so,
|
|
465
|
+
// rather than manufacturing the wrong reason for the empty surface.
|
|
466
|
+
lines.push(`> **Incomplete:** ${readError}`);
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
lines.push('> **Incomplete:** api-map.json has no entries (intel extraction is regex/JS-only or not yet populated).');
|
|
470
|
+
}
|
|
396
471
|
lines.push('> Treat absence here as "unknown", not "does not exist".');
|
|
397
472
|
lines.push('');
|
|
398
473
|
}
|
|
@@ -414,7 +489,7 @@ function intelApiSurface(planningDir) {
|
|
|
414
489
|
}
|
|
415
490
|
}
|
|
416
491
|
(0, shell_command_projection_cjs_1.platformWriteSync)(outputPath, lines.join('\n'));
|
|
417
|
-
return { written: outputPath, symbolCount, stale };
|
|
492
|
+
return { written: outputPath, symbolCount, stale, read_error: readError };
|
|
418
493
|
}
|
|
419
494
|
/**
|
|
420
495
|
* Patch _meta.updated_at in a JSON intel file to the current timestamp.
|