@opengsd/gsd-core 1.12.0 → 1.14.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-advisor-researcher.compact.md +85 -0
- package/agents/gsd-ai-researcher.compact.md +96 -0
- package/agents/gsd-assumptions-analyzer.compact.md +81 -0
- package/agents/gsd-code-fixer.compact.md +458 -0
- package/agents/gsd-code-fixer.md +5 -5
- package/agents/gsd-code-reviewer.compact.md +269 -0
- package/agents/gsd-code-reviewer.md +15 -3
- package/agents/gsd-codebase-mapper.compact.md +760 -0
- package/agents/gsd-debug-session-manager.compact.md +345 -0
- package/agents/gsd-doc-classifier.compact.md +192 -0
- package/agents/gsd-doc-synthesizer.compact.md +200 -0
- package/agents/gsd-doc-verifier.compact.md +143 -0
- package/agents/gsd-doc-writer.compact.md +440 -0
- package/agents/gsd-dom-verifier.compact.md +138 -0
- package/agents/gsd-domain-researcher.compact.md +141 -0
- package/agents/gsd-eval-auditor.compact.md +160 -0
- package/agents/gsd-eval-planner.compact.md +137 -0
- package/agents/gsd-executor.md +63 -35
- package/agents/gsd-framework-selector.compact.md +82 -0
- package/agents/gsd-integration-checker.compact.md +245 -0
- package/agents/gsd-intel-updater.compact.md +226 -0
- package/agents/gsd-mempalace-curator.compact.md +45 -0
- package/agents/gsd-nyquist-auditor.compact.md +179 -0
- package/agents/gsd-pattern-mapper.compact.md +275 -0
- package/agents/gsd-plan-checker.md +76 -57
- package/agents/gsd-planner.md +14 -0
- package/agents/gsd-project-researcher.compact.md +587 -0
- package/agents/gsd-research-synthesizer.compact.md +212 -0
- package/agents/gsd-roadmapper.compact.md +454 -0
- package/agents/gsd-roadmapper.md +13 -0
- package/agents/gsd-security-auditor.compact.md +162 -0
- package/agents/gsd-ui-auditor.compact.md +404 -0
- package/agents/gsd-ui-checker.compact.md +277 -0
- package/agents/gsd-ui-checker.md +19 -3
- package/agents/gsd-ui-researcher.compact.md +282 -0
- package/agents/gsd-ui-researcher.md +29 -0
- package/agents/gsd-user-profiler.compact.md +108 -0
- package/agents/gsd-verifier.md +23 -1
- package/bin/install.js +444 -134
- package/commands/gsd/cleanup.md +1 -0
- package/commands/gsd/code-review.md +2 -1
- package/commands/gsd/complete-milestone.md +1 -0
- package/commands/gsd/config.md +1 -0
- package/commands/gsd/debug.md +1 -0
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/graphify.md +1 -0
- package/commands/gsd/health.md +1 -0
- package/commands/gsd/mempalace-capture.md +1 -0
- package/commands/gsd/mempalace-recall.md +1 -0
- package/commands/gsd/new-milestone.md +1 -0
- package/commands/gsd/new-project.md +1 -0
- package/commands/gsd/next.md +1 -0
- package/commands/gsd/ns-workflow.md +2 -1
- package/commands/gsd/pause-work.md +1 -0
- package/commands/gsd/phase.md +2 -1
- package/commands/gsd/pr-branch.md +1 -0
- package/commands/gsd/quick-batch.md +105 -0
- package/commands/gsd/resume-work.md +1 -0
- package/commands/gsd/review-backlog.md +1 -0
- package/commands/gsd/settings.md +2 -1
- package/commands/gsd/stats.md +1 -0
- package/commands/gsd/surface.md +18 -8
- package/commands/gsd/thread.md +1 -0
- package/commands/gsd/workspace.md +1 -0
- package/commands/gsd/workstreams.md +1 -0
- package/gsd-core/bin/check-latest-version.cjs +8 -3
- package/gsd-core/bin/gsd-tools.cjs +532 -174
- package/gsd-core/bin/lib/adr-parser.cjs +1 -1
- package/gsd-core/bin/lib/artifacts.cjs +2 -1
- package/gsd-core/bin/lib/audit.cjs +39 -22
- package/gsd-core/bin/lib/broken-windows.cjs +168 -49
- package/gsd-core/bin/lib/capability-activation.cjs +27 -0
- package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
- package/gsd-core/bin/lib/capability-loader.cjs +135 -1
- package/gsd-core/bin/lib/capability-registry.cjs +528 -116
- package/gsd-core/bin/lib/capability-source.cjs +19 -2
- package/gsd-core/bin/lib/capability-state.cjs +7 -1
- package/gsd-core/bin/lib/capability-validator.cjs +134 -5
- package/gsd-core/bin/lib/capability-writer.cjs +14 -4
- package/gsd-core/bin/lib/check-command-router.cjs +198 -38
- package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
- package/gsd-core/bin/lib/clusters.cjs +1 -0
- package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
- package/gsd-core/bin/lib/command-aliases.cjs +16 -0
- package/gsd-core/bin/lib/commands.cjs +981 -79
- package/gsd-core/bin/lib/config-loader.cjs +4 -0
- package/gsd-core/bin/lib/config.cjs +153 -38
- package/gsd-core/bin/lib/core-utils.cjs +34 -7
- package/gsd-core/bin/lib/coverage.cjs +1 -1
- package/gsd-core/bin/lib/decisions.cjs +343 -28
- package/gsd-core/bin/lib/edge-probe.cjs +14 -1
- package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
- package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
- package/gsd-core/bin/lib/frontmatter.cjs +137 -23
- package/gsd-core/bin/lib/gap-checker.cjs +22 -13
- package/gsd-core/bin/lib/git-base-branch.cjs +10 -2
- package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
- 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 +54 -11
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +87 -23
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
- package/gsd-core/bin/lib/host-integration.cjs +57 -5
- package/gsd-core/bin/lib/init-command-router.cjs +14 -0
- package/gsd-core/bin/lib/init.cjs +539 -60
- package/gsd-core/bin/lib/install-engine.cjs +199 -14
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +45 -0
- package/gsd-core/bin/lib/install-profiles.cjs +36 -14
- package/gsd-core/bin/lib/installer-migration-report.cjs +1 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +33 -4
- package/gsd-core/bin/lib/io.cjs +35 -0
- package/gsd-core/bin/lib/loop-resolver.cjs +64 -39
- package/gsd-core/bin/lib/markdown-table.cjs +123 -0
- package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
- package/gsd-core/bin/lib/milestone.cjs +41 -10
- package/gsd-core/bin/lib/model-resolver.cjs +101 -10
- package/gsd-core/bin/lib/phase-command-router.cjs +20 -7
- package/gsd-core/bin/lib/phase-id.cjs +412 -31
- package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
- package/gsd-core/bin/lib/phase.cjs +941 -98
- package/gsd-core/bin/lib/plan-document.cjs +10 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +34 -18
- package/gsd-core/bin/lib/planning-snapshot.cjs +206 -30
- package/gsd-core/bin/lib/planning-workspace.cjs +153 -29
- package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
- 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/refactor-trigger-command-router.cjs +61 -2
- package/gsd-core/bin/lib/research-store.cjs +11 -12
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +53 -5
- package/gsd-core/bin/lib/review-lane-invocation.cjs +96 -1
- package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
- package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
- package/gsd-core/bin/lib/roadmap-parser.cjs +555 -41
- package/gsd-core/bin/lib/roadmap.cjs +292 -69
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +260 -43
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +28 -20
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +294 -108
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -47
- package/gsd-core/bin/lib/security.cjs +126 -7
- package/gsd-core/bin/lib/shell-command-projection.cjs +4 -0
- package/gsd-core/bin/lib/smart-entry.cjs +7 -9
- package/gsd-core/bin/lib/state-document.cjs +159 -32
- package/gsd-core/bin/lib/state-md-schema.cjs +44 -27
- package/gsd-core/bin/lib/state-transition.cjs +465 -62
- package/gsd-core/bin/lib/state.cjs +906 -151
- package/gsd-core/bin/lib/surface.cjs +83 -10
- package/gsd-core/bin/lib/task-command-router.cjs +12 -6
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
- package/gsd-core/bin/lib/uat.cjs +1420 -516
- package/gsd-core/bin/lib/update-context.cjs +36 -26
- package/gsd-core/bin/lib/validate.cjs +230 -12
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +316 -23
- package/gsd-core/bin/lib/verify-command-grounding.cjs +1 -1
- package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
- package/gsd-core/bin/lib/verify.cjs +531 -36
- package/gsd-core/bin/lib/workstream-inventory.cjs +21 -2
- package/gsd-core/bin/lib/worktree-safety.cjs +21 -7
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
- package/gsd-core/bin/verify-reapply-patches.cjs +507 -81
- package/gsd-core/references/agent-contracts.md +3 -3
- package/gsd-core/references/compact-content-gate.md +66 -0
- package/gsd-core/references/edge-probe.md +17 -13
- package/gsd-core/references/execute-mvp-tdd.md +18 -16
- package/gsd-core/references/execute-phase-response-language.md +6 -0
- package/gsd-core/references/executor-examples.md +42 -0
- package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
- package/gsd-core/references/loop-hook-dispatch.md +18 -0
- package/gsd-core/references/model-profiles.md +12 -3
- package/gsd-core/references/mvp-concepts.md +2 -2
- 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-quick-batch.md +71 -0
- package/gsd-core/references/planner-reviews.md +47 -0
- package/gsd-core/references/planner-revision.md +75 -2
- package/gsd-core/references/planning-config.md +5 -1
- package/gsd-core/references/response-language-directive.md +9 -0
- package/gsd-core/references/revision-loop.md +118 -11
- package/gsd-core/references/tdd.md +17 -9
- package/gsd-core/references/thinking-models-planning.md +18 -2
- package/gsd-core/references/verification-patterns.md +17 -4
- package/gsd-core/references/verifier-evidence-gate.md +160 -0
- package/gsd-core/references/worktree-path-safety.md +112 -2
- package/gsd-core/templates/README.md +7 -1
- package/gsd-core/templates/phase-prompt.md +4 -0
- package/gsd-core/templates/state.md +6 -3
- package/gsd-core/templates/summary.compact.md +212 -0
- package/gsd-core/templates/user-setup.compact.md +199 -0
- package/gsd-core/templates/user-setup.md +0 -9
- package/gsd-core/templates/verification-report.md +5 -0
- package/gsd-core/workflows/add-backlog.md +2 -0
- package/gsd-core/workflows/add-phase.md +2 -0
- package/gsd-core/workflows/add-tests.md +1 -1
- package/gsd-core/workflows/add-todo.md +4 -3
- package/gsd-core/workflows/ai-integration-phase.md +1 -1
- package/gsd-core/workflows/analyze-dependencies.md +2 -0
- package/gsd-core/workflows/audit-fix.md +2 -0
- package/gsd-core/workflows/audit-milestone.md +2 -0
- package/gsd-core/workflows/audit-uat.md +2 -0
- package/gsd-core/workflows/autonomous.md +15 -10
- package/gsd-core/workflows/check-todos.md +5 -3
- package/gsd-core/workflows/cleanup.md +4 -2
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +22 -13
- package/gsd-core/workflows/code-review-fix.md +5 -3
- package/gsd-core/workflows/code-review.md +211 -43
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
- package/gsd-core/workflows/complete-milestone.md +40 -254
- package/gsd-core/workflows/debug.md +1 -1
- package/gsd-core/workflows/diagnose-issues.md +5 -1
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -0
- 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 +2 -0
- 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 +2 -0
- 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.md +1 -1
- package/gsd-core/workflows/discuss-phase-power.md +2 -0
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +43 -13
- package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
- package/gsd-core/workflows/docs-update.md +15 -156
- package/gsd-core/workflows/edit-phase.md +2 -0
- package/gsd-core/workflows/eval-review.md +1 -1
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +20 -3
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +24 -3
- package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +8 -2
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
- package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
- package/gsd-core/workflows/execute-phase.md +78 -159
- package/gsd-core/workflows/execute-plan.md +28 -15
- package/gsd-core/workflows/explore.md +2 -0
- package/gsd-core/workflows/extract-learnings.md +2 -0
- package/gsd-core/workflows/fast.md +6 -0
- package/gsd-core/workflows/forensics.md +2 -0
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +1 -1
- 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.compact.md +398 -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 +3 -1
- package/gsd-core/workflows/import.md +3 -3
- package/gsd-core/workflows/inbox.md +1 -1
- package/gsd-core/workflows/ingest-docs.md +1 -1
- package/gsd-core/workflows/insert-phase.md +2 -0
- package/gsd-core/workflows/list-phase-assumptions.md +2 -0
- package/gsd-core/workflows/list-seeds.md +2 -0
- package/gsd-core/workflows/list-workspaces.md +2 -0
- package/gsd-core/workflows/manager.md +3 -3
- package/gsd-core/workflows/map-codebase.md +52 -3
- package/gsd-core/workflows/milestone-summary.md +2 -0
- package/gsd-core/workflows/mvp-phase.md +1 -1
- package/gsd-core/workflows/new-milestone.md +55 -13
- package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
- package/gsd-core/workflows/new-project.md +37 -205
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +2 -0
- 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 +19 -4
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -0
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +9 -0
- package/gsd-core/workflows/plan-phase.md +144 -185
- package/gsd-core/workflows/plan-review-convergence.md +102 -10
- package/gsd-core/workflows/plant-seed.md +1 -1
- package/gsd-core/workflows/pr-branch.md +30 -10
- package/gsd-core/workflows/profile-user.md +1 -1
- package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
- package/gsd-core/workflows/progress.md +25 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +37 -2
- package/gsd-core/workflows/quick/steps/research-phase.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 +21 -4
- package/gsd-core/workflows/reapply-patches.md +79 -3
- package/gsd-core/workflows/remove-phase.md +2 -0
- package/gsd-core/workflows/remove-workspace.md +1 -1
- package/gsd-core/workflows/resume-project.md +6 -2
- package/gsd-core/workflows/review.md +215 -10
- package/gsd-core/workflows/scan.md +2 -0
- package/gsd-core/workflows/section-manifest.json +12 -0
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/session-report.md +2 -0
- package/gsd-core/workflows/settings-advanced.md +2 -0
- package/gsd-core/workflows/settings-integrations.md +9 -8
- package/gsd-core/workflows/settings.md +19 -6
- package/gsd-core/workflows/ship.md +10 -10
- package/gsd-core/workflows/sketch-wrap-up.md +2 -0
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +1 -1
- package/gsd-core/workflows/spec-phase.md +24 -19
- package/gsd-core/workflows/spike-wrap-up.md +2 -0
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +2 -0
- package/gsd-core/workflows/sync-skills.md +12 -4
- package/gsd-core/workflows/thread.md +2 -0
- package/gsd-core/workflows/transition.md +2 -0
- package/gsd-core/workflows/ui-phase.md +26 -5
- package/gsd-core/workflows/ui-review.md +1 -1
- package/gsd-core/workflows/ultraplan-phase.md +2 -0
- package/gsd-core/workflows/undo.md +1 -1
- package/gsd-core/workflows/update.md +48 -43
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
- package/gsd-core/workflows/verify-work.md +68 -182
- package/hooks/dist/gsd-agent-isolation-guard.js +42 -16
- package/hooks/dist/gsd-check-update-worker.js +19 -2
- package/hooks/dist/gsd-context-monitor.js +371 -27
- package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
- package/hooks/dist/gsd-node-runner.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +30 -5
- package/hooks/dist/gsd-read-guard.js +2 -0
- package/hooks/dist/gsd-read-injection-scanner.js +5 -5
- package/hooks/dist/gsd-secret-read-guard.js +1105 -0
- package/hooks/dist/gsd-statusline.js +18 -10
- package/hooks/dist/gsd-validate-commit.sh +474 -7
- package/hooks/dist/gsd-workflow-guard.js +2 -1
- package/hooks/dist/gsd-worktree-path-guard.js +25 -14
- package/hooks/dist/gsd-write-guard.js +46 -1
- package/hooks/dist/lib/dispatch-identity.js +187 -0
- package/hooks/dist/lib/filename-classification.js +64 -0
- package/hooks/dist/lib/git-cmd.js +210 -1
- package/hooks/dist/lib/injection-patterns.js +36 -6
- package/hooks/dist/lib/isolation-deny-reason.js +53 -1
- package/hooks/dist/lib/isolation-sentinel.js +58 -19
- package/hooks/dist/managed-hooks-registry.cjs +1 -0
- package/hooks/gsd-agent-isolation-guard.js +42 -16
- package/hooks/gsd-check-update-worker.js +19 -2
- package/hooks/gsd-context-monitor.js +371 -27
- package/hooks/gsd-cursor-subagent-start.js +34 -14
- package/hooks/gsd-node-runner.sh +1 -0
- package/hooks/gsd-prompt-guard.js +30 -5
- package/hooks/gsd-read-guard.js +2 -0
- package/hooks/gsd-read-injection-scanner.js +5 -5
- package/hooks/gsd-secret-read-guard.js +1105 -0
- package/hooks/gsd-statusline.js +18 -10
- package/hooks/gsd-validate-commit.sh +474 -7
- package/hooks/gsd-workflow-guard.js +2 -1
- package/hooks/gsd-worktree-path-guard.js +25 -14
- package/hooks/gsd-write-guard.js +46 -1
- package/hooks/hooks.json +6 -0
- package/hooks/lib/dispatch-identity.js +187 -0
- package/hooks/lib/filename-classification.js +64 -0
- package/hooks/lib/git-cmd.js +210 -1
- package/hooks/lib/injection-patterns.js +36 -6
- package/hooks/lib/isolation-deny-reason.js +53 -1
- package/hooks/lib/isolation-sentinel.js +58 -19
- package/hooks/managed-hooks-registry.cjs +1 -0
- package/package.json +13 -9
- package/scripts/benchmark-compact-content-variants.cjs +298 -0
- package/scripts/benchmark-compact-content.cjs +368 -0
- package/scripts/build-hooks.js +11 -4
- package/scripts/check-contract-drift.cjs +4 -1
- package/scripts/check-env.cjs +36 -8
- package/scripts/check-glossary-refs.cjs +25 -21
- package/scripts/ci-next-health.cjs +271 -0
- package/scripts/ci-prepare-test-scope.cjs +7 -7
- package/scripts/ci-test-scope.cjs +133 -20
- package/scripts/ci-timeout-report.cjs +1 -1
- package/scripts/diff-touches-shipped-paths.cjs +1 -1
- package/scripts/docs-guard-registry.cjs +17 -2
- package/scripts/gen-adr-index.cjs +8 -2
- package/scripts/gen-inventory-manifest.cjs +12 -0
- package/scripts/gen-loop-host-contract.cjs +67 -15
- package/scripts/gen-platform-conformance-tier.cjs +557 -0
- package/scripts/lib/drift-scan.cjs +1 -1
- package/scripts/lib/macos-conformance-tier.generated.cjs +210 -0
- package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +276 -0
- package/scripts/lib/shellcheck-fetch.cjs +247 -0
- package/scripts/lib/suite-detection.cjs +32 -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-allowed-tools-parity.cjs +221 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +24 -2
- package/scripts/lint-phase-enumeration-drift.cjs +24 -6
- package/scripts/lint-phase-id-drift.cjs +465 -15
- package/scripts/lint-portable-grep.cjs +176 -0
- package/scripts/lint-response-language-coverage.cjs +530 -0
- package/scripts/lint-source-test-name-collision.cjs +1 -1
- package/scripts/lint-test-file-count.allowlist.json +4 -1
- package/scripts/lint-vendored-deps.cjs +128 -17
- package/scripts/lint-workflow-shellcheck-baseline.json +1112 -0
- package/scripts/lint-workflow-shellcheck.cjs +614 -0
- package/scripts/npm-audit-baseline.cjs +376 -0
- package/scripts/prompt-injection-scan.sh +22 -0
- package/scripts/require-issue-link-policy.cjs +16 -1
- package/scripts/workflow-size.cjs +139 -0
- package/skills/gsd-cleanup/SKILL.md +1 -0
- package/skills/gsd-code-review/SKILL.md +2 -1
- package/skills/gsd-complete-milestone/SKILL.md +1 -0
- package/skills/gsd-config/SKILL.md +1 -0
- package/skills/gsd-debug/SKILL.md +1 -0
- package/skills/gsd-execute-phase/SKILL.md +1 -1
- package/skills/gsd-graphify/SKILL.md +1 -0
- package/skills/gsd-health/SKILL.md +1 -0
- package/skills/gsd-mempalace-capture/SKILL.md +1 -0
- package/skills/gsd-mempalace-recall/SKILL.md +1 -0
- package/skills/gsd-new-milestone/SKILL.md +1 -0
- package/skills/gsd-new-project/SKILL.md +1 -0
- package/skills/gsd-next/SKILL.md +1 -0
- package/skills/gsd-ns-workflow/SKILL.md +1 -0
- package/skills/gsd-pause-work/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +2 -1
- package/skills/gsd-pr-branch/SKILL.md +1 -0
- package/skills/gsd-quick-batch/SKILL.md +105 -0
- package/skills/gsd-resume-work/SKILL.md +1 -0
- package/skills/gsd-review-backlog/SKILL.md +1 -0
- package/skills/gsd-settings/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +1 -0
- package/skills/gsd-surface/SKILL.md +18 -8
- package/skills/gsd-thread/SKILL.md +1 -0
- package/skills/gsd-workspace/SKILL.md +1 -0
- package/skills/gsd-workstreams/SKILL.md +1 -0
- package/vscode/package.json +1 -1
- package/gsd-core/templates/claude-md.md +0 -145
- package/gsd-core/templates/codebase/concerns.md +0 -310
- package/gsd-core/templates/codebase/conventions.md +0 -307
- package/gsd-core/templates/codebase/integrations.md +0 -280
- package/gsd-core/templates/codebase/structure.md +0 -285
- package/gsd-core/templates/codebase/testing.md +0 -480
- package/gsd-core/templates/debug-subagent-prompt.md +0 -91
- package/gsd-core/templates/discovery.md +0 -146
package/bin/install.js
CHANGED
|
@@ -183,10 +183,11 @@ function isCodexHooksFeatureKey(key) {
|
|
|
183
183
|
return CODEX_HOOKS_FEATURE_ALL_KEYS.includes(key);
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
// #768 \u2014 Claude Code permissions.allow
|
|
186
|
+
// #768 \u2014 Claude Code permissions.allow entries.
|
|
187
187
|
// Pre-populated during Claude installs to eliminate first-run approval friction
|
|
188
|
-
// for gsd-core's own known-safe tool calls
|
|
189
|
-
//
|
|
188
|
+
// for gsd-core's own known-safe tool calls. (The defense-in-depth deny entries
|
|
189
|
+
// for credential files that #768 also wrote are retired \u2014 see
|
|
190
|
+
// GSD_CLAUDE_LEGACY_DENY_PERMISSIONS below.)
|
|
190
191
|
//
|
|
191
192
|
// Format: each string uses Claude Code's documented permission rule syntax \u2014
|
|
192
193
|
// "Tool(pattern)" e.g. "Bash(npx gsd-core *)", "Read(.planning/*)"
|
|
@@ -204,7 +205,20 @@ const GSD_CLAUDE_ALLOW_PERMISSIONS = Object.freeze([
|
|
|
204
205
|
'Read(STATE.md)',
|
|
205
206
|
'Edit(STATE.md)',
|
|
206
207
|
]);
|
|
207
|
-
|
|
208
|
+
// #4221 \u2014 Retired deny rules. #768 wrote these three `Read()` deny rules
|
|
209
|
+
// into settings.json; Claude Code 2.1.259 hardened the Bash-side enforcement
|
|
210
|
+
// of Read() deny rules so that ANY such rule makes every
|
|
211
|
+
// `cd DIR && grep/cat relative-path` compound prompt for approval, even in
|
|
212
|
+
// `auto` permission mode \u2014 and GSD subagents emit hundreds of those per
|
|
213
|
+
// session. The same protection now ships as the managed PreToolUse hook
|
|
214
|
+
// hooks/gsd-secret-read-guard.js (a hook denial is not a permission rule and
|
|
215
|
+
// never arms that check). Unlike the #2278 allow-side migration, there is no
|
|
216
|
+
// surviving "current" deny list: the constant is RENAMED to its legacy role
|
|
217
|
+
// and only ever filtered, never added. Byte-equal strings only \u2014 a user's
|
|
218
|
+
// own hand-written identical rule is indistinguishable and is removed too
|
|
219
|
+
// (the install manifest never recorded permission strings, so a
|
|
220
|
+
// manifest-gated cleanup is not possible).
|
|
221
|
+
const GSD_CLAUDE_LEGACY_DENY_PERMISSIONS = Object.freeze([
|
|
208
222
|
'Read(.env)',
|
|
209
223
|
'Read(.env.*)',
|
|
210
224
|
'Read(.secrets)',
|
|
@@ -236,6 +250,13 @@ const GSD_CLAUDE_LEGACY_ALLOW_PERMISSIONS = Object.freeze([
|
|
|
236
250
|
* so existing installs end up with the working `Edit(...)` forms instead of
|
|
237
251
|
* both the dead legacy entry and its replacement sitting side by side.
|
|
238
252
|
*
|
|
253
|
+
* Migration (#4221): the retired GSD_CLAUDE_LEGACY_DENY_PERMISSIONS entries
|
|
254
|
+
* are removed from permissions.deny (byte-equal only). Nothing is added to
|
|
255
|
+
* deny any more: an absent `deny` key is left absent (never created as an
|
|
256
|
+
* empty array), and a `deny` array emptied BY THIS FILTER is deleted so the
|
|
257
|
+
* retirement leaves no `"deny": []` residue; a user's pre-existing empty
|
|
258
|
+
* `deny: []` is untouched.
|
|
259
|
+
*
|
|
239
260
|
* Defensive: if settings is not a plain object, returns immediately without
|
|
240
261
|
* throwing. If permissions.allow / permissions.deny exist but are not arrays
|
|
241
262
|
* (malformed settings), they are replaced with valid arrays.
|
|
@@ -252,7 +273,7 @@ function mergeClaudePermissions(settings) {
|
|
|
252
273
|
if (!Array.isArray(settings.permissions.allow)) {
|
|
253
274
|
settings.permissions.allow = [];
|
|
254
275
|
}
|
|
255
|
-
if (!Array.isArray(settings.permissions.deny)) {
|
|
276
|
+
if (settings.permissions.deny !== undefined && !Array.isArray(settings.permissions.deny)) {
|
|
256
277
|
settings.permissions.deny = [];
|
|
257
278
|
}
|
|
258
279
|
|
|
@@ -265,9 +286,13 @@ function mergeClaudePermissions(settings) {
|
|
|
265
286
|
settings.permissions.allow.push(entry);
|
|
266
287
|
}
|
|
267
288
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
289
|
+
if (Array.isArray(settings.permissions.deny)) {
|
|
290
|
+
const before = settings.permissions.deny.length;
|
|
291
|
+
settings.permissions.deny = settings.permissions.deny.filter(
|
|
292
|
+
(e) => !GSD_CLAUDE_LEGACY_DENY_PERMISSIONS.includes(e)
|
|
293
|
+
);
|
|
294
|
+
if (settings.permissions.deny.length === 0 && before > 0) {
|
|
295
|
+
delete settings.permissions.deny;
|
|
271
296
|
}
|
|
272
297
|
}
|
|
273
298
|
}
|
|
@@ -411,7 +436,7 @@ const GSD_CHANGESET_FILES = [
|
|
|
411
436
|
'github-release-notes.cjs', 'lint.cjs', 'new.cjs',
|
|
412
437
|
'README.md', // documentation only — not user-authored
|
|
413
438
|
];
|
|
414
|
-
const GSD_SCRIPTS_LIB_FILES = ['cli-exit.cjs', 'allowlist-ratchet.cjs', 'drift-scan.cjs', 'alias-drift-families.cjs', 'exit-code-registry.cjs', 'ndjson-reporter.cjs', 'ci-job-timing.cjs'];
|
|
439
|
+
const GSD_SCRIPTS_LIB_FILES = ['cli-exit.cjs', 'allowlist-ratchet.cjs', 'drift-scan.cjs', 'alias-drift-families.cjs', 'exit-code-registry.cjs', 'ndjson-reporter.cjs', 'ci-job-timing.cjs', 'shellcheck-fetch.cjs', 'npm-version-check-diagnosis.cjs', 'platform-conformance-tier.generated.cjs', 'suite-detection.cjs', 'macos-conformance-tier.generated.cjs'];
|
|
415
440
|
|
|
416
441
|
/**
|
|
417
442
|
* Resolve a runtime's shared-hooks directory name from its descriptor.
|
|
@@ -519,6 +544,13 @@ const {
|
|
|
519
544
|
RUNTIME_PROFILE_MAP: GSD_RUNTIME_PROFILE_MAP,
|
|
520
545
|
isAnthropicFlavoredModel: gsdIsAnthropicFlavoredModel,
|
|
521
546
|
} = require(path.join(_gsdLibDir, 'model-catalog.cjs'));
|
|
547
|
+
// #4145: shared hash-first recovery for gsd-pristine/ baselines stored at an
|
|
548
|
+
// unexpected path (e.g. without the gsd-core/ prefix an earlier release's
|
|
549
|
+
// writer dropped). Same module the reapply verifier uses, so the two readers
|
|
550
|
+
// cannot drift apart again.
|
|
551
|
+
const {
|
|
552
|
+
findPristineByHash: gsdFindPristineByHash,
|
|
553
|
+
} = require(path.join(_gsdLibDir, 'pristine-baseline.cjs'));
|
|
522
554
|
// #2875 Part 2: MODEL_PROFILES + resolveTierEntry are now consumed only by
|
|
523
555
|
// install-model-override-resolver.cjs's readGsdRuntimeProfileResolver
|
|
524
556
|
// (required below) — this installer no longer needs its own bindings.
|
|
@@ -790,6 +822,10 @@ const {
|
|
|
790
822
|
const {
|
|
791
823
|
resolveRuntimeArtifactLayout,
|
|
792
824
|
} = require(path.join(_gsdLibDir, 'runtime-artifact-layout.cjs'));
|
|
825
|
+
const {
|
|
826
|
+
readSurface,
|
|
827
|
+
resolveSurface,
|
|
828
|
+
} = require(path.join(_gsdLibDir, 'surface.cjs'));
|
|
793
829
|
const {
|
|
794
830
|
assertDestWithinConfigHome,
|
|
795
831
|
createRuntimeArtifactInstallPlan,
|
|
@@ -1256,6 +1292,7 @@ const removeKimiHooksToml = hooksSurface.removeKimiHooksToml;
|
|
|
1256
1292
|
// callers continue to work and there is a single implementation. (All call
|
|
1257
1293
|
// sites are below this line, so the const binding has no TDZ hazard.)
|
|
1258
1294
|
const processAttribution = runtimeArtifactConversion.processAttribution;
|
|
1295
|
+
const filterRuntimeNotesForTarget = runtimeArtifactConversion.filterRuntimeNotesForTarget;
|
|
1259
1296
|
// computePathPrefix: implementation lives in runtimeArtifactConversion
|
|
1260
1297
|
// (ADR-1508 / #1511 Phase 2 — single owner). Re-bound here so install.js call
|
|
1261
1298
|
// sites continue to work. #2876 retired the sibling
|
|
@@ -1333,34 +1370,13 @@ function ensureCodexHooksJsonSessionStart(targetDir, opts = {}) {
|
|
|
1333
1370
|
}
|
|
1334
1371
|
|
|
1335
1372
|
/**
|
|
1336
|
-
*
|
|
1337
|
-
*
|
|
1338
|
-
*
|
|
1339
|
-
*
|
|
1340
|
-
*
|
|
1341
|
-
*
|
|
1342
|
-
*
|
|
1343
|
-
*
|
|
1344
|
-
* All three events are routed through gsd-context-monitor.js — the same hook
|
|
1345
|
-
* used for PostToolUse in the Claude Code baseline — so context-headroom
|
|
1346
|
-
* warnings surface at these key Codex session lifecycle moments.
|
|
1347
|
-
*
|
|
1348
|
-
* On Windows (#3426): writes a gsd-context-monitor.cmd shim alongside the .js
|
|
1349
|
-
* file and uses the .cmd path as the hook command — exactly the same fix as
|
|
1350
|
-
* SessionStart uses for gsd-check-update — to avoid the bash.exe POSIX-exec
|
|
1351
|
-
* failure when Codex's hook dispatcher tries to run node.exe through Git Bash.
|
|
1352
|
-
*
|
|
1353
|
-
* @param {string} targetDir
|
|
1354
|
-
* @param {string} eventName - One of 'SubagentStart', 'Stop', 'PostToolUse'.
|
|
1355
|
-
* @param {{ absoluteRunner: string|null, platform?: NodeJS.Platform }} opts
|
|
1356
|
-
* @returns {{ changed: boolean, wrote: boolean, path: string }}
|
|
1357
|
-
*/
|
|
1358
|
-
function ensureCodexHooksJsonEvent(targetDir, eventName, opts = {}) {
|
|
1359
|
-
return hooksSurface.ensureCodexHooksJsonEvent(targetDir, eventName, opts);
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
/**
|
|
1363
|
-
* Remove a GSD-managed event entry from hooks.json. Called during uninstall.
|
|
1373
|
+
* Remove a GSD-managed event entry from hooks.json. Called during uninstall,
|
|
1374
|
+
* and (#2586) unconditionally during install/reinstall to clean up a
|
|
1375
|
+
* pre-#2586 install's stale gsd-context-monitor.js registrations — GSD no
|
|
1376
|
+
* longer ADDS entries for these events (see CODEX_HOOKS_TO_COPY /
|
|
1377
|
+
* cleanupOrphanedCodexContextMonitorScript in bin/install.js's Codex branch),
|
|
1378
|
+
* only removes recognized ones, so the `ensureCodexHooksJsonEvent` wrapper
|
|
1379
|
+
* that used to add them was removed as dead code.
|
|
1364
1380
|
*
|
|
1365
1381
|
* @param {string} targetDir
|
|
1366
1382
|
* @param {string} eventName
|
|
@@ -1796,8 +1812,20 @@ const kiloAgentPermissionOrder = [
|
|
|
1796
1812
|
'lsp',
|
|
1797
1813
|
];
|
|
1798
1814
|
|
|
1815
|
+
const kiloMcpPermissionPattern = /^mcp__([A-Za-z0-9_-]+)__((?:[A-Za-z0-9_-]+)|\*)$/;
|
|
1816
|
+
|
|
1817
|
+
// Derives Kilo's native `{server}_{tool}` MCP permission key (kilo.ai/docs —
|
|
1818
|
+
// external, fixed format). Not injective: both capture groups allow `_`, so
|
|
1819
|
+
// e.g. `mcp__a_b__c` and `mcp__a__b_c` derive the same key. The `Set` below
|
|
1820
|
+
// resolves any such collision deterministically to first-seen-wins — see
|
|
1821
|
+
// src/runtime-artifact-conversion.cts's regression test. Mirrors that file's
|
|
1822
|
+
// convertClaudeToKiloPermissionTool exactly (#4032).
|
|
1799
1823
|
function convertClaudeToKiloPermissionTool(claudeTool) {
|
|
1800
|
-
|
|
1824
|
+
const builtinPermission = claudeToKiloAgentPermissions[claudeTool];
|
|
1825
|
+
if (builtinPermission) return builtinPermission;
|
|
1826
|
+
|
|
1827
|
+
const mcpPermission = kiloMcpPermissionPattern.exec(claudeTool);
|
|
1828
|
+
return mcpPermission ? `${mcpPermission[1]}_${mcpPermission[2]}` : null;
|
|
1801
1829
|
}
|
|
1802
1830
|
|
|
1803
1831
|
function buildKiloAgentPermissionBlock(claudeTools) {
|
|
@@ -1814,6 +1842,10 @@ function buildKiloAgentPermissionBlock(claudeTools) {
|
|
|
1814
1842
|
for (const permission of kiloAgentPermissionOrder) {
|
|
1815
1843
|
lines.push(` ${permission}: ${allowedPermissions.has(permission) ? 'allow' : 'deny'}`);
|
|
1816
1844
|
}
|
|
1845
|
+
for (const permission of allowedPermissions) {
|
|
1846
|
+
if (kiloAgentPermissionOrder.includes(permission)) continue;
|
|
1847
|
+
lines.push(` ${permission}: allow`);
|
|
1848
|
+
}
|
|
1817
1849
|
|
|
1818
1850
|
return lines;
|
|
1819
1851
|
}
|
|
@@ -2384,7 +2416,7 @@ function convertClaudeAgentToCopilotAgent(content, isGlobal = false) {
|
|
|
2384
2416
|
* @param {boolean} [isGlobal=false] - Whether this is a global install
|
|
2385
2417
|
*/
|
|
2386
2418
|
function convertClaudeToAntigravityContent(content, isGlobal = false) {
|
|
2387
|
-
let c = content;
|
|
2419
|
+
let c = filterRuntimeNotesForTarget(content, 'antigravity');
|
|
2388
2420
|
if (isGlobal) {
|
|
2389
2421
|
// #3738: global skills install under ~/.gemini/config/skills (the dir AGY
|
|
2390
2422
|
// scans for global discovery), so skills-path references must divert there
|
|
@@ -2520,7 +2552,7 @@ function convertSlashCommandsToCursorSkillMentions(content) {
|
|
|
2520
2552
|
}
|
|
2521
2553
|
|
|
2522
2554
|
function convertClaudeToCursorMarkdown(content) {
|
|
2523
|
-
let converted = convertSlashCommandsToCursorSkillMentions(content);
|
|
2555
|
+
let converted = convertSlashCommandsToCursorSkillMentions(filterRuntimeNotesForTarget(content, 'cursor'));
|
|
2524
2556
|
// Replace tool name references in body text
|
|
2525
2557
|
converted = converted.replace(/\bBash\(/g, 'Shell(');
|
|
2526
2558
|
converted = converted.replace(/\bEdit\(/g, 'StrReplace(');
|
|
@@ -2641,7 +2673,7 @@ function convertSlashCommandsToTraeSkillMentions(content) {
|
|
|
2641
2673
|
}
|
|
2642
2674
|
|
|
2643
2675
|
function convertClaudeToTraeMarkdown(content) {
|
|
2644
|
-
let converted = convertSlashCommandsToTraeSkillMentions(content);
|
|
2676
|
+
let converted = convertSlashCommandsToTraeSkillMentions(filterRuntimeNotesForTarget(content, 'trae'));
|
|
2645
2677
|
converted = converted.replace(/\bBash\(/g, 'Shell(');
|
|
2646
2678
|
converted = converted.replace(/\bEdit\(/g, 'StrReplace(');
|
|
2647
2679
|
// Replace general-purpose subagent type with Trae's equivalent "general_purpose_task"
|
|
@@ -2767,7 +2799,7 @@ function convertSlashCommandsToCodebuddySkillMentions(content) {
|
|
|
2767
2799
|
}
|
|
2768
2800
|
|
|
2769
2801
|
function convertClaudeToCodebuddyMarkdown(content) {
|
|
2770
|
-
let converted = convertSlashCommandsToCodebuddySkillMentions(content);
|
|
2802
|
+
let converted = convertSlashCommandsToCodebuddySkillMentions(filterRuntimeNotesForTarget(content, 'codebuddy'));
|
|
2771
2803
|
// CodeBuddy uses the same tool names as Claude Code (Bash, Edit, Read, Write, etc.)
|
|
2772
2804
|
// No tool name conversion needed
|
|
2773
2805
|
converted = converted.replace(/\$ARGUMENTS\b/g, '{{GSD_ARGS}}');
|
|
@@ -2859,7 +2891,7 @@ function convertClaudeAgentToCodebuddyAgent(content) {
|
|
|
2859
2891
|
// ── Cline converters ────────────────────────────────────────────────────────
|
|
2860
2892
|
|
|
2861
2893
|
function convertClaudeToCliineMarkdown(content) {
|
|
2862
|
-
let converted = content;
|
|
2894
|
+
let converted = filterRuntimeNotesForTarget(content, 'cline');
|
|
2863
2895
|
// Cline uses the same tool names as Claude Code — no tool name conversion needed
|
|
2864
2896
|
converted = converted.replace(/`\.\/CLAUDE\.md`/g, '`.clinerules`');
|
|
2865
2897
|
converted = converted.replace(/\.\/CLAUDE\.md/g, '.clinerules');
|
|
@@ -3782,7 +3814,7 @@ function rewriteBareGsdToolsCommandsForCodex(content) {
|
|
|
3782
3814
|
}
|
|
3783
3815
|
|
|
3784
3816
|
function convertClaudeToCodexMarkdown(content) {
|
|
3785
|
-
let converted = convertSlashCommandsToCodexSkillMentions(content);
|
|
3817
|
+
let converted = convertSlashCommandsToCodexSkillMentions(filterRuntimeNotesForTarget(content, 'codex'));
|
|
3786
3818
|
converted = converted.replace(/\$ARGUMENTS\b/g, '{{GSD_ARGS}}');
|
|
3787
3819
|
// Remove /clear references — Codex has no equivalent command
|
|
3788
3820
|
// Handle backtick-wrapped: `\/clear` then: → (removed)
|
|
@@ -3849,26 +3881,37 @@ Execute mode fallback:
|
|
|
3849
3881
|
## C. Task() → spawn_agent Mapping
|
|
3850
3882
|
GSD workflows use \`Task(...)\` (Claude Code syntax). Translate to Codex collaboration tools:
|
|
3851
3883
|
|
|
3852
|
-
**Schema detection (required first step):**
|
|
3853
|
-
|
|
3854
|
-
|
|
3884
|
+
**Schema detection (required first step):** Before spawning, inspect the \`spawn_agent\`
|
|
3885
|
+
tool's visible parameter schema (via \`tool_search\` or the tool list). Use the presence
|
|
3886
|
+
of \`agent_type\` only to choose typed dispatch versus the generic-agent workaround.
|
|
3887
|
+
Detect optional fields independently: \`model\`, \`reasoning_effort\`, \`task_name\`,
|
|
3888
|
+
\`fork_turns\`, and \`fork_context\` may be added or removed without \`agent_type\` changing.
|
|
3889
|
+
Never infer one field from a schema/version label or from the presence of another field.
|
|
3855
3890
|
|
|
3856
|
-
|
|
3891
|
+
- **agent_type-capable schema:** \`spawn_agent\` advertises \`agent_type\` — typed GSD agent dispatch is available.
|
|
3892
|
+
- **Generic schema:** \`spawn_agent\` does not advertise \`agent_type\` — typed GSD agent dispatch is unavailable in this session, even if other optional fields are present.
|
|
3857
3893
|
|
|
3858
3894
|
Typed mapping (agent_type-capable schema only):
|
|
3859
3895
|
- \`Task(subagent_type="X", prompt="Y")\` → \`spawn_agent(agent_type="X", message="Y")\`
|
|
3860
3896
|
- \`Agent(subagent_type="X", prompt="Y")\` → \`spawn_agent(agent_type="X", message="Y")\`
|
|
3861
|
-
- \`Task(model="
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3897
|
+
- \`Task(model="{resolved_model}")\` → pass \`model="{resolved_model}"\` when the
|
|
3898
|
+
visible \`spawn_agent\` schema advertises \`model\` and the resolved value is explicit.
|
|
3899
|
+
This is how \`model_profile\` tier routing, including \`adaptive\`, reaches the child agent.
|
|
3900
|
+
Omit \`model\` only when the schema does not advertise \`model\`, or when the value is
|
|
3901
|
+
missing, empty, or \`"inherit"\`; omission deliberately inherits the session/static agent
|
|
3902
|
+
configuration. Explicit \`model_overrides\` may also be embedded in agent \`.toml\` files,
|
|
3903
|
+
but ordinary profile-resolved models are not, so a TOML file is not a reason to discard
|
|
3904
|
+
an available inline value.
|
|
3905
|
+
- Before each typed spawn, obtain the paired effort for its role with
|
|
3906
|
+
\`gsd_run query resolve-model <subagent_type> --pick effort\` when the workflow has not
|
|
3907
|
+
already exposed it. The resolver's unified \`effort\` field maps to the Codex spawn argument
|
|
3908
|
+
\`reasoning_effort\`; do not look for a resolver field named \`reasoning_effort\`.
|
|
3909
|
+
Pass it when the visible \`spawn_agent\` schema advertises \`reasoning_effort\`. Omit the
|
|
3910
|
+
field when it is not advertised, or when the value is missing, empty, \`"inherit"\`, or
|
|
3911
|
+
unsupported; do not invent one-off effort literals in workflow prose.
|
|
3869
3912
|
- \`fork_context: false\` by default — GSD agents load their own context via \`<required_reading>\` blocks
|
|
3870
|
-
- \`task_name\` —
|
|
3871
|
-
- \`fork_turns\` —
|
|
3913
|
+
- \`task_name\` — when advertised, provide a descriptive name for each spawned task
|
|
3914
|
+
- \`fork_turns\` — when advertised, controls turn-forking depth; coexists with \`fork_context\` (not a replacement)
|
|
3872
3915
|
- \`Task(isolation="worktree")\` / \`Agent(isolation="worktree")\` → no direct \`spawn_agent\` mapping,
|
|
3873
3916
|
but Codex declares \`dispatch.isolation: orchestrator-worktree\` (#2584). Codex
|
|
3874
3917
|
\`spawn_agent\` still does not create or bind a git worktree; instead GSD itself
|
|
@@ -7152,7 +7195,7 @@ function neutralizeAgentReferences(content, instructionFile) {
|
|
|
7152
7195
|
|
|
7153
7196
|
function convertClaudeToOpencodeFrontmatter(content, { isAgent = false, modelOverride = null } = {}) {
|
|
7154
7197
|
// Replace tool name references in content (applies to all files)
|
|
7155
|
-
let convertedContent = content;
|
|
7198
|
+
let convertedContent = filterRuntimeNotesForTarget(content, 'opencode');
|
|
7156
7199
|
convertedContent = convertedContent.replace(/\bAskUserQuestion\b/g, 'question');
|
|
7157
7200
|
convertedContent = convertedContent.replace(/\bSlashCommand\b/g, 'skill');
|
|
7158
7201
|
convertedContent = convertedContent.replace(/\bTodoWrite\b/g, 'todowrite');
|
|
@@ -7314,7 +7357,7 @@ function convertClaudeToOpencodeFrontmatter(content, { isAgent = false, modelOve
|
|
|
7314
7357
|
// tests/runtime-converters.test.cjs (#2093).
|
|
7315
7358
|
function convertClaudeToKiloFrontmatter(content, { isAgent = false, modelOverride = null } = {}) {
|
|
7316
7359
|
// Replace tool name references in content (applies to all files)
|
|
7317
|
-
let convertedContent = content;
|
|
7360
|
+
let convertedContent = filterRuntimeNotesForTarget(content, 'kilo');
|
|
7318
7361
|
convertedContent = convertedContent.replace(/\bAskUserQuestion\b/g, 'question');
|
|
7319
7362
|
convertedContent = convertedContent.replace(/\bSlashCommand\b/g, 'skill');
|
|
7320
7363
|
convertedContent = convertedContent.replace(/\bTodoWrite\b/g, 'todowrite');
|
|
@@ -7372,7 +7415,8 @@ function convertClaudeToKiloFrontmatter(content, { isAgent = false, modelOverrid
|
|
|
7372
7415
|
|
|
7373
7416
|
if (isAgent && inAgentTools) {
|
|
7374
7417
|
if (trimmed.startsWith('- ')) {
|
|
7375
|
-
|
|
7418
|
+
const tool = runtimeArtifactConversion._decodeToolScalar(trimmed.substring(2));
|
|
7419
|
+
if (tool !== null) agentTools.push(tool);
|
|
7376
7420
|
continue;
|
|
7377
7421
|
}
|
|
7378
7422
|
if (trimmed && !trimmed.startsWith('-')) {
|
|
@@ -7384,8 +7428,12 @@ function convertClaudeToKiloFrontmatter(content, { isAgent = false, modelOverrid
|
|
|
7384
7428
|
if (trimmed.startsWith('tools:')) {
|
|
7385
7429
|
if (isAgent) {
|
|
7386
7430
|
const toolsValue = trimmed.substring(6).trim();
|
|
7387
|
-
|
|
7388
|
-
|
|
7431
|
+
// A comment-only value (`tools: # note`) is not real inline content —
|
|
7432
|
+
// fall through to the block-list scan instead of decoding the
|
|
7433
|
+
// comment as a bogus tool name and dropping the list (mirrors
|
|
7434
|
+
// src/runtime-artifact-conversion.cts's convertClaudeToKiloFrontmatter, #4032).
|
|
7435
|
+
if (toolsValue && !toolsValue.startsWith('#')) {
|
|
7436
|
+
const tools = runtimeArtifactConversion._splitToolScalars(toolsValue).map(runtimeArtifactConversion._decodeToolScalar).filter(tool => tool !== null);
|
|
7389
7437
|
agentTools.push(...tools);
|
|
7390
7438
|
} else {
|
|
7391
7439
|
inAgentTools = true;
|
|
@@ -7451,7 +7499,8 @@ function convertClaudeToKiloFrontmatter(content, { isAgent = false, modelOverrid
|
|
|
7451
7499
|
if (trimmed.startsWith('- ')) {
|
|
7452
7500
|
const tool = trimmed.substring(2).trim();
|
|
7453
7501
|
if (isAgent) {
|
|
7454
|
-
|
|
7502
|
+
const decoded = runtimeArtifactConversion._decodeToolScalar(tool);
|
|
7503
|
+
if (decoded !== null) agentTools.push(decoded);
|
|
7455
7504
|
} else {
|
|
7456
7505
|
allowedTools.push(tool);
|
|
7457
7506
|
}
|
|
@@ -7862,6 +7911,8 @@ function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime, isCommand
|
|
|
7862
7911
|
content = composeWorkflow(content, { sourcePath: srcPath });
|
|
7863
7912
|
}
|
|
7864
7913
|
|
|
7914
|
+
content = filterRuntimeNotesForTarget(content, runtime);
|
|
7915
|
+
|
|
7865
7916
|
if (!dispatch.mdSkipGenericRewrite) {
|
|
7866
7917
|
const globalClaudeRegex = /~\/\.claude\//g;
|
|
7867
7918
|
const globalClaudeHomeRegex = /\$HOME\/\.claude\//g;
|
|
@@ -8416,6 +8467,16 @@ function uninstall(isGlobal, runtime = DEFAULT_RUNTIME) {
|
|
|
8416
8467
|
console.log(` ${green}✓${reset} Removed managed Codex ${eventName} hook from hooks.json`);
|
|
8417
8468
|
}
|
|
8418
8469
|
}
|
|
8470
|
+
// #2586: uninstall's own symmetric half of the orphaned-script cleanup —
|
|
8471
|
+
// same GSD-owned + unreferenced gate as the install-time call.
|
|
8472
|
+
const uninstallMonitorCleanup = hooksSurface.cleanupOrphanedCodexContextMonitorScript(targetDir);
|
|
8473
|
+
for (const deletedPath of uninstallMonitorCleanup.deleted) {
|
|
8474
|
+
removedCount++;
|
|
8475
|
+
console.log(` ${green}✓${reset} Removed orphaned Codex hook script (${path.basename(deletedPath)})`);
|
|
8476
|
+
}
|
|
8477
|
+
for (const warning of uninstallMonitorCleanup.warnings) {
|
|
8478
|
+
console.warn(` ${yellow}⚠${reset} Could not remove orphaned Codex hook script ${warning.path}: ${warning.reason}`);
|
|
8479
|
+
}
|
|
8419
8480
|
}
|
|
8420
8481
|
|
|
8421
8482
|
// 1a-kimi. Non-layout Kimi side-effect (#2095 EoS/kimi Upgrade 1): kimi's
|
|
@@ -9039,17 +9100,29 @@ function uninstall(isGlobal, runtime = DEFAULT_RUNTIME) {
|
|
|
9039
9100
|
);
|
|
9040
9101
|
if (settings.permissions.allow.length !== before) {
|
|
9041
9102
|
permissionsModified = true;
|
|
9103
|
+
// #4221: an array this filter emptied was GSD-only \u2014 remove the
|
|
9104
|
+
// key rather than leave an empty array behind (Antigravity symmetry).
|
|
9105
|
+
if (settings.permissions.allow.length === 0) {
|
|
9106
|
+
delete settings.permissions.allow;
|
|
9107
|
+
}
|
|
9042
9108
|
}
|
|
9043
9109
|
}
|
|
9044
9110
|
if (Array.isArray(settings.permissions.deny)) {
|
|
9045
9111
|
const before = settings.permissions.deny.length;
|
|
9112
|
+
// #4221: the deny rules are retired, so this is a legacy-only filter.
|
|
9046
9113
|
settings.permissions.deny = settings.permissions.deny.filter(
|
|
9047
|
-
(e) => !
|
|
9114
|
+
(e) => !GSD_CLAUDE_LEGACY_DENY_PERMISSIONS.includes(e)
|
|
9048
9115
|
);
|
|
9049
9116
|
if (settings.permissions.deny.length !== before) {
|
|
9050
9117
|
permissionsModified = true;
|
|
9118
|
+
if (settings.permissions.deny.length === 0) {
|
|
9119
|
+
delete settings.permissions.deny;
|
|
9120
|
+
}
|
|
9051
9121
|
}
|
|
9052
9122
|
}
|
|
9123
|
+
if (permissionsModified && Object.keys(settings.permissions).length === 0) {
|
|
9124
|
+
delete settings.permissions;
|
|
9125
|
+
}
|
|
9053
9126
|
if (permissionsModified) {
|
|
9054
9127
|
settingsModified = true;
|
|
9055
9128
|
console.log(` ${green}✓${reset} Removed GSD permissions from settings.json`);
|
|
@@ -10079,6 +10152,86 @@ function populatePristineDir({ packageSrc, pristineDir, modified, runtime, pathP
|
|
|
10079
10152
|
return written;
|
|
10080
10153
|
}
|
|
10081
10154
|
|
|
10155
|
+
/**
|
|
10156
|
+
* #4145: recover a pristine baseline from a hash-matching orphan stored at an
|
|
10157
|
+
* unexpected path under gsd-pristine/ (e.g. without the gsd-core/ prefix an
|
|
10158
|
+
* earlier release's writer dropped).
|
|
10159
|
+
*
|
|
10160
|
+
* The preserve-check's strict join (pristineDir + manifest-keyed relPath)
|
|
10161
|
+
* misses such snapshots, so they were pushed into regeneration from the
|
|
10162
|
+
* incoming release — and when the file changed upstream, the candidate's hash
|
|
10163
|
+
* could never satisfy the recorded outgoing hash, leaving the correct
|
|
10164
|
+
* baseline permanently unconsumed and unpruned (the self-perpetuating state
|
|
10165
|
+
* #4145 reports). Hash equality with pristine_hashes is the same authority
|
|
10166
|
+
* the #3657 drift guard trusts, so an exact match cannot be the wrong
|
|
10167
|
+
* baseline no matter where under gsd-pristine/ it lives.
|
|
10168
|
+
*
|
|
10169
|
+
* Recovery = relocation: copy the orphan to the canonical manifest-keyed path
|
|
10170
|
+
* (hash-verified after the copy) and remove the orphan only once the
|
|
10171
|
+
* canonical copy is verified in place. Returns true when the canonical path
|
|
10172
|
+
* ended up holding recorded-hash bytes. Never deletes anything it cannot
|
|
10173
|
+
* vouch for by hash, and never consumes a path that is the canonical path of
|
|
10174
|
+
* ANY manifest file (see canonicalSkip below) — only genuine orphans, which
|
|
10175
|
+
* no strict-join reader ever consults, are eligible for removal.
|
|
10176
|
+
*/
|
|
10177
|
+
function recoverOrphanedPristine(pristineDir, relPath, recordedHash, canonicalSkip) {
|
|
10178
|
+
if (!recordedHash) return false;
|
|
10179
|
+
let orphanRel;
|
|
10180
|
+
try {
|
|
10181
|
+
// canonicalSkip = the normalized manifest keys: a file already sitting at
|
|
10182
|
+
// any canonical path can never be (re-)adopted through the scan. Without
|
|
10183
|
+
// this, two modified files sharing byte-identical outgoing content would
|
|
10184
|
+
// repeatedly "rescue" each other's canonical away (relocate + delete at
|
|
10185
|
+
// its home path) in alternating updates — bytes identical, state unstable.
|
|
10186
|
+
// It also keeps drift (#3657) / stale (#3407) territory with the caller.
|
|
10187
|
+
orphanRel = gsdFindPristineByHash(pristineDir, recordedHash, canonicalSkip);
|
|
10188
|
+
} catch {
|
|
10189
|
+
return false;
|
|
10190
|
+
}
|
|
10191
|
+
if (!orphanRel) return false;
|
|
10192
|
+
const outRef = resolveInstallRelativePath(pristineDir, relPath);
|
|
10193
|
+
if (!outRef) return false;
|
|
10194
|
+
try {
|
|
10195
|
+
fs.mkdirSync(path.dirname(outRef.fullPath), { recursive: true });
|
|
10196
|
+
fs.copyFileSync(path.join(pristineDir, orphanRel), outRef.fullPath);
|
|
10197
|
+
// Verify the relocated copy before removing the orphan — only a
|
|
10198
|
+
// hash-matching canonical counts as recovered.
|
|
10199
|
+
if (fileHash(outRef.fullPath) !== recordedHash) {
|
|
10200
|
+
try { fs.rmSync(outRef.fullPath, { force: true }); } catch { /* best-effort */ }
|
|
10201
|
+
return false;
|
|
10202
|
+
}
|
|
10203
|
+
// Orphan removal is best-effort: the canonical copy is already verified,
|
|
10204
|
+
// so a failed unlink leaves a harmless duplicate, never data loss.
|
|
10205
|
+
try { fs.rmSync(path.join(pristineDir, orphanRel), { force: true }); } catch { /* best-effort */ }
|
|
10206
|
+
return true;
|
|
10207
|
+
} catch {
|
|
10208
|
+
return false;
|
|
10209
|
+
}
|
|
10210
|
+
}
|
|
10211
|
+
|
|
10212
|
+
/**
|
|
10213
|
+
* #4135: honest N-of-M accounting for gsd-pristine/ baselines after an
|
|
10214
|
+
* update. The #3407 promotion rule keeps only hash-validated candidates
|
|
10215
|
+
* (byte-identical across the version span), so a multi-version update
|
|
10216
|
+
* legitimately ends with near-zero baselines — the collapse itself is NOT a
|
|
10217
|
+
* bug to hide; hiding it is. This renders the covered-of-total line the
|
|
10218
|
+
* update output prints either way, so "1 of 13" can never present like a
|
|
10219
|
+
* fully-covered run. Pure function (typed return) so tests lock the exact
|
|
10220
|
+
* contract without matching console prose.
|
|
10221
|
+
*/
|
|
10222
|
+
function describeBaselineCoverage(totalModified, covered) {
|
|
10223
|
+
const total = Math.max(0, totalModified);
|
|
10224
|
+
const have = Math.min(Math.max(0, covered), total);
|
|
10225
|
+
const uncovered = total - have;
|
|
10226
|
+
return {
|
|
10227
|
+
complete: uncovered === 0,
|
|
10228
|
+
uncovered,
|
|
10229
|
+
text: uncovered === 0
|
|
10230
|
+
? `gsd-pristine/ baselines cover ${have} of ${total} modified file(s)`
|
|
10231
|
+
: `gsd-pristine/ baselines cover ${have} of ${total} modified file(s) — ${uncovered} will be reported no_baseline by the reapply verifier`,
|
|
10232
|
+
};
|
|
10233
|
+
}
|
|
10234
|
+
|
|
10082
10235
|
/**
|
|
10083
10236
|
* Detect user-modified GSD files by comparing against install manifest.
|
|
10084
10237
|
* Backs up modified files to gsd-local-patches/ for reapply after update.
|
|
@@ -10111,19 +10264,57 @@ function saveLocalPatches(configDir, pristineCtx) {
|
|
|
10111
10264
|
const modified = [];
|
|
10112
10265
|
const pristineHashes = {};
|
|
10113
10266
|
|
|
10267
|
+
// #4086: skills/ manifest keys may live OUTSIDE configDir at the runtime's
|
|
10268
|
+
// ACTUAL skills root — codex global installs to $HOME/.agents/skills (the
|
|
10269
|
+
// ADR-1239 skills-kind `home` override), which writeManifest() already
|
|
10270
|
+
// hashes from via _resolveSkillsRootDir (#2088/#3738). Resolving every key
|
|
10271
|
+
// against configDir alone made every skills/ key miss here, so user
|
|
10272
|
+
// modifications to Codex skills were never hash-compared, never backed up,
|
|
10273
|
+
// and were silently overwritten by the next update. configDir stays FIRST
|
|
10274
|
+
// (Postel: runtimes whose skills genuinely live under configDir resolve
|
|
10275
|
+
// byte-identically to before); the skills root is a fallback, only for keys
|
|
10276
|
+
// under the SAME descriptor-driven manifest prefix writeManifest uses, and
|
|
10277
|
+
// only when that root resolves outside configDir. Containment + symlink
|
|
10278
|
+
// guards apply to the alternate root too (resolveInstallRelativePath).
|
|
10279
|
+
const patchRuntime = (pristineCtx && pristineCtx.runtime) || manifest.runtime || null;
|
|
10280
|
+
const patchScope = manifest.scope === 'local' ? 'local' : 'global';
|
|
10281
|
+
let skillsRedirect = null;
|
|
10282
|
+
if (patchRuntime) {
|
|
10283
|
+
const skillsRoot = _resolveSkillsRootDir(patchRuntime, configDir, patchScope);
|
|
10284
|
+
const resolvedConfig = path.resolve(configDir);
|
|
10285
|
+
if (
|
|
10286
|
+
skillsRoot &&
|
|
10287
|
+
skillsRoot !== resolvedConfig &&
|
|
10288
|
+
!skillsRoot.startsWith(resolvedConfig + path.sep)
|
|
10289
|
+
) {
|
|
10290
|
+
const prefix = _hostBehaviors(patchRuntime).skillsManifestPrefix || 'skills/';
|
|
10291
|
+
skillsRedirect = { root: skillsRoot, prefix };
|
|
10292
|
+
}
|
|
10293
|
+
}
|
|
10294
|
+
|
|
10114
10295
|
for (const [relPath, originalHash] of Object.entries(manifest.files || {})) {
|
|
10115
10296
|
const safeRef = resolveInstallRelativePath(configDir, relPath);
|
|
10116
10297
|
if (!safeRef) continue;
|
|
10117
10298
|
const { relPath: safeRelPath, fullPath } = safeRef;
|
|
10118
|
-
|
|
10119
|
-
|
|
10299
|
+
let installedPath = fullPath;
|
|
10300
|
+
if (!fs.existsSync(installedPath) && skillsRedirect && safeRelPath.startsWith(skillsRedirect.prefix)) {
|
|
10301
|
+
const altRef = resolveInstallRelativePath(
|
|
10302
|
+
skillsRedirect.root,
|
|
10303
|
+
safeRelPath.slice(skillsRedirect.prefix.length)
|
|
10304
|
+
);
|
|
10305
|
+
if (altRef && fs.existsSync(altRef.fullPath)) {
|
|
10306
|
+
installedPath = altRef.fullPath;
|
|
10307
|
+
}
|
|
10308
|
+
}
|
|
10309
|
+
if (!fs.existsSync(installedPath)) continue;
|
|
10310
|
+
const currentHash = fileHash(installedPath);
|
|
10120
10311
|
if (currentHash !== originalHash) {
|
|
10121
10312
|
// Back up the user's modified version
|
|
10122
10313
|
const backupRef = resolveInstallRelativePath(patchesDir, safeRelPath);
|
|
10123
10314
|
if (!backupRef) continue;
|
|
10124
10315
|
const backupPath = backupRef.fullPath;
|
|
10125
10316
|
fs.mkdirSync(path.dirname(backupPath), { recursive: true });
|
|
10126
|
-
fs.copyFileSync(
|
|
10317
|
+
fs.copyFileSync(installedPath, backupPath);
|
|
10127
10318
|
modified.push(safeRelPath);
|
|
10128
10319
|
pristineHashes[safeRelPath] = originalHash;
|
|
10129
10320
|
}
|
|
@@ -10187,6 +10378,15 @@ function saveLocalPatches(configDir, pristineCtx) {
|
|
|
10187
10378
|
const stalePaths = new Set();
|
|
10188
10379
|
// Track which relPaths were successfully regenerated (from either missing or stale).
|
|
10189
10380
|
const regeneratedPaths = new Set();
|
|
10381
|
+
// #4145: track which relPaths were recovered by relocating a hash-matching
|
|
10382
|
+
// orphan (stored at an unexpected path, e.g. without the gsd-core/ prefix).
|
|
10383
|
+
const rescuedPaths = new Set();
|
|
10384
|
+
// #4145: the set of paths that are SOME file's canonical pristine path
|
|
10385
|
+
// (every normalized manifest key). The orphan scan must never consume
|
|
10386
|
+
// these — see recoverOrphanedPristine.
|
|
10387
|
+
const canonicalSkip = new Set(
|
|
10388
|
+
Object.keys(manifest.files || {}).map((k) => normalizeInstallRelativePath(k)).filter(Boolean),
|
|
10389
|
+
);
|
|
10190
10390
|
const missingPaths = [];
|
|
10191
10391
|
for (const relPath of modified) {
|
|
10192
10392
|
const outRef = resolveInstallRelativePath(pristineDir, relPath);
|
|
@@ -10208,6 +10408,17 @@ function saveLocalPatches(configDir, pristineCtx) {
|
|
|
10208
10408
|
stalePaths.add(relPath);
|
|
10209
10409
|
}
|
|
10210
10410
|
}
|
|
10411
|
+
// #4145: canonical absent (or just removed as stale) — before falling
|
|
10412
|
+
// into regeneration, try to recover the baseline from a hash-matching
|
|
10413
|
+
// orphan elsewhere under gsd-pristine/ and relocate it to the canonical
|
|
10414
|
+
// path. This is the self-heal for snapshots an earlier release stored
|
|
10415
|
+
// without the gsd-core/ prefix: without it the state repeats forever
|
|
10416
|
+
// (regeneration candidates from the incoming release can never satisfy
|
|
10417
|
+
// the recorded outgoing hash when upstream changed the file).
|
|
10418
|
+
if (recoverOrphanedPristine(pristineDir, relPath, pristineHashes[relPath], canonicalSkip)) {
|
|
10419
|
+
rescuedPaths.add(relPath);
|
|
10420
|
+
continue;
|
|
10421
|
+
}
|
|
10211
10422
|
// File absent from gsd-pristine/ (or just removed above as stale):
|
|
10212
10423
|
// attempt hash-validated regeneration from new-release source.
|
|
10213
10424
|
missingPaths.push(relPath);
|
|
@@ -10250,19 +10461,38 @@ function saveLocalPatches(configDir, pristineCtx) {
|
|
|
10250
10461
|
}
|
|
10251
10462
|
// `regenerated` = total files successfully regenerated (from missing OR stale).
|
|
10252
10463
|
const regenerated = regeneratedPaths.size;
|
|
10464
|
+
// `rescued` = files recovered by relocating a hash-matching orphan to its
|
|
10465
|
+
// canonical path (#4145) — distinct from preservation (canonical already
|
|
10466
|
+
// correct) and regeneration (bytes re-derived from new-release source).
|
|
10467
|
+
const rescued = rescuedPaths.size;
|
|
10253
10468
|
// `removed` = stale entries that were deleted and NOT subsequently regenerated.
|
|
10254
10469
|
// Entries that were stale-deleted but then successfully regenerated are counted
|
|
10255
|
-
// only in `regenerated
|
|
10256
|
-
|
|
10470
|
+
// only in `regenerated`; stale-deleted-then-orphan-rescued entries are counted
|
|
10471
|
+
// only in `rescued` — the counts are non-overlapping.
|
|
10472
|
+
const removed = [...stalePaths].filter(p => !regeneratedPaths.has(p) && !rescuedPaths.has(p)).length;
|
|
10257
10473
|
if (preserved > 0) {
|
|
10258
10474
|
console.log(' ' + green + '✓' + reset + ' Preserved ' + cyan + 'gsd-pristine/' + reset + ' (' + preserved + ' file(s)) for three-way merge');
|
|
10259
10475
|
}
|
|
10476
|
+
if (rescued > 0) {
|
|
10477
|
+
console.log(' ' + green + '✓' + reset + ' Recovered ' + cyan + 'gsd-pristine/' + reset + ' (' + rescued + ' file(s)) by recorded hash from a legacy-path snapshot and relocated them (#4145)');
|
|
10478
|
+
}
|
|
10260
10479
|
if (regenerated > 0) {
|
|
10261
10480
|
console.log(' ' + green + '✓' + reset + ' Regenerated ' + cyan + 'gsd-pristine/' + reset + ' (' + regenerated + ' file(s)) via hash-validated new-release source');
|
|
10262
10481
|
}
|
|
10263
10482
|
if (removed > 0) {
|
|
10264
10483
|
console.log(' ' + yellow + 'i' + reset + ' Removed ' + removed + ' stale gsd-pristine/ snapshot(s); regenerated ' + regenerated + ' of those — falls back to over-broad verify heuristic for the rest');
|
|
10265
10484
|
}
|
|
10485
|
+
// #4135: the honest N-of-M coverage line. Preserved/rescued/regenerated
|
|
10486
|
+
// are disjoint buckets (see their accounting comments above), so their
|
|
10487
|
+
// sum is exactly the files that ended this update with a hash-valid
|
|
10488
|
+
// baseline. A partial result renders as an info line, not an error:
|
|
10489
|
+
// the collapse is legitimate (#3407), hiding it was the bug.
|
|
10490
|
+
const coverage = describeBaselineCoverage(modified.length, preserved + rescued + regenerated);
|
|
10491
|
+
if (coverage.complete) {
|
|
10492
|
+
console.log(' ' + green + '✓' + reset + ' ' + coverage.text);
|
|
10493
|
+
} else {
|
|
10494
|
+
console.log(' ' + yellow + 'i' + reset + ' ' + coverage.text);
|
|
10495
|
+
}
|
|
10266
10496
|
}
|
|
10267
10497
|
}
|
|
10268
10498
|
return modified;
|
|
@@ -10512,7 +10742,7 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
|
|
|
10512
10742
|
// one) is honored on every profile, `full` included (#2322 blocker 2).
|
|
10513
10743
|
const _commandsDir = path.join(src, 'commands', 'gsd');
|
|
10514
10744
|
const _skillsManifest = _isCoreProfileAlias ? new Map() : loadSkillsManifest(_commandsDir);
|
|
10515
|
-
|
|
10745
|
+
let _resolvedProfile = resolveProfile({
|
|
10516
10746
|
modes: [_activeProfileName],
|
|
10517
10747
|
manifest: _skillsManifest,
|
|
10518
10748
|
registry: _installedCapabilityRegistry,
|
|
@@ -10863,6 +11093,28 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
|
|
|
10863
11093
|
return Array.isArray(scopeLayout) && scopeLayout.length > 0;
|
|
10864
11094
|
})();
|
|
10865
11095
|
|
|
11096
|
+
// Install the distribution-owned gsd-core tree before layout materialization.
|
|
11097
|
+
// installRuntimeArtifacts then provisions the durable Runtime Surface corpus
|
|
11098
|
+
// exactly once into this final tree instead of having that corpus overwritten
|
|
11099
|
+
// by a later whole-tree copy and needing a second provisioning pass.
|
|
11100
|
+
const skillSrc = path.join(src, 'gsd-core');
|
|
11101
|
+
const skillDest = path.join(targetDir, 'gsd-core');
|
|
11102
|
+
const _gsdArtifactsStagingRoot = _tryResolveUserArtifactStagingRoot(targetDir);
|
|
11103
|
+
if (_gsdArtifactsStagingRoot === null) {
|
|
11104
|
+
console.warn(` ${yellow}!${reset} Skipping gsd-core/${USER_OWNED_ARTIFACTS.join(', gsd-core/')} preservation (staging unavailable) — it will be lost if present.`);
|
|
11105
|
+
copyWithPathReplacement(skillSrc, skillDest, pathPrefix, runtime, false, isGlobal, targetDir);
|
|
11106
|
+
} else {
|
|
11107
|
+
const stagedGsdArtifacts = stageUserArtifacts(skillDest, USER_OWNED_ARTIFACTS, _gsdArtifactsStagingRoot);
|
|
11108
|
+
copyWithPathReplacement(skillSrc, skillDest, pathPrefix, runtime, false, isGlobal, targetDir);
|
|
11109
|
+
restoreStagedUserArtifacts(skillDest, stagedGsdArtifacts);
|
|
11110
|
+
discardStagedUserArtifacts(stagedGsdArtifacts);
|
|
11111
|
+
}
|
|
11112
|
+
if (verifyInstalled(skillDest, 'gsd-core')) {
|
|
11113
|
+
console.log(` ${green}✓${reset} Installed workflow assets`);
|
|
11114
|
+
} else {
|
|
11115
|
+
failures.push('gsd-core');
|
|
11116
|
+
}
|
|
11117
|
+
|
|
10866
11118
|
// #2624: write the .gsd-source marker. Extracted from its former late position so it can be
|
|
10867
11119
|
// called BEFORE staging reads the marker (see the call site below). Scoped to the Claude-global
|
|
10868
11120
|
// layout (issue #1477) — the only install path that ships the skills layout without a
|
|
@@ -10879,6 +11131,9 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
|
|
|
10879
11131
|
// ADR-1239 Phase B write-confinement: the descriptor-sourced marker filename
|
|
10880
11132
|
// must resolve under targetDir (parity with the other descriptor-driven writes).
|
|
10881
11133
|
const _markerPath = assertDestWithinConfigHome(targetDir, _hostBehaviors(runtime).sourceMarkerFile);
|
|
11134
|
+
if (hasExistingSymlinkBetween(path.resolve(targetDir), _markerPath, { allowOptInFollow: isSymlinkedDestOptIn() })) {
|
|
11135
|
+
throw new Error(`compatibility marker "${_markerPath}" contains an untrusted symlink`);
|
|
11136
|
+
}
|
|
10882
11137
|
fs.writeFileSync(_markerPath, gsdSourceCommands + '\n', 'utf8');
|
|
10883
11138
|
} catch (err) {
|
|
10884
11139
|
// Non-fatal: install proceeds. But on the Claude-global layout walk-up
|
|
@@ -10905,6 +11160,19 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
|
|
|
10905
11160
|
if (_isSkillsRuntime) {
|
|
10906
11161
|
// Layout-driven install for skills-based runtimes (full and minimal modes)
|
|
10907
11162
|
const scope = _installScopeId;
|
|
11163
|
+
// Preserve an existing Runtime Surface selection during upgrades. The
|
|
11164
|
+
// installer refreshes the source corpus first, then emits exactly the
|
|
11165
|
+
// already-committed selection instead of widening it to the base profile.
|
|
11166
|
+
const _committedSurface = readSurface(targetDir);
|
|
11167
|
+
if (_committedSurface) {
|
|
11168
|
+
_resolvedProfile = resolveSurface(
|
|
11169
|
+
targetDir,
|
|
11170
|
+
loadSkillsManifest(_commandsDir),
|
|
11171
|
+
undefined,
|
|
11172
|
+
_installedCapabilityRegistry,
|
|
11173
|
+
_committedSurface,
|
|
11174
|
+
);
|
|
11175
|
+
}
|
|
10908
11176
|
// ADR-1239 upgrade 3 / #2088: a kind may declare an alternate install `home`
|
|
10909
11177
|
// (e.g. Codex skills -> $HOME/.agents/skills) instead of the runtime's normal
|
|
10910
11178
|
// configDir. Resolve the ACTUAL on-disk skills root here, descriptor-driven
|
|
@@ -11197,36 +11465,6 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
|
|
|
11197
11465
|
_installNativePluginIfDeclared(runtime, targetDir, _hostBehaviors(runtime), src);
|
|
11198
11466
|
}
|
|
11199
11467
|
|
|
11200
|
-
// Copy gsd-core skill with path replacement
|
|
11201
|
-
// Stage user-generated files DURABLY to disk before the wipe-and-copy so
|
|
11202
|
-
// they survive re-install even if the process dies mid-copy (#2875 /
|
|
11203
|
-
// #1874-F19) — copyWithPathReplacement wipes and recursively re-copies the
|
|
11204
|
-
// entire gsd-core/ tree, the single longest operation in the install, on
|
|
11205
|
-
// the path every user takes (40-design.md "Site 4 is far worse...").
|
|
11206
|
-
const skillSrc = path.join(src, 'gsd-core');
|
|
11207
|
-
const skillDest = path.join(targetDir, 'gsd-core');
|
|
11208
|
-
// #2875 defect fix: this IS the mainline install step (installing
|
|
11209
|
-
// gsd-core/ itself) — unlike the optional legacy-cleanup blocks above,
|
|
11210
|
-
// install must still be able to proceed and actually write gsd-core/ even
|
|
11211
|
-
// when the staging root cannot be resolved. Degrade by skipping ONLY the
|
|
11212
|
-
// USER_OWNED_ARTIFACTS preserve/restore wrapper around the copy (warn),
|
|
11213
|
-
// never the copy itself.
|
|
11214
|
-
const _gsdArtifactsStagingRoot = _tryResolveUserArtifactStagingRoot(targetDir);
|
|
11215
|
-
if (_gsdArtifactsStagingRoot === null) {
|
|
11216
|
-
console.warn(` ${yellow}!${reset} Skipping gsd-core/${USER_OWNED_ARTIFACTS.join(', gsd-core/')} preservation (staging unavailable) — it will be lost if present.`);
|
|
11217
|
-
copyWithPathReplacement(skillSrc, skillDest, pathPrefix, runtime, false, isGlobal, targetDir);
|
|
11218
|
-
} else {
|
|
11219
|
-
const stagedGsdArtifacts = stageUserArtifacts(skillDest, USER_OWNED_ARTIFACTS, _gsdArtifactsStagingRoot);
|
|
11220
|
-
copyWithPathReplacement(skillSrc, skillDest, pathPrefix, runtime, false, isGlobal, targetDir);
|
|
11221
|
-
restoreStagedUserArtifacts(skillDest, stagedGsdArtifacts);
|
|
11222
|
-
discardStagedUserArtifacts(stagedGsdArtifacts);
|
|
11223
|
-
}
|
|
11224
|
-
if (verifyInstalled(skillDest, 'gsd-core')) {
|
|
11225
|
-
console.log(` ${green}✓${reset} Installed workflow assets`);
|
|
11226
|
-
} else {
|
|
11227
|
-
failures.push('gsd-core');
|
|
11228
|
-
}
|
|
11229
|
-
|
|
11230
11468
|
// #2624: the .gsd-source marker is now written by _writeGsdSourceMarker()
|
|
11231
11469
|
// BEFORE staging reads it (see the early call above the _isSkillsRuntime
|
|
11232
11470
|
// block). The former write lived here — AFTER staging — which on an upgrade
|
|
@@ -11314,7 +11552,8 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
|
|
|
11314
11552
|
} else if (_isSkillsRuntime) {
|
|
11315
11553
|
console.log(` ${dim}↳${reset} Agents installed via descriptor-driven layout (${runtime})`);
|
|
11316
11554
|
} else {
|
|
11317
|
-
const
|
|
11555
|
+
const _standaloneProjectDir = isGlobal ? process.cwd() : targetDir;
|
|
11556
|
+
const _standaloneAgentsResult = installAgentsKindStandalone(runtime, targetDir, _installScopeId, _resolvedProfile, pathPrefix, getCommitAttribution, _installedCapabilityRegistry, _standaloneProjectDir);
|
|
11318
11557
|
if (_standaloneAgentsResult) {
|
|
11319
11558
|
// #2875 defect fix: installAgentsKindStandalone now returns `null`
|
|
11320
11559
|
// (rather than a truthy result pointing at an empty destDir) whenever a
|
|
@@ -12064,13 +12303,29 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
|
|
|
12064
12303
|
// require()s managed-hooks-registry.cjs for MANAGED_HOOKS — so all four must be
|
|
12065
12304
|
// installed/refreshed together for every profile, or Codex is wired to a dependency
|
|
12066
12305
|
// chain the same installer never delivers.
|
|
12067
|
-
// We deliberately do *not* copy gsd-graphify-update.sh
|
|
12068
|
-
//
|
|
12306
|
+
// We deliberately do *not* copy gsd-graphify-update.sh for Codex in this
|
|
12307
|
+
// change (graphify auto-update support for Codex is out of scope for #3579).
|
|
12308
|
+
// hooks/lib/ WAS excluded here for the same reason, and that stopped being
|
|
12309
|
+
// correct when #3911 (2ea5efc15) gave gsd-context-monitor.js a real
|
|
12310
|
+
// `require('./lib/hook-exit.js')`: the allowlist below is flat and never
|
|
12311
|
+
// recursed, so the hook shipped without its helper and died with
|
|
12312
|
+
// MODULE_NOT_FOUND at load, before its own try/catch, on every registered
|
|
12313
|
+
// event (#4087, #4098). The libs are now derived from what the staged
|
|
12314
|
+
// scripts actually require rather than hand-listed — see the
|
|
12315
|
+
// stageTransitiveHookLibs call after the copy loop. The #3579 boundary is
|
|
12316
|
+
// preserved: helpers no staged Codex hook requires (graphify tooling among
|
|
12317
|
+
// them) are still not shipped.
|
|
12318
|
+
// #2586: gsd-context-monitor.js is deliberately NOT copied for Codex.
|
|
12319
|
+
// It reads the statusline bridge file (${TMPDIR}/claude-ctx-{session_id}.json)
|
|
12320
|
+
// written only by hooks/gsd-statusline.js, which Codex never installs — so
|
|
12321
|
+
// every registered event was a guaranteed silent no-op (readSentinel throws
|
|
12322
|
+
// ENOENT -> allow(undefined), every invocation, every event, no exceptions).
|
|
12323
|
+
// A pre-#2586 install's stale copy + hooks.json registrations are cleaned
|
|
12324
|
+
// up below (see the CODEX_EXTENDED_HOOK_EVENTS loop), not re-added here.
|
|
12069
12325
|
const CODEX_HOOKS_TO_COPY = [
|
|
12070
12326
|
'gsd-check-update.js',
|
|
12071
12327
|
'gsd-check-update-worker.js',
|
|
12072
12328
|
'managed-hooks-registry.cjs',
|
|
12073
|
-
'gsd-context-monitor.js',
|
|
12074
12329
|
];
|
|
12075
12330
|
const codexHooksSrc = path.join(src, 'hooks', 'dist');
|
|
12076
12331
|
if (fs.existsSync(codexHooksSrc)) {
|
|
@@ -12081,6 +12336,12 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
|
|
|
12081
12336
|
// is not the same as an allowlisted file landing in it — see the marker
|
|
12082
12337
|
// gate below.
|
|
12083
12338
|
let codexStagedHooks = false;
|
|
12339
|
+
// The entries THIS invocation actually staged. Seeding the lib scan from
|
|
12340
|
+
// `existsSync` over the destination instead would also pick up a file
|
|
12341
|
+
// left by a PREVIOUS install whose source is no longer staged — e.g. a
|
|
12342
|
+
// name dropped from the allowlist — and derive helpers for a hook that is
|
|
12343
|
+
// no longer shipped (review of #4087).
|
|
12344
|
+
const codexStagedEntries = [];
|
|
12084
12345
|
for (const entry of fs.readdirSync(codexHooksSrc)) {
|
|
12085
12346
|
if (!CODEX_HOOKS_TO_COPY.includes(entry)) continue;
|
|
12086
12347
|
const srcFile = path.join(codexHooksSrc, entry);
|
|
@@ -12115,8 +12376,43 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
|
|
|
12115
12376
|
try { fs.chmodSync(destFile, 0o755); } catch (e) { /* Windows */ }
|
|
12116
12377
|
}
|
|
12117
12378
|
codexStagedHooks = true;
|
|
12379
|
+
codexStagedEntries.push(entry);
|
|
12380
|
+
}
|
|
12381
|
+
// Stage the hooks/lib/ helpers the staged scripts require, transitively
|
|
12382
|
+
// (#4087, #4098). Shares writeCursorHooksJson's walker rather than a
|
|
12383
|
+
// second copy: both reduced bundles hand-pick SCRIPTS, and the identical
|
|
12384
|
+
// MODULE_NOT_FOUND was already fixed once for Cursor in 704859e9c. A flat
|
|
12385
|
+
// list of today's three helpers would re-break the next time a
|
|
12386
|
+
// Codex-bundled hook grows a lib dependency, which is exactly how this
|
|
12387
|
+
// regressed. Gated on codexStagedHooks for the same reason the CommonJS
|
|
12388
|
+
// marker below is: hooks/ is shared space, and staging nothing must not
|
|
12389
|
+
// leave a GSD-owned lib/ behind in a directory GSD created but did not
|
|
12390
|
+
// fill (#2544). Seeded from the copies staged by THIS invocation, so the
|
|
12391
|
+
// scan sees the same bytes Node will load and never derives helpers for a
|
|
12392
|
+
// hook left behind by an earlier install.
|
|
12393
|
+
let codexStagedLibs = [];
|
|
12394
|
+
if (codexStagedHooks) {
|
|
12395
|
+
codexStagedLibs = hooksSurface.stageTransitiveHookLibs({
|
|
12396
|
+
seedSources: codexStagedEntries
|
|
12397
|
+
.map((entry) => fs.readFileSync(path.join(codexHooksDest, entry), 'utf8')),
|
|
12398
|
+
srcLibDir: path.join(codexHooksSrc, 'lib'),
|
|
12399
|
+
destLibDir: path.join(codexHooksDest, 'lib'),
|
|
12400
|
+
runtimeLabel: 'Codex',
|
|
12401
|
+
// Same substitutions the .js branch above applies to hook scripts, so
|
|
12402
|
+
// a helper that ever gains a runtime path or version token is
|
|
12403
|
+
// rewritten identically instead of shipping a Claude-shaped path.
|
|
12404
|
+
// No-ops on today's helpers, which carry neither.
|
|
12405
|
+
transform: (content) => content
|
|
12406
|
+
.replace(/'\.claude'/g, configDirReplacement)
|
|
12407
|
+
.replace(/\/\.claude\//g, `/${getDirName(runtime)}/`)
|
|
12408
|
+
.replace(/\.claude\//g, `${getDirName(runtime)}/`)
|
|
12409
|
+
.replace(/\{\{GSD_VERSION\}\}/g, pkg.version),
|
|
12410
|
+
});
|
|
12118
12411
|
}
|
|
12119
12412
|
console.log(` ${green}✓${reset} Installed hooks (Codex)`);
|
|
12413
|
+
if (codexStagedLibs.length > 0) {
|
|
12414
|
+
console.log(` ${green}✓${reset} Installed hooks/lib/ helpers (${codexStagedLibs.join(', ')})`);
|
|
12415
|
+
}
|
|
12120
12416
|
// #2717: write the CommonJS marker into hooks/ alongside the staged .js
|
|
12121
12417
|
// scripts. Codex is excluded from installSharedHooksBundle by the
|
|
12122
12418
|
// !isCodex gate, so it never received the marker the shared-bundle path
|
|
@@ -12231,35 +12527,48 @@ function install(isGlobal, runtime = DEFAULT_RUNTIME, options = {}) {
|
|
|
12231
12527
|
}
|
|
12232
12528
|
}
|
|
12233
12529
|
|
|
12234
|
-
//
|
|
12235
|
-
//
|
|
12236
|
-
//
|
|
12237
|
-
//
|
|
12238
|
-
//
|
|
12239
|
-
//
|
|
12240
|
-
|
|
12241
|
-
|
|
12242
|
-
//
|
|
12243
|
-
// context-monitor
|
|
12244
|
-
//
|
|
12245
|
-
//
|
|
12246
|
-
//
|
|
12247
|
-
//
|
|
12248
|
-
|
|
12249
|
-
|
|
12250
|
-
|
|
12251
|
-
|
|
12252
|
-
|
|
12253
|
-
|
|
12254
|
-
|
|
12255
|
-
|
|
12256
|
-
|
|
12257
|
-
|
|
12258
|
-
|
|
12259
|
-
}
|
|
12530
|
+
// #2586: Codex's hook payload carries no context/token-usage field
|
|
12531
|
+
// (confirmed against codex-rs/hooks/src/schema.rs), so agent-facing
|
|
12532
|
+
// context warnings and GSD phase/lifecycle display cannot be
|
|
12533
|
+
// supported on this runtime — state that plainly during install
|
|
12534
|
+
// rather than silently omitting the capability. Matches
|
|
12535
|
+
// capabilities/codex/capability.json's hostBehaviors.unsupportedFeatures.
|
|
12536
|
+
console.log(` ${dim}↳${reset} Codex: agent-facing context warnings and GSD phase/lifecycle display are unsupported (Codex's hook payload has no context-usage metric)`);
|
|
12537
|
+
|
|
12538
|
+
// ── Codex extended hook events (#772, #2088) — REMOVED by #2586 ──────
|
|
12539
|
+
// gsd-context-monitor.js is no longer copied or registered for Codex
|
|
12540
|
+
// (see the CODEX_HOOKS_TO_COPY comment above): every one of these
|
|
12541
|
+
// events was a guaranteed silent no-op, since the metrics bridge file
|
|
12542
|
+
// it reads is only ever written by Claude's own statusline hook.
|
|
12543
|
+
// Every event in CODEX_EXTENDED_HOOK_EVENTS is unconditionally
|
|
12544
|
+
// reconciled here — not gated on the script existing — so a
|
|
12545
|
+
// pre-#2586 install's stale registrations (exact current shape, or a
|
|
12546
|
+
// recognized legacy shape via isManagedHookCommand's
|
|
12547
|
+
// includeLegacyAliases) are stripped on reinstall. Mirrors the
|
|
12548
|
+
// unconditional uninstall-time loop over the same constant. A
|
|
12549
|
+
// registration whose command does not match the managed shape (a
|
|
12550
|
+
// hand-customized entry) survives untouched — see
|
|
12551
|
+
// reconcileCodexHooksJsonEvent's isManagedHookCommand filter.
|
|
12552
|
+
for (const codexEvent of CODEX_EXTENDED_HOOK_EVENTS) {
|
|
12553
|
+
const eventCleanup = removeCodexHooksJsonEvent(targetDir, codexEvent);
|
|
12554
|
+
if (eventCleanup.changed) {
|
|
12555
|
+
console.log(` ${green}✓${reset} Removed stale Codex ${codexEvent} context-monitor hook from hooks.json`);
|
|
12260
12556
|
}
|
|
12261
|
-
}
|
|
12262
|
-
|
|
12557
|
+
}
|
|
12558
|
+
// Delete the orphaned script (+ Windows .cmd shim) left by a
|
|
12559
|
+
// pre-#2586 install, but ONLY once no surviving hooks.json
|
|
12560
|
+
// registration under any event still references it, and only when
|
|
12561
|
+
// the on-disk file is GSD's own (see design doc's Ownership check —
|
|
12562
|
+
// a content-signature check, not manifest membership, so this works
|
|
12563
|
+
// on the very first reinstall after upgrading, with no bootstrap
|
|
12564
|
+
// gap). A deletion failure never reverts the (already safe,
|
|
12565
|
+
// already-written) hooks.json cleanup above — must-have #8.
|
|
12566
|
+
const monitorCleanup = hooksSurface.cleanupOrphanedCodexContextMonitorScript(targetDir);
|
|
12567
|
+
for (const deletedPath of monitorCleanup.deleted) {
|
|
12568
|
+
console.log(` ${green}✓${reset} Removed orphaned Codex hook script (${path.basename(deletedPath)})`);
|
|
12569
|
+
}
|
|
12570
|
+
for (const warning of monitorCleanup.warnings) {
|
|
12571
|
+
console.warn(` ${yellow}⚠${reset} Could not remove orphaned Codex hook script ${warning.path}: ${warning.reason}`);
|
|
12263
12572
|
}
|
|
12264
12573
|
// ── end Codex extended hook events ────────────────────────────────────
|
|
12265
12574
|
}
|
|
@@ -13881,7 +14190,7 @@ module.exports = {
|
|
|
13881
14190
|
mergeClaudePermissions,
|
|
13882
14191
|
GSD_CLAUDE_ALLOW_PERMISSIONS,
|
|
13883
14192
|
GSD_CLAUDE_LEGACY_ALLOW_PERMISSIONS,
|
|
13884
|
-
|
|
14193
|
+
GSD_CLAUDE_LEGACY_DENY_PERMISSIONS,
|
|
13885
14194
|
GSD_CODEX_MARKER,
|
|
13886
14195
|
// #3897 rung 3 (ADR-3473 §8.3, HALT.md option 2)
|
|
13887
14196
|
CODEX_SANDBOX_HOLDS,
|
|
@@ -13945,6 +14254,7 @@ module.exports = {
|
|
|
13945
14254
|
reportLocalPatches,
|
|
13946
14255
|
validateHookFields,
|
|
13947
14256
|
populatePristineDir,
|
|
14257
|
+
describeBaselineCoverage,
|
|
13948
14258
|
_resolveUserArtifactStagingRoot,
|
|
13949
14259
|
_tryResolveUserArtifactStagingRoot,
|
|
13950
14260
|
finishInstall,
|