@therocketcode/gsd-core 1.4.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/plugin.json +23 -0
- package/GEMINI.md +53 -0
- package/LICENSE +21 -0
- package/README.ja-JP.md +125 -0
- package/README.ko-KR.md +125 -0
- package/README.md +144 -0
- package/README.pt-BR.md +125 -0
- package/README.zh-CN.md +125 -0
- package/agents/gsd-advisor-researcher.md +108 -0
- package/agents/gsd-ai-researcher.md +114 -0
- package/agents/gsd-assumptions-analyzer.md +105 -0
- package/agents/gsd-code-fixer.md +668 -0
- package/agents/gsd-code-reviewer.md +387 -0
- package/agents/gsd-codebase-mapper.md +853 -0
- package/agents/gsd-debug-session-manager.md +314 -0
- package/agents/gsd-debugger.md +1452 -0
- package/agents/gsd-doc-classifier.md +168 -0
- package/agents/gsd-doc-synthesizer.md +204 -0
- package/agents/gsd-doc-verifier.md +217 -0
- package/agents/gsd-doc-writer.md +616 -0
- package/agents/gsd-domain-researcher.md +147 -0
- package/agents/gsd-eval-auditor.md +191 -0
- package/agents/gsd-eval-planner.md +154 -0
- package/agents/gsd-executor.md +785 -0
- package/agents/gsd-framework-selector.md +160 -0
- package/agents/gsd-integration-checker.md +470 -0
- package/agents/gsd-intel-updater.md +342 -0
- package/agents/gsd-nyquist-auditor.md +203 -0
- package/agents/gsd-pattern-mapper.md +335 -0
- package/agents/gsd-phase-researcher.md +867 -0
- package/agents/gsd-plan-checker.md +978 -0
- package/agents/gsd-planner.md +1204 -0
- package/agents/gsd-project-researcher.md +611 -0
- package/agents/gsd-research-synthesizer.md +259 -0
- package/agents/gsd-roadmapper.md +688 -0
- package/agents/gsd-security-auditor.md +155 -0
- package/agents/gsd-ui-auditor.md +495 -0
- package/agents/gsd-ui-checker.md +309 -0
- package/agents/gsd-ui-researcher.md +374 -0
- package/agents/gsd-user-profiler.md +171 -0
- package/agents/gsd-verifier.md +923 -0
- package/assets/gsd-logo-2000-transparent.png +0 -0
- package/assets/gsd-logo-2000-transparent.svg +17 -0
- package/assets/gsd-logo-2000.png +0 -0
- package/assets/gsd-logo-2000.svg +21 -0
- package/assets/terminal.svg +68 -0
- package/bin/install.js +12726 -0
- package/bin/lib/ui-safety-gate.cjs +107 -0
- package/commands/gsd/add-tests.md +42 -0
- package/commands/gsd/ai-integration-phase.md +37 -0
- package/commands/gsd/audit-fix.md +34 -0
- package/commands/gsd/audit-milestone.md +37 -0
- package/commands/gsd/audit-uat.md +24 -0
- package/commands/gsd/autonomous.md +48 -0
- package/commands/gsd/capture.md +62 -0
- package/commands/gsd/cleanup.md +24 -0
- package/commands/gsd/code-review.md +59 -0
- package/commands/gsd/complete-milestone.md +143 -0
- package/commands/gsd/config.md +56 -0
- package/commands/gsd/debug.md +52 -0
- package/commands/gsd/discover-product.md +65 -0
- package/commands/gsd/discuss-phase.md +77 -0
- package/commands/gsd/docs-update.md +49 -0
- package/commands/gsd/eval-review.md +33 -0
- package/commands/gsd/execute-phase.md +66 -0
- package/commands/gsd/explore.md +27 -0
- package/commands/gsd/extract-learnings.md +23 -0
- package/commands/gsd/fast.md +31 -0
- package/commands/gsd/forensics.md +57 -0
- package/commands/gsd/graphify.md +204 -0
- package/commands/gsd/health.md +31 -0
- package/commands/gsd/help.md +28 -0
- package/commands/gsd/import.md +45 -0
- package/commands/gsd/inbox.md +39 -0
- package/commands/gsd/ingest-docs.md +42 -0
- package/commands/gsd/manager.md +45 -0
- package/commands/gsd/map-codebase.md +83 -0
- package/commands/gsd/milestone-summary.md +51 -0
- package/commands/gsd/model-domain.md +65 -0
- package/commands/gsd/mvp-phase.md +45 -0
- package/commands/gsd/new-milestone.md +45 -0
- package/commands/gsd/new-project.md +47 -0
- package/commands/gsd/ns-context.md +23 -0
- package/commands/gsd/ns-ideate.md +24 -0
- package/commands/gsd/ns-manage.md +29 -0
- package/commands/gsd/ns-project.md +22 -0
- package/commands/gsd/ns-review.md +26 -0
- package/commands/gsd/ns-workflow.md +28 -0
- package/commands/gsd/pause-work.md +43 -0
- package/commands/gsd/phase.md +56 -0
- package/commands/gsd/plan-phase.md +64 -0
- package/commands/gsd/plan-review-convergence.md +59 -0
- package/commands/gsd/pr-branch.md +26 -0
- package/commands/gsd/profile-user.md +46 -0
- package/commands/gsd/progress.md +48 -0
- package/commands/gsd/quick.md +174 -0
- package/commands/gsd/recommend-architecture.md +64 -0
- package/commands/gsd/resume-work.md +30 -0
- package/commands/gsd/review-backlog.md +63 -0
- package/commands/gsd/review.md +42 -0
- package/commands/gsd/secure-phase.md +36 -0
- package/commands/gsd/settings.md +29 -0
- package/commands/gsd/ship.md +24 -0
- package/commands/gsd/sketch.md +60 -0
- package/commands/gsd/spec-phase.md +63 -0
- package/commands/gsd/spike.md +57 -0
- package/commands/gsd/stats.md +20 -0
- package/commands/gsd/surface.md +155 -0
- package/commands/gsd/testing-strategy.md +65 -0
- package/commands/gsd/thread.md +24 -0
- package/commands/gsd/ui-phase.md +35 -0
- package/commands/gsd/ui-review.md +33 -0
- package/commands/gsd/ultraplan-phase.md +34 -0
- package/commands/gsd/undo.md +35 -0
- package/commands/gsd/update.md +49 -0
- package/commands/gsd/validate-phase.md +36 -0
- package/commands/gsd/verify-work.md +39 -0
- package/commands/gsd/workspace.md +52 -0
- package/commands/gsd/workstreams.md +70 -0
- package/gemini-extension.json +6 -0
- package/gsd-core/bin/check-latest-version.cjs +161 -0
- package/gsd-core/bin/gsd-tools.cjs +1928 -0
- package/gsd-core/bin/lib/active-workstream-store.cjs +291 -0
- package/gsd-core/bin/lib/adr-parser.cjs +399 -0
- package/gsd-core/bin/lib/agent-command-router.cjs +68 -0
- package/gsd-core/bin/lib/artifacts.cjs +51 -0
- package/gsd-core/bin/lib/audit.cjs +743 -0
- package/gsd-core/bin/lib/check-command-router.cjs +343 -0
- package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +81 -0
- package/gsd-core/bin/lib/cli-exit.cjs +42 -0
- package/gsd-core/bin/lib/clock.cjs +95 -0
- package/gsd-core/bin/lib/clusters.cjs +132 -0
- package/gsd-core/bin/lib/code-review-flags.cjs +59 -0
- package/gsd-core/bin/lib/command-aliases.cjs +809 -0
- package/gsd-core/bin/lib/command-arg-projection.cjs +55 -0
- package/gsd-core/bin/lib/command-routing-hub.cjs +300 -0
- package/gsd-core/bin/lib/commands.cjs +1203 -0
- package/gsd-core/bin/lib/config-schema.cjs +29 -0
- package/gsd-core/bin/lib/config-types.cjs +19 -0
- package/gsd-core/bin/lib/config.cjs +738 -0
- package/gsd-core/bin/lib/configuration.cjs +239 -0
- package/gsd-core/bin/lib/context-utilization.cjs +48 -0
- package/gsd-core/bin/lib/core.cjs +2051 -0
- package/gsd-core/bin/lib/decisions.cjs +118 -0
- package/gsd-core/bin/lib/docs.cjs +252 -0
- package/gsd-core/bin/lib/drift.cjs +364 -0
- package/gsd-core/bin/lib/fallow-runner.cjs +115 -0
- package/gsd-core/bin/lib/frontmatter.cjs +442 -0
- package/gsd-core/bin/lib/gap-checker.cjs +257 -0
- package/gsd-core/bin/lib/graphify.cjs +496 -0
- package/gsd-core/bin/lib/gsd2-import.cjs +456 -0
- package/gsd-core/bin/lib/init-command-router.cjs +62 -0
- package/gsd-core/bin/lib/init.cjs +1815 -0
- package/gsd-core/bin/lib/install-profiles.cjs +584 -0
- package/gsd-core/bin/lib/installer-migration-authoring.cjs +122 -0
- package/gsd-core/bin/lib/installer-migration-report.cjs +350 -0
- package/gsd-core/bin/lib/installer-migrations/000-first-time-baseline.cjs +218 -0
- package/gsd-core/bin/lib/installer-migrations/001-legacy-orphan-files.cjs +48 -0
- package/gsd-core/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs +94 -0
- package/gsd-core/bin/lib/installer-migrations/003-rename-get-shit-done-to-gsd-core.cjs +108 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +823 -0
- package/gsd-core/bin/lib/intel.cjs +590 -0
- package/gsd-core/bin/lib/learnings.cjs +270 -0
- package/gsd-core/bin/lib/legacy-cleanup.cjs +253 -0
- package/gsd-core/bin/lib/milestone.cjs +373 -0
- package/gsd-core/bin/lib/model-catalog.cjs +154 -0
- package/gsd-core/bin/lib/model-profiles.cjs +24 -0
- package/gsd-core/bin/lib/observability/event.cjs +51 -0
- package/gsd-core/bin/lib/observability/logger.cjs +146 -0
- package/gsd-core/bin/lib/observability/redaction.cjs +48 -0
- package/gsd-core/bin/lib/package-identity.cjs +35 -0
- package/gsd-core/bin/lib/package-legitimacy.cjs +368 -0
- package/gsd-core/bin/lib/phase-command-router.cjs +189 -0
- package/gsd-core/bin/lib/phase-lifecycle.cjs +74 -0
- package/gsd-core/bin/lib/phase.cjs +1307 -0
- package/gsd-core/bin/lib/phases-command-router.cjs +43 -0
- package/gsd-core/bin/lib/plan-scan.cjs +91 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +245 -0
- package/gsd-core/bin/lib/profile-output.cjs +1120 -0
- package/gsd-core/bin/lib/profile-pipeline.cjs +517 -0
- package/gsd-core/bin/lib/project-root.cjs +119 -0
- package/gsd-core/bin/lib/prompt-budget.cjs +305 -0
- package/gsd-core/bin/lib/research-provider.cjs +137 -0
- package/gsd-core/bin/lib/research-store.cjs +167 -0
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +121 -0
- package/gsd-core/bin/lib/roadmap-command-router.cjs +166 -0
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +476 -0
- package/gsd-core/bin/lib/roadmap.cjs +600 -0
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +312 -0
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +56 -0
- package/gsd-core/bin/lib/runtime-homes.cjs +190 -0
- package/gsd-core/bin/lib/runtime-name-policy.cjs +96 -0
- package/gsd-core/bin/lib/runtime-slash.cjs +119 -0
- package/gsd-core/bin/lib/schema-detect.cjs +159 -0
- package/gsd-core/bin/lib/secrets.cjs +34 -0
- package/gsd-core/bin/lib/security.cjs +480 -0
- package/gsd-core/bin/lib/semver-compare.cjs +42 -0
- package/gsd-core/bin/lib/shell-command-projection.cjs +533 -0
- package/gsd-core/bin/lib/state-command-router.cjs +160 -0
- package/gsd-core/bin/lib/state-document.cjs +259 -0
- package/gsd-core/bin/lib/state.cjs +2010 -0
- package/gsd-core/bin/lib/surface.cjs +449 -0
- package/gsd-core/bin/lib/task-command-router.cjs +85 -0
- package/gsd-core/bin/lib/template.cjs +237 -0
- package/gsd-core/bin/lib/uat.cjs +297 -0
- package/gsd-core/bin/lib/ui-safety-gate.cjs +98 -0
- package/gsd-core/bin/lib/update-context.cjs +218 -0
- package/gsd-core/bin/lib/validate-command-router.cjs +91 -0
- package/gsd-core/bin/lib/validate.cjs +112 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +31 -0
- package/gsd-core/bin/lib/verification.cjs +193 -0
- package/gsd-core/bin/lib/verify-command-router.cjs +44 -0
- package/gsd-core/bin/lib/verify.cjs +1451 -0
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +81 -0
- package/gsd-core/bin/lib/workstream-inventory.cjs +147 -0
- package/gsd-core/bin/lib/workstream-name-policy.cjs +91 -0
- package/gsd-core/bin/lib/workstream.cjs +380 -0
- package/gsd-core/bin/lib/worktree-base-ref.cjs +325 -0
- package/gsd-core/bin/lib/worktree-safety.cjs +943 -0
- package/gsd-core/bin/shared/config-defaults.manifest.json +98 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +192 -0
- package/gsd-core/bin/shared/model-catalog.json +149 -0
- package/gsd-core/bin/shared/runtime-aliases.manifest.json +75 -0
- package/gsd-core/bin/verify-reapply-patches.cjs +349 -0
- package/gsd-core/contexts/dev.md +21 -0
- package/gsd-core/contexts/research.md +22 -0
- package/gsd-core/contexts/review.md +23 -0
- package/gsd-core/references/agent-contracts.md +79 -0
- package/gsd-core/references/ai-evals.md +156 -0
- package/gsd-core/references/ai-frameworks.md +186 -0
- package/gsd-core/references/architecture-decision.md +74 -0
- package/gsd-core/references/artifact-types.md +131 -0
- package/gsd-core/references/auth-in-tests.md +91 -0
- package/gsd-core/references/autonomous-smart-discuss.md +277 -0
- package/gsd-core/references/checkpoints.md +814 -0
- package/gsd-core/references/common-bug-patterns.md +114 -0
- package/gsd-core/references/context-budget.md +85 -0
- package/gsd-core/references/continuation-format.md +253 -0
- package/gsd-core/references/db-test-isolation.md +54 -0
- package/gsd-core/references/debugger-philosophy.md +76 -0
- package/gsd-core/references/decimal-phase-calculation.md +64 -0
- package/gsd-core/references/doc-conflict-engine.md +91 -0
- package/gsd-core/references/domain-modeling.md +80 -0
- package/gsd-core/references/domain-probes.md +125 -0
- package/gsd-core/references/e2e-tiering.md +35 -0
- package/gsd-core/references/execute-mvp-tdd.md +81 -0
- package/gsd-core/references/executor-examples.md +110 -0
- package/gsd-core/references/few-shot-examples/plan-checker.md +73 -0
- package/gsd-core/references/few-shot-examples/verifier.md +109 -0
- package/gsd-core/references/flaky-test-checklist.md +22 -0
- package/gsd-core/references/gate-prompts.md +100 -0
- package/gsd-core/references/gates.md +70 -0
- package/gsd-core/references/git-integration.md +298 -0
- package/gsd-core/references/git-planning-commit.md +40 -0
- package/gsd-core/references/ios-scaffold.md +123 -0
- package/gsd-core/references/mandatory-initial-read.md +2 -0
- package/gsd-core/references/model-profile-resolution.md +38 -0
- package/gsd-core/references/model-profiles.md +245 -0
- package/gsd-core/references/mvp-concepts.md +49 -0
- package/gsd-core/references/phase-argument-parsing.md +61 -0
- package/gsd-core/references/planner-antipatterns.md +89 -0
- package/gsd-core/references/planner-chunked.md +49 -0
- package/gsd-core/references/planner-gap-closure.md +62 -0
- package/gsd-core/references/planner-graphify-auto-update.md +67 -0
- package/gsd-core/references/planner-human-verify-mode.md +57 -0
- package/gsd-core/references/planner-interface-context.md +62 -0
- package/gsd-core/references/planner-load-graph-context.md +36 -0
- package/gsd-core/references/planner-mvp-mode.md +53 -0
- package/gsd-core/references/planner-reviews.md +39 -0
- package/gsd-core/references/planner-revision.md +87 -0
- package/gsd-core/references/planner-source-audit.md +73 -0
- package/gsd-core/references/planning-config.md +473 -0
- package/gsd-core/references/product-discovery.md +49 -0
- package/gsd-core/references/project-skills-discovery.md +19 -0
- package/gsd-core/references/questioning.md +162 -0
- package/gsd-core/references/realistic-test-data.md +44 -0
- package/gsd-core/references/research-documentation-lookup.md +29 -0
- package/gsd-core/references/research-philosophy.md +29 -0
- package/gsd-core/references/research-verification-protocol.md +27 -0
- package/gsd-core/references/revision-loop.md +97 -0
- package/gsd-core/references/scout-codebase.md +51 -0
- package/gsd-core/references/skeleton-template.md +48 -0
- package/gsd-core/references/sketch-interactivity.md +41 -0
- package/gsd-core/references/sketch-theme-system.md +94 -0
- package/gsd-core/references/sketch-tooling.md +45 -0
- package/gsd-core/references/sketch-variant-patterns.md +81 -0
- package/gsd-core/references/spidr-splitting.md +69 -0
- package/gsd-core/references/tdd.md +330 -0
- package/gsd-core/references/test-containers.md +55 -0
- package/gsd-core/references/test-strategy.md +75 -0
- package/gsd-core/references/thinking-models-debug.md +44 -0
- package/gsd-core/references/thinking-models-execution.md +50 -0
- package/gsd-core/references/thinking-models-planning.md +62 -0
- package/gsd-core/references/thinking-models-research.md +50 -0
- package/gsd-core/references/thinking-models-verification.md +55 -0
- package/gsd-core/references/thinking-partner.md +96 -0
- package/gsd-core/references/ui-brand.md +162 -0
- package/gsd-core/references/universal-anti-patterns.md +63 -0
- package/gsd-core/references/user-profiling.md +681 -0
- package/gsd-core/references/user-story-template.md +58 -0
- package/gsd-core/references/verification-overrides.md +227 -0
- package/gsd-core/references/verification-patterns.md +612 -0
- package/gsd-core/references/verify-mvp-mode.md +85 -0
- package/gsd-core/references/workstream-flag.md +111 -0
- package/gsd-core/references/worktree-branch-check.md +38 -0
- package/gsd-core/references/worktree-path-safety.md +67 -0
- package/gsd-core/templates/AI-SPEC.md +246 -0
- package/gsd-core/templates/DEBUG.md +169 -0
- package/gsd-core/templates/README.md +77 -0
- package/gsd-core/templates/SECURITY.md +61 -0
- package/gsd-core/templates/UAT.md +265 -0
- package/gsd-core/templates/UI-SPEC.md +100 -0
- package/gsd-core/templates/VALIDATION.md +76 -0
- package/gsd-core/templates/adr.md +58 -0
- package/gsd-core/templates/claude-md.md +145 -0
- package/gsd-core/templates/codebase/architecture.md +255 -0
- package/gsd-core/templates/codebase/concerns.md +310 -0
- package/gsd-core/templates/codebase/conventions.md +307 -0
- package/gsd-core/templates/codebase/integrations.md +280 -0
- package/gsd-core/templates/codebase/stack.md +186 -0
- package/gsd-core/templates/codebase/structure.md +285 -0
- package/gsd-core/templates/codebase/testing.md +480 -0
- package/gsd-core/templates/config.json +62 -0
- package/gsd-core/templates/context.md +352 -0
- package/gsd-core/templates/continue-here.md +78 -0
- package/gsd-core/templates/copilot-instructions.md +7 -0
- package/gsd-core/templates/debug-subagent-prompt.md +91 -0
- package/gsd-core/templates/dev-preferences.md +21 -0
- package/gsd-core/templates/discovery.md +146 -0
- package/gsd-core/templates/discussion-log.md +63 -0
- package/gsd-core/templates/domain-model.md +54 -0
- package/gsd-core/templates/milestone-archive.md +123 -0
- package/gsd-core/templates/milestone.md +115 -0
- package/gsd-core/templates/phase-prompt.md +610 -0
- package/gsd-core/templates/planner-subagent-prompt.md +117 -0
- package/gsd-core/templates/product-brief.md +55 -0
- package/gsd-core/templates/project.md +186 -0
- package/gsd-core/templates/requirements.md +231 -0
- package/gsd-core/templates/research-project/ARCHITECTURE.md +204 -0
- package/gsd-core/templates/research-project/FEATURES.md +147 -0
- package/gsd-core/templates/research-project/PITFALLS.md +200 -0
- package/gsd-core/templates/research-project/STACK.md +120 -0
- package/gsd-core/templates/research-project/SUMMARY.md +170 -0
- package/gsd-core/templates/research.md +592 -0
- package/gsd-core/templates/retrospective.md +54 -0
- package/gsd-core/templates/roadmap.md +202 -0
- package/gsd-core/templates/spec.md +307 -0
- package/gsd-core/templates/state.md +195 -0
- package/gsd-core/templates/summary-complex.md +59 -0
- package/gsd-core/templates/summary-minimal.md +41 -0
- package/gsd-core/templates/summary-standard.md +48 -0
- package/gsd-core/templates/summary.md +248 -0
- package/gsd-core/templates/test-strategy.md +50 -0
- package/gsd-core/templates/user-profile.md +146 -0
- package/gsd-core/templates/user-setup.md +311 -0
- package/gsd-core/templates/verification-report.md +322 -0
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -0
- package/gsd-core/workflows/add-backlog.md +91 -0
- package/gsd-core/workflows/add-phase.md +113 -0
- package/gsd-core/workflows/add-tests.md +355 -0
- package/gsd-core/workflows/add-todo.md +161 -0
- package/gsd-core/workflows/ai-integration-phase.md +295 -0
- package/gsd-core/workflows/analyze-dependencies.md +96 -0
- package/gsd-core/workflows/audit-fix.md +178 -0
- package/gsd-core/workflows/audit-milestone.md +360 -0
- package/gsd-core/workflows/audit-uat.md +110 -0
- package/gsd-core/workflows/autonomous.md +797 -0
- package/gsd-core/workflows/check-todos.md +180 -0
- package/gsd-core/workflows/cleanup.md +195 -0
- package/gsd-core/workflows/code-review-fix.md +502 -0
- package/gsd-core/workflows/code-review.md +658 -0
- package/gsd-core/workflows/complete-milestone.md +855 -0
- package/gsd-core/workflows/debug.md +237 -0
- package/gsd-core/workflows/diagnose-issues.md +245 -0
- package/gsd-core/workflows/discover-product.md +112 -0
- package/gsd-core/workflows/discovery-phase.md +291 -0
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +176 -0
- package/gsd-core/workflows/discuss-phase/modes/all.md +28 -0
- package/gsd-core/workflows/discuss-phase/modes/analyze.md +44 -0
- package/gsd-core/workflows/discuss-phase/modes/auto.md +57 -0
- package/gsd-core/workflows/discuss-phase/modes/batch.md +52 -0
- package/gsd-core/workflows/discuss-phase/modes/chain.md +98 -0
- package/gsd-core/workflows/discuss-phase/modes/default.md +141 -0
- package/gsd-core/workflows/discuss-phase/modes/power.md +44 -0
- package/gsd-core/workflows/discuss-phase/modes/text.md +55 -0
- package/gsd-core/workflows/discuss-phase/templates/checkpoint.json +18 -0
- package/gsd-core/workflows/discuss-phase/templates/context.md +136 -0
- package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +50 -0
- package/gsd-core/workflows/discuss-phase-assumptions.md +675 -0
- package/gsd-core/workflows/discuss-phase-power.md +291 -0
- package/gsd-core/workflows/discuss-phase.md +499 -0
- package/gsd-core/workflows/do.md +111 -0
- package/gsd-core/workflows/docs-update.md +1176 -0
- package/gsd-core/workflows/edit-phase.md +295 -0
- package/gsd-core/workflows/eval-review.md +156 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +95 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +117 -0
- package/gsd-core/workflows/execute-phase.md +1752 -0
- package/gsd-core/workflows/execute-plan.md +526 -0
- package/gsd-core/workflows/explore.md +146 -0
- package/gsd-core/workflows/extract-learnings.md +243 -0
- package/gsd-core/workflows/fast.md +124 -0
- package/gsd-core/workflows/forensics.md +279 -0
- package/gsd-core/workflows/graduation.md +196 -0
- package/gsd-core/workflows/health.md +224 -0
- package/gsd-core/workflows/help/modes/brief.md +22 -0
- package/gsd-core/workflows/help/modes/default.md +50 -0
- package/gsd-core/workflows/help/modes/full.md +789 -0
- package/gsd-core/workflows/help/modes/topic.md +74 -0
- package/gsd-core/workflows/help.md +24 -0
- package/gsd-core/workflows/import.md +256 -0
- package/gsd-core/workflows/inbox.md +387 -0
- package/gsd-core/workflows/ingest-docs.md +340 -0
- package/gsd-core/workflows/insert-phase.md +152 -0
- package/gsd-core/workflows/list-phase-assumptions.md +178 -0
- package/gsd-core/workflows/list-workspaces.md +57 -0
- package/gsd-core/workflows/manager.md +393 -0
- package/gsd-core/workflows/map-codebase.md +446 -0
- package/gsd-core/workflows/milestone-summary.md +224 -0
- package/gsd-core/workflows/model-domain.md +162 -0
- package/gsd-core/workflows/mvp-phase.md +222 -0
- package/gsd-core/workflows/new-milestone.md +635 -0
- package/gsd-core/workflows/new-project.md +1555 -0
- package/gsd-core/workflows/new-workspace.md +240 -0
- package/gsd-core/workflows/next.md +299 -0
- package/gsd-core/workflows/node-repair.md +92 -0
- package/gsd-core/workflows/note.md +158 -0
- package/gsd-core/workflows/pause-work.md +244 -0
- package/gsd-core/workflows/plan-milestone-gaps.md +281 -0
- package/gsd-core/workflows/plan-phase.md +1814 -0
- package/gsd-core/workflows/plan-review-convergence.md +346 -0
- package/gsd-core/workflows/plant-seed.md +230 -0
- package/gsd-core/workflows/pr-branch.md +157 -0
- package/gsd-core/workflows/profile-user.md +453 -0
- package/gsd-core/workflows/progress.md +699 -0
- package/gsd-core/workflows/quick.md +1017 -0
- package/gsd-core/workflows/reapply-patches.md +426 -0
- package/gsd-core/workflows/recommend-architecture.md +135 -0
- package/gsd-core/workflows/remove-phase.md +156 -0
- package/gsd-core/workflows/remove-workspace.md +108 -0
- package/gsd-core/workflows/resume-project.md +332 -0
- package/gsd-core/workflows/review.md +748 -0
- package/gsd-core/workflows/scan.md +107 -0
- package/gsd-core/workflows/secure-phase.md +182 -0
- package/gsd-core/workflows/session-report.md +146 -0
- package/gsd-core/workflows/settings-advanced.md +810 -0
- package/gsd-core/workflows/settings-integrations.md +312 -0
- package/gsd-core/workflows/settings.md +566 -0
- package/gsd-core/workflows/ship.md +405 -0
- package/gsd-core/workflows/sketch-wrap-up.md +286 -0
- package/gsd-core/workflows/sketch.md +361 -0
- package/gsd-core/workflows/spec-phase.md +263 -0
- package/gsd-core/workflows/spike-wrap-up.md +307 -0
- package/gsd-core/workflows/spike.md +453 -0
- package/gsd-core/workflows/stats.md +80 -0
- package/gsd-core/workflows/sync-skills.md +182 -0
- package/gsd-core/workflows/testing-strategy.md +122 -0
- package/gsd-core/workflows/thread.md +222 -0
- package/gsd-core/workflows/transition.md +694 -0
- package/gsd-core/workflows/ui-phase.md +328 -0
- package/gsd-core/workflows/ui-review.md +193 -0
- package/gsd-core/workflows/ultraplan-phase.md +199 -0
- package/gsd-core/workflows/undo.md +314 -0
- package/gsd-core/workflows/update.md +496 -0
- package/gsd-core/workflows/validate-phase.md +181 -0
- package/gsd-core/workflows/verify-phase.md +544 -0
- package/gsd-core/workflows/verify-work.md +781 -0
- package/hooks/dist/gsd-check-update-worker.js +108 -0
- package/hooks/dist/gsd-check-update.js +66 -0
- package/hooks/dist/gsd-config-reload.js +133 -0
- package/hooks/dist/gsd-context-monitor.js +195 -0
- package/hooks/dist/gsd-cursor-post-tool.js +75 -0
- package/hooks/dist/gsd-cursor-session-start.js +52 -0
- package/hooks/dist/gsd-graphify-update.sh +158 -0
- package/hooks/dist/gsd-phase-boundary.sh +47 -0
- package/hooks/dist/gsd-prompt-guard.js +97 -0
- package/hooks/dist/gsd-read-guard.js +101 -0
- package/hooks/dist/gsd-read-injection-scanner.js +203 -0
- package/hooks/dist/gsd-session-state.sh +59 -0
- package/hooks/dist/gsd-statusline.js +566 -0
- package/hooks/dist/gsd-update-banner.js +138 -0
- package/hooks/dist/gsd-validate-commit.sh +57 -0
- package/hooks/dist/gsd-workflow-guard.js +167 -0
- package/hooks/dist/gsd-worktree-path-guard.js +169 -0
- package/hooks/dist/lib/git-cmd.js +150 -0
- package/hooks/dist/lib/gsd-graphify-rebuild.sh +65 -0
- package/hooks/dist/managed-hooks-registry.cjs +38 -0
- package/hooks/gsd-check-update-worker.js +108 -0
- package/hooks/gsd-check-update.js +66 -0
- package/hooks/gsd-config-reload.js +133 -0
- package/hooks/gsd-context-monitor.js +195 -0
- package/hooks/gsd-cursor-post-tool.js +75 -0
- package/hooks/gsd-cursor-session-start.js +52 -0
- package/hooks/gsd-graphify-update.sh +158 -0
- package/hooks/gsd-phase-boundary.sh +47 -0
- package/hooks/gsd-prompt-guard.js +97 -0
- package/hooks/gsd-read-guard.js +101 -0
- package/hooks/gsd-read-injection-scanner.js +203 -0
- package/hooks/gsd-session-state.sh +59 -0
- package/hooks/gsd-statusline.js +566 -0
- package/hooks/gsd-update-banner.js +138 -0
- package/hooks/gsd-validate-commit.sh +57 -0
- package/hooks/gsd-workflow-guard.js +167 -0
- package/hooks/gsd-worktree-path-guard.js +169 -0
- package/hooks/hooks.json +69 -0
- package/hooks/lib/git-cmd.js +150 -0
- package/hooks/lib/gsd-graphify-rebuild.sh +65 -0
- package/hooks/managed-hooks-registry.cjs +38 -0
- package/package.json +115 -0
- package/scripts/affected-tests-lib.cjs +542 -0
- package/scripts/audit-workflow-script-paths.cjs +73 -0
- package/scripts/base64-scan.sh +351 -0
- package/scripts/build-hooks.js +247 -0
- package/scripts/changeset/README.md +129 -0
- package/scripts/changeset/cli.cjs +590 -0
- package/scripts/changeset/github-release-notes.cjs +199 -0
- package/scripts/changeset/lint.cjs +111 -0
- package/scripts/changeset/new.cjs +137 -0
- package/scripts/changeset/parse.cjs +114 -0
- package/scripts/changeset/render.cjs +34 -0
- package/scripts/changeset/serialize.cjs +130 -0
- package/scripts/check-alias-drift.cjs +114 -0
- package/scripts/check-env.cjs +312 -0
- package/scripts/check-npm-integrity.cjs +215 -0
- package/scripts/ci-guard-runner.cjs +22 -0
- package/scripts/ci-prepare-test-scope.cjs +51 -0
- package/scripts/ci-rebase-check.cjs +86 -0
- package/scripts/ci-test-scope.cjs +431 -0
- package/scripts/command-contract-helpers.cjs +64 -0
- package/scripts/diff-touches-shipped-paths.cjs +155 -0
- package/scripts/fix-slash-commands.cjs +147 -0
- package/scripts/gen-inventory-manifest.cjs +115 -0
- package/scripts/gen-research-agents.cjs +276 -0
- package/scripts/generate-package-identity.cjs +125 -0
- package/scripts/issue-dedupe.cjs +278 -0
- package/scripts/lib/allowlist-ratchet.cjs +136 -0
- package/scripts/lib/cli-exit.cjs +56 -0
- package/scripts/lint-command-contract.cjs +114 -0
- package/scripts/lint-descriptions.cjs +87 -0
- package/scripts/lint-docs-required.cjs +222 -0
- package/scripts/lint-legacy-dir-name.cjs +160 -0
- package/scripts/lint-package-identity-drift.cjs +141 -0
- package/scripts/lint-pr-check-project-dir.cjs +99 -0
- package/scripts/lint-shell-command-projection-drift.cjs +62 -0
- package/scripts/lint-skill-deps.cjs +185 -0
- package/scripts/lint-test-file-count.allowlist.json +135 -0
- package/scripts/lint-test-file-count.cjs +246 -0
- package/scripts/mutation-matrix.cjs +222 -0
- package/scripts/pr-template-policy.cjs +268 -0
- package/scripts/prompt-injection-scan.sh +207 -0
- package/scripts/release-notes/discord-release-summary.cjs +373 -0
- package/scripts/release-notes/format-github-release-notes.cjs +261 -0
- package/scripts/release-tarball-smoke.cjs +629 -0
- package/scripts/research-profiles.cjs +149 -0
- package/scripts/run-affected-tests.cjs +7 -0
- package/scripts/run-cross-platform-tests.cjs +67 -0
- package/scripts/run-tests.cjs +315 -0
- package/scripts/secret-scan-lint.sh +231 -0
- package/scripts/secret-scan.sh +358 -0
- package/scripts/setup-branch-protection.sh +236 -0
- package/scripts/strip-prose-atrefs.cjs +106 -0
- package/scripts/sync-manifest-versions.cjs +119 -0
- package/scripts/sync-rulesets.sh +34 -0
- package/scripts/sync-runtime-launcher.cjs +399 -0
- package/scripts/test-failure-reasons.cjs +34 -0
- package/scripts/verify-npm-publish.cjs +240 -0
- package/scripts/workflow-policy.cjs +450 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Docs-required lint (#3213).
|
|
6
|
+
*
|
|
7
|
+
* Mirrors scripts/changeset/lint.cjs. Pure verdict function
|
|
8
|
+
* evaluateLint({ changedFiles, fragments, labels, malformed }) returns
|
|
9
|
+
* { ok, reason, triggering } using the LINT_REASON enum. The CLI wrapper
|
|
10
|
+
* reads the PR diff (`git diff --name-only origin/${base}...HEAD`), parses
|
|
11
|
+
* each touched `.changeset/*.md` fragment, then calls evaluateLint.
|
|
12
|
+
*
|
|
13
|
+
* Tests assert on the structured verdict, never on free text.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { parseFragment, FRAGMENT_ERROR } = require('./changeset/parse.cjs');
|
|
17
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
18
|
+
|
|
19
|
+
const LINT_REASON = Object.freeze({
|
|
20
|
+
OK_NO_TRIGGERING_FRAGMENTS: 'ok_no_triggering_fragments',
|
|
21
|
+
OK_DOCS_UPDATED: 'ok_docs_updated',
|
|
22
|
+
OK_OPT_OUT_LABEL: 'ok_opt_out_label',
|
|
23
|
+
OK_FRAGMENTS_EXEMPT: 'ok_fragments_exempt',
|
|
24
|
+
FAIL_DOCS_MISSING: 'fail_docs_missing',
|
|
25
|
+
FAIL_MALFORMED_FRAGMENT: 'fail_malformed_fragment',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const OPT_OUT_LABEL = 'no-docs';
|
|
29
|
+
|
|
30
|
+
// Fragment types that require a docs update. `Fixed` and `Security` are
|
|
31
|
+
// bug-class — they describe regressions or vulnerabilities, not new
|
|
32
|
+
// behavior to document.
|
|
33
|
+
const TRIGGERING_TYPES = new Set(['Added', 'Changed', 'Deprecated', 'Removed']);
|
|
34
|
+
|
|
35
|
+
const DOCS_PREFIX = 'docs/';
|
|
36
|
+
|
|
37
|
+
function isFragmentPath(file) {
|
|
38
|
+
return /^\.changeset\/[^/]+\.md$/.test(file) && !file.endsWith('/README.md');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isDocsFile(file) {
|
|
42
|
+
return file.startsWith(DOCS_PREFIX);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Per-fragment escape hatch: parse.cjs extracts `<!-- docs-exempt: <reason> -->`
|
|
46
|
+
// from the body into `fragment.docsExempt` (a non-empty reason string when the
|
|
47
|
+
// marker was present and well-formed; `null` otherwise). A non-empty audit
|
|
48
|
+
// trail is required — the lint defends in depth here too: even if a caller
|
|
49
|
+
// constructs a fragment with `docsExempt: ''`, that does not count as exempt.
|
|
50
|
+
function isExemptFragment(fragment) {
|
|
51
|
+
return typeof fragment.docsExempt === 'string' && fragment.docsExempt.trim().length > 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Pure verdict — no fs, no git.
|
|
56
|
+
*
|
|
57
|
+
* Malformed fragments fail closed: a triggering fragment with bad frontmatter
|
|
58
|
+
* cannot silently bypass docs enforcement. The changeset-required lint only
|
|
59
|
+
* checks fragment _presence_, not _validity_, so docs lint takes responsibility
|
|
60
|
+
* for any fragment it tries to consume.
|
|
61
|
+
*
|
|
62
|
+
* @param {object} args
|
|
63
|
+
* @param {string[]} args.changedFiles - file paths changed in the PR
|
|
64
|
+
* @param {Array<{ path: string, type: string, body: string, docsExempt: string|null }>} args.fragments
|
|
65
|
+
* - parsed records for well-formed `.changeset/*.md` files in `changedFiles`
|
|
66
|
+
* @param {Array<{ path: string, reason: string }>} [args.malformed]
|
|
67
|
+
* - records for `.changeset/*.md` files that failed `parseFragment`
|
|
68
|
+
* @param {string[]} args.labels - PR labels
|
|
69
|
+
* @returns {{ ok: boolean, reason: string, triggering: string[], malformed?: Array<{path:string,reason:string}> }}
|
|
70
|
+
*/
|
|
71
|
+
function evaluateLint({ changedFiles, fragments, labels, malformed = [] }) {
|
|
72
|
+
if (malformed.length > 0) {
|
|
73
|
+
return {
|
|
74
|
+
ok: false,
|
|
75
|
+
reason: LINT_REASON.FAIL_MALFORMED_FRAGMENT,
|
|
76
|
+
triggering: [],
|
|
77
|
+
malformed,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const triggering = fragments.filter((f) => TRIGGERING_TYPES.has(f.type));
|
|
82
|
+
const triggeringPaths = triggering.map((f) => f.path);
|
|
83
|
+
|
|
84
|
+
if (triggering.length === 0) {
|
|
85
|
+
return { ok: true, reason: LINT_REASON.OK_NO_TRIGGERING_FRAGMENTS, triggering: [] };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Per-fragment exempt path: every triggering fragment must carry the marker.
|
|
89
|
+
// Partial exemption fails closed — one un-marked Added fragment still requires docs.
|
|
90
|
+
if (triggering.every(isExemptFragment)) {
|
|
91
|
+
return { ok: true, reason: LINT_REASON.OK_FRAGMENTS_EXEMPT, triggering: triggeringPaths };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (labels.includes(OPT_OUT_LABEL)) {
|
|
95
|
+
return { ok: true, reason: LINT_REASON.OK_OPT_OUT_LABEL, triggering: triggeringPaths };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (changedFiles.some(isDocsFile)) {
|
|
99
|
+
return { ok: true, reason: LINT_REASON.OK_DOCS_UPDATED, triggering: triggeringPaths };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return { ok: false, reason: LINT_REASON.FAIL_DOCS_MISSING, triggering: triggeringPaths };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function readFragmentsFromDisk(changedFiles, rootDir) {
|
|
106
|
+
const fs = require('node:fs');
|
|
107
|
+
const path = require('node:path');
|
|
108
|
+
const fragments = [];
|
|
109
|
+
const malformed = [];
|
|
110
|
+
for (const rel of changedFiles) {
|
|
111
|
+
if (!isFragmentPath(rel)) continue;
|
|
112
|
+
const abs = path.join(rootDir, rel);
|
|
113
|
+
if (!fs.existsSync(abs)) continue; // fragment deleted in PR — skip
|
|
114
|
+
let src;
|
|
115
|
+
try {
|
|
116
|
+
src = fs.readFileSync(abs, 'utf8');
|
|
117
|
+
} catch (e) {
|
|
118
|
+
malformed.push({ path: rel, reason: 'read_error', detail: e.code || e.message });
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const parsed = parseFragment(src);
|
|
122
|
+
if (!parsed.ok) {
|
|
123
|
+
malformed.push({ path: rel, reason: parsed.reason, detail: parsed.detail || null });
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
fragments.push({
|
|
127
|
+
path: rel,
|
|
128
|
+
type: parsed.fragment.type,
|
|
129
|
+
body: parsed.fragment.body,
|
|
130
|
+
docsExempt: parsed.fragment.docsExempt,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return { fragments, malformed };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function main() {
|
|
137
|
+
const fs = require('node:fs');
|
|
138
|
+
const cp = require('node:child_process');
|
|
139
|
+
const path = require('node:path');
|
|
140
|
+
|
|
141
|
+
const rootDir = path.join(__dirname, '..');
|
|
142
|
+
|
|
143
|
+
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
144
|
+
let labels = [];
|
|
145
|
+
if (eventPath && fs.existsSync(eventPath)) {
|
|
146
|
+
try {
|
|
147
|
+
const event = JSON.parse(fs.readFileSync(eventPath, 'utf8'));
|
|
148
|
+
labels = (event.pull_request?.labels || []).map((l) => l.name);
|
|
149
|
+
} catch { /* fall through */ }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const base = process.env.GITHUB_BASE_REF || 'main';
|
|
153
|
+
let changedFiles = [];
|
|
154
|
+
try {
|
|
155
|
+
// execFileSync with argv — no shell, so a malicious GITHUB_BASE_REF
|
|
156
|
+
// cannot inject shell syntax. Git's own ref-name validator rejects
|
|
157
|
+
// any metacharacters it would otherwise interpret.
|
|
158
|
+
const out = cp.execFileSync(
|
|
159
|
+
'git',
|
|
160
|
+
['diff', '--name-only', `origin/${base}...HEAD`],
|
|
161
|
+
{ encoding: 'utf8', cwd: rootDir },
|
|
162
|
+
);
|
|
163
|
+
changedFiles = out.split('\n').filter(Boolean);
|
|
164
|
+
} catch (e) {
|
|
165
|
+
throw new ExitError(2, `could not compute diff: ${e.message}`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const { fragments, malformed } = readFragmentsFromDisk(changedFiles, rootDir);
|
|
169
|
+
const verdict = evaluateLint({ changedFiles, fragments, labels, malformed });
|
|
170
|
+
|
|
171
|
+
if (process.argv.includes('--json')) {
|
|
172
|
+
process.stdout.write(
|
|
173
|
+
JSON.stringify({ ...verdict, changedFiles, fragments, malformed, labels }, null, 2) + '\n',
|
|
174
|
+
);
|
|
175
|
+
} else if (verdict.ok) {
|
|
176
|
+
process.stdout.write(`ok docs-lint: ${verdict.reason}\n`);
|
|
177
|
+
} else if (verdict.reason === LINT_REASON.FAIL_MALFORMED_FRAGMENT) {
|
|
178
|
+
process.stderr.write(`\nERROR docs-lint: ${verdict.reason}\n`);
|
|
179
|
+
process.stderr.write(
|
|
180
|
+
`${malformed.length} changeset fragment(s) failed to parse — docs lint cannot consume them:\n`,
|
|
181
|
+
);
|
|
182
|
+
for (const m of malformed) {
|
|
183
|
+
process.stderr.write(` ${m.path} (reason: ${m.reason}${m.detail ? `, detail: ${m.detail}` : ''})\n`);
|
|
184
|
+
}
|
|
185
|
+
process.stderr.write(
|
|
186
|
+
`\nFix the fragment frontmatter (\`type:\` + \`pr:\`) before this PR can pass.\n`,
|
|
187
|
+
);
|
|
188
|
+
} else {
|
|
189
|
+
process.stderr.write(`\nERROR docs-lint: ${verdict.reason}\n`);
|
|
190
|
+
process.stderr.write(
|
|
191
|
+
`${verdict.triggering.length} changeset fragment(s) require documentation updates:\n`,
|
|
192
|
+
);
|
|
193
|
+
for (const f of fragments.filter((f) => TRIGGERING_TYPES.has(f.type))) {
|
|
194
|
+
process.stderr.write(` ${f.path} (type: ${f.type})\n`);
|
|
195
|
+
}
|
|
196
|
+
process.stderr.write(`\nNo files under docs/ were modified in this PR.\n\n`);
|
|
197
|
+
process.stderr.write(
|
|
198
|
+
`Update the relevant docs/ file(s), or add the \`${OPT_OUT_LABEL}\` label if this change\n`,
|
|
199
|
+
);
|
|
200
|
+
process.stderr.write(
|
|
201
|
+
`is genuinely internal-only (infrastructure, refactor, test-only). Per-fragment\n`,
|
|
202
|
+
);
|
|
203
|
+
process.stderr.write(
|
|
204
|
+
`exemption via \`<!-- docs-exempt: <reason> -->\` inside the fragment body also works.\n`,
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
return verdict.ok ? 0 : 1;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (require.main === module) runMain(main);
|
|
211
|
+
|
|
212
|
+
module.exports = {
|
|
213
|
+
evaluateLint,
|
|
214
|
+
readFragmentsFromDisk,
|
|
215
|
+
LINT_REASON,
|
|
216
|
+
OPT_OUT_LABEL,
|
|
217
|
+
TRIGGERING_TYPES,
|
|
218
|
+
FRAGMENT_ERROR,
|
|
219
|
+
isFragmentPath,
|
|
220
|
+
isDocsFile,
|
|
221
|
+
isExemptFragment,
|
|
222
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* lint-legacy-dir-name.cjs
|
|
4
|
+
*
|
|
5
|
+
* Prevents accidental re-introduction of the bare legacy directory token
|
|
6
|
+
* `get-shit-done` now that the package has been renamed to `gsd-core` (#604).
|
|
7
|
+
*
|
|
8
|
+
* The forbidden token is constructed by splitting across the concat operator
|
|
9
|
+
* so this guard script cannot flag itself:
|
|
10
|
+
* const FORBIDDEN = 'get-shit' + '-done';
|
|
11
|
+
*
|
|
12
|
+
* Regex: FORBIDDEN + '(?!-\\w)' — allows any hyphenated slug variant
|
|
13
|
+
* (get-shit-done-OLD, get-shit-done-classic, get-shit-done-cli, etc.)
|
|
14
|
+
* while forbidding the bare word boundary that would indicate a stale
|
|
15
|
+
* directory reference. This is the exact pattern that would appear in a
|
|
16
|
+
* path like `~/.claude/get-shit-done/` or `'get-shit-done'` in code.
|
|
17
|
+
*
|
|
18
|
+
* Allowlist:
|
|
19
|
+
* - CHANGELOG.md (historical record; reviewed manually)
|
|
20
|
+
* - .changeset/ (ephemeral release-note fragments; consumed into CHANGELOG on
|
|
21
|
+
* release — like CHANGELOG, not swept by rename PRs)
|
|
22
|
+
* - Translated READMEs: README.ja-JP.md, README.ko-KR.md, README.pt-BR.md, README.zh-CN.md
|
|
23
|
+
* - Locale-specific docs dirs: docs/ja-JP/, docs/ko-KR/, docs/pt-BR/, docs/zh-CN/
|
|
24
|
+
* - Lines containing the marker `gsd-allow-legacy-name` (intentional uses)
|
|
25
|
+
* - Binary files (detected by NUL byte scan)
|
|
26
|
+
* - This guard script itself (by path)
|
|
27
|
+
*
|
|
28
|
+
* Exit 0 if no violations; exit 1 if any are found (with stderr diagnostics).
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
'use strict';
|
|
32
|
+
|
|
33
|
+
const { execFileSync } = require('child_process');
|
|
34
|
+
const fs = require('fs');
|
|
35
|
+
const path = require('path');
|
|
36
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
37
|
+
|
|
38
|
+
// Constructed with split to avoid self-match when this script is scanned.
|
|
39
|
+
const FORBIDDEN = 'get-shit' + '-done';
|
|
40
|
+
const FORBIDDEN_RE = new RegExp(FORBIDDEN + '(?!-\\w)', 'gi');
|
|
41
|
+
|
|
42
|
+
const ALLOW_MARKER = 'gsd-allow-legacy-name';
|
|
43
|
+
const SELF_PATH = path.resolve(__filename);
|
|
44
|
+
// GSD_LINT_LEGACY_REPO_ROOT is used by tests to redirect the guard to a
|
|
45
|
+
// temporary fixture git repo without touching the real working tree.
|
|
46
|
+
const REPO_ROOT = process.env.GSD_LINT_LEGACY_REPO_ROOT
|
|
47
|
+
? path.resolve(process.env.GSD_LINT_LEGACY_REPO_ROOT)
|
|
48
|
+
: path.resolve(__dirname, '..');
|
|
49
|
+
|
|
50
|
+
const ALLOWLIST_FILES = new Set([
|
|
51
|
+
'CHANGELOG.md',
|
|
52
|
+
'README.ja-JP.md',
|
|
53
|
+
'README.ko-KR.md',
|
|
54
|
+
'README.pt-BR.md',
|
|
55
|
+
'README.zh-CN.md',
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
const ALLOWLIST_DIR_PREFIXES = [
|
|
59
|
+
// Pending changeset fragments are ephemeral release-note stubs consumed into
|
|
60
|
+
// CHANGELOG on release — like CHANGELOG itself, they should not be swept by
|
|
61
|
+
// rename PRs and may legitimately contain the legacy token in historical prose.
|
|
62
|
+
'.changeset/',
|
|
63
|
+
'docs/ja-JP/',
|
|
64
|
+
'docs/ko-KR/',
|
|
65
|
+
'docs/pt-BR/',
|
|
66
|
+
'docs/zh-CN/',
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
function isAllowlisted(relPath) {
|
|
70
|
+
if (ALLOWLIST_FILES.has(relPath)) return true;
|
|
71
|
+
for (const prefix of ALLOWLIST_DIR_PREFIXES) {
|
|
72
|
+
if (relPath.startsWith(prefix)) return true;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isBinary(fullPath) {
|
|
78
|
+
// Read a small chunk and check for NUL bytes.
|
|
79
|
+
let fd;
|
|
80
|
+
try {
|
|
81
|
+
fd = fs.openSync(fullPath, 'r');
|
|
82
|
+
const buf = Buffer.allocUnsafe(512);
|
|
83
|
+
const bytesRead = fs.readSync(fd, buf, 0, 512, 0);
|
|
84
|
+
return buf.subarray(0, bytesRead).includes(0);
|
|
85
|
+
} catch {
|
|
86
|
+
return false;
|
|
87
|
+
} finally {
|
|
88
|
+
if (fd != null) {
|
|
89
|
+
try { fs.closeSync(fd); } catch { /* best-effort */ }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function main() {
|
|
95
|
+
// Enumerate tracked files via git ls-files so only committed/staged source is checked.
|
|
96
|
+
let trackedFiles;
|
|
97
|
+
try {
|
|
98
|
+
trackedFiles = execFileSync('git', ['ls-files'], { cwd: REPO_ROOT, encoding: 'utf8' })
|
|
99
|
+
.split('\n')
|
|
100
|
+
.map((f) => f.trim())
|
|
101
|
+
.filter(Boolean);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
throw new ExitError(1, 'ERROR lint-legacy-dir-name: git ls-files failed: ' + err.message);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const violations = [];
|
|
107
|
+
|
|
108
|
+
for (const relPath of trackedFiles) {
|
|
109
|
+
if (isAllowlisted(relPath)) continue;
|
|
110
|
+
|
|
111
|
+
const fullPath = path.join(REPO_ROOT, relPath);
|
|
112
|
+
|
|
113
|
+
// Skip this guard script itself.
|
|
114
|
+
if (path.resolve(fullPath) === SELF_PATH) continue;
|
|
115
|
+
|
|
116
|
+
if (isBinary(fullPath)) continue;
|
|
117
|
+
|
|
118
|
+
let content;
|
|
119
|
+
try {
|
|
120
|
+
content = fs.readFileSync(fullPath, 'utf8');
|
|
121
|
+
} catch {
|
|
122
|
+
// Unreadable files (permissions, etc.) — skip silently.
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const lines = content.split('\n');
|
|
127
|
+
for (let i = 0; i < lines.length; i++) {
|
|
128
|
+
const line = lines[i];
|
|
129
|
+
// Skip lines that carry the explicit allow marker.
|
|
130
|
+
if (line.includes(ALLOW_MARKER)) continue;
|
|
131
|
+
|
|
132
|
+
FORBIDDEN_RE.lastIndex = 0;
|
|
133
|
+
let match;
|
|
134
|
+
while ((match = FORBIDDEN_RE.exec(line)) !== null) {
|
|
135
|
+
violations.push({
|
|
136
|
+
file: relPath,
|
|
137
|
+
line: i + 1,
|
|
138
|
+
col: match.index + 1,
|
|
139
|
+
text: match[0],
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (violations.length === 0) {
|
|
146
|
+
process.stdout.write('ok lint-legacy-dir-name: ' + trackedFiles.length + ' tracked file(s) checked, 0 violations\n');
|
|
147
|
+
return 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
process.stderr.write('\nERROR lint-legacy-dir-name: ' + violations.length + ' violation(s) found\n\n');
|
|
151
|
+
for (const v of violations) {
|
|
152
|
+
process.stderr.write(' ' + v.file + ':' + v.line + ':' + v.col + ' — ' + JSON.stringify(v.text) + '\n');
|
|
153
|
+
}
|
|
154
|
+
process.stderr.write('\n');
|
|
155
|
+
process.stderr.write('Fix: rename to gsd-core, or add `gsd-allow-legacy-name` marker on the line if the\n');
|
|
156
|
+
process.stderr.write(' use is intentional (migration modules, tests, guard, changeset).\n\n');
|
|
157
|
+
return 1;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
runMain(main);
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Drift-guard lint for the Package Identity seam (issue #498).
|
|
6
|
+
*
|
|
7
|
+
* The seam (`get-shit-done/bin/lib/package-identity.cjs`, derived from // gsd-allow-legacy-name
|
|
8
|
+
* package.json) is the single source of GSD's published coordinates. Many
|
|
9
|
+
* runtime surfaces still carry a literal copy of those coordinates because
|
|
10
|
+
* they cannot `require()` the seam at runtime: the bash launcher snippet (and
|
|
11
|
+
* its byte-equal copies across ~85 workflows, kept in lockstep by the
|
|
12
|
+
* runtime-launcher parity test) and the installer's user-facing install/help
|
|
13
|
+
* strings.
|
|
14
|
+
*
|
|
15
|
+
* This lint makes those literals *value-checked*: every GSD package/repo
|
|
16
|
+
* coordinate that appears as a literal must equal the seam's current value.
|
|
17
|
+
* It passes today (the literals are correct) and FAILS the moment a repoint is
|
|
18
|
+
* not propagated — rename package.json, regenerate the seam, and every stale
|
|
19
|
+
* literal is reported until updated. That is what turns a repoint into a
|
|
20
|
+
* one-line change with mechanical enforcement.
|
|
21
|
+
*
|
|
22
|
+
* Scope: the runtime/code surface (bin/, hooks/, scripts/, get-shit-done/). // gsd-allow-legacy-name
|
|
23
|
+
* Pure-prose docs and localized READMEs are intentionally out of scope.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const fs = require('node:fs');
|
|
27
|
+
const path = require('node:path');
|
|
28
|
+
|
|
29
|
+
// A GSD package coordinate: a scoped npm name whose package part contains
|
|
30
|
+
// "get-shit-done" (so @opengsd/gsd-sdk and unrelated scopes never match). // gsd-allow-legacy-name
|
|
31
|
+
const PACKAGE_RE = /@[A-Za-z0-9._-]+\/[A-Za-z0-9._-]*get-shit-done[A-Za-z0-9._-]*/g; // gsd-allow-legacy-name
|
|
32
|
+
// A GSD repo slug, only inside a GitHub URL context so it never overlaps the
|
|
33
|
+
// scoped package literal above. The `.git` suffix is trimmed before compare.
|
|
34
|
+
const SLUG_RE = /(?:github\.com[/:]|raw\.githubusercontent\.com\/)([A-Za-z0-9._-]+\/[A-Za-z0-9._-]*get-shit-done[A-Za-z0-9._-]*)/g; // gsd-allow-legacy-name
|
|
35
|
+
|
|
36
|
+
function lineOf(text, index) {
|
|
37
|
+
let line = 1;
|
|
38
|
+
for (let i = 0; i < index && i < text.length; i++) {
|
|
39
|
+
if (text[i] === '\n') line++;
|
|
40
|
+
}
|
|
41
|
+
return line;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Pure: find every GSD coordinate literal in `text` that does not match the
|
|
46
|
+
* expected seam values. Returns [{ kind, found, expected, line }].
|
|
47
|
+
*/
|
|
48
|
+
function findCoordinateDrift(text, { packageName, repoSlug }) {
|
|
49
|
+
const out = [];
|
|
50
|
+
for (const m of text.matchAll(PACKAGE_RE)) {
|
|
51
|
+
if (m[0] !== packageName) {
|
|
52
|
+
out.push({ kind: 'package', found: m[0], expected: packageName, line: lineOf(text, m.index) });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
for (const m of text.matchAll(SLUG_RE)) {
|
|
56
|
+
const slug = m[1].replace(/\.git$/, '');
|
|
57
|
+
if (slug !== repoSlug) {
|
|
58
|
+
out.push({ kind: 'slug', found: slug, expected: repoSlug, line: lineOf(text, m.index) });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Directories scanned, relative to repo root.
|
|
65
|
+
const SCAN_DIRS = ['bin', 'hooks', 'scripts', 'gsd-core'];
|
|
66
|
+
const SCAN_EXT = new Set(['.js', '.cjs', '.sh', '.md']);
|
|
67
|
+
// Files exempt because they ARE the source of truth / the tooling that defines
|
|
68
|
+
// the coordinate patterns. The generated seam holds the correct value by
|
|
69
|
+
// construction; the generator and this lint carry regex/templates, not stray
|
|
70
|
+
// literals.
|
|
71
|
+
const EXEMPT = new Set([
|
|
72
|
+
path.join('gsd-core', 'bin', 'lib', 'package-identity.cjs'),
|
|
73
|
+
path.join('scripts', 'generate-package-identity.cjs'),
|
|
74
|
+
path.join('scripts', 'lint-package-identity-drift.cjs'),
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
function walk(dir, acc) {
|
|
78
|
+
let entries;
|
|
79
|
+
try {
|
|
80
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
81
|
+
} catch (e) {
|
|
82
|
+
return acc;
|
|
83
|
+
}
|
|
84
|
+
for (const entry of entries) {
|
|
85
|
+
const full = path.join(dir, entry.name);
|
|
86
|
+
if (entry.isDirectory()) {
|
|
87
|
+
if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === '.git') continue;
|
|
88
|
+
walk(full, acc);
|
|
89
|
+
} else if (entry.isFile() && SCAN_EXT.has(path.extname(entry.name))) {
|
|
90
|
+
acc.push(full);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return acc;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Scan the repo's runtime/code surface and return all coordinate drift, each
|
|
98
|
+
* annotated with the repo-relative file path.
|
|
99
|
+
*/
|
|
100
|
+
function scanRepo(root) {
|
|
101
|
+
const seam = require(path.join(root, 'gsd-core', 'bin', 'lib', 'package-identity.cjs'));
|
|
102
|
+
const expected = { packageName: seam.packageName, repoSlug: seam.repoSlug };
|
|
103
|
+
const violations = [];
|
|
104
|
+
for (const dir of SCAN_DIRS) {
|
|
105
|
+
const files = walk(path.join(root, dir), []);
|
|
106
|
+
for (const file of files) {
|
|
107
|
+
const rel = path.relative(root, file);
|
|
108
|
+
if (EXEMPT.has(rel)) continue;
|
|
109
|
+
let text;
|
|
110
|
+
try {
|
|
111
|
+
text = fs.readFileSync(file, 'utf8');
|
|
112
|
+
} catch (e) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
for (const d of findCoordinateDrift(text, expected)) {
|
|
116
|
+
violations.push({ file: rel, ...d });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return violations;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function main() {
|
|
124
|
+
const root = path.join(__dirname, '..');
|
|
125
|
+
const violations = scanRepo(root);
|
|
126
|
+
if (violations.length === 0) {
|
|
127
|
+
process.stdout.write('ok identity-drift: all GSD coordinate literals match the seam\n');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
process.stderr.write('identity-drift: stale GSD coordinate literal(s) found.\n');
|
|
131
|
+
process.stderr.write('Repoint by editing package.json, then `node scripts/generate-package-identity.cjs`,\n');
|
|
132
|
+
process.stderr.write('and update the value-checked materialization sites below:\n');
|
|
133
|
+
for (const d of violations) {
|
|
134
|
+
process.stderr.write(` ${d.file}:${d.line} ${d.kind} '${d.found}' != '${d.expected}'\n`);
|
|
135
|
+
}
|
|
136
|
+
process.exitCode = 1;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (require.main === module) main();
|
|
140
|
+
|
|
141
|
+
module.exports = { findCoordinateDrift, scanRepo };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const { runMain } = require('./lib/cli-exit.cjs');
|
|
8
|
+
|
|
9
|
+
const ROOT = path.join(__dirname, '..');
|
|
10
|
+
|
|
11
|
+
const DEFAULT_RELATIVE_FILES = [
|
|
12
|
+
'.github/workflows/test.yml',
|
|
13
|
+
'.github/workflows/pr-template-format.yml',
|
|
14
|
+
'.github/workflows/changeset-required.yml',
|
|
15
|
+
'scripts/lint-command-contract.cjs',
|
|
16
|
+
'scripts/lint-skill-deps.cjs',
|
|
17
|
+
'scripts/lint-descriptions.cjs',
|
|
18
|
+
'scripts/lint-shell-command-projection-drift.cjs',
|
|
19
|
+
'scripts/pr-template-policy.cjs',
|
|
20
|
+
'scripts/changeset/lint.cjs',
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
function defaultFiles(rootDir = ROOT) {
|
|
24
|
+
return DEFAULT_RELATIVE_FILES
|
|
25
|
+
.map((file) => path.join(rootDir, file))
|
|
26
|
+
.filter((file) => fs.existsSync(file));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function findForbiddenCwd(content, file = '<inline>') {
|
|
30
|
+
const findings = [];
|
|
31
|
+
const lines = content.split(/\r?\n/);
|
|
32
|
+
|
|
33
|
+
lines.forEach((line, index) => {
|
|
34
|
+
const pattern = /\bcwd\b/g;
|
|
35
|
+
let match;
|
|
36
|
+
while ((match = pattern.exec(line)) !== null) {
|
|
37
|
+
findings.push({
|
|
38
|
+
file,
|
|
39
|
+
line: index + 1,
|
|
40
|
+
column: match.index + 1,
|
|
41
|
+
source: line.trim(),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return findings;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function checkFiles(files, { rootDir = ROOT } = {}) {
|
|
50
|
+
const findings = [];
|
|
51
|
+
for (const file of files) {
|
|
52
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
53
|
+
const rel = path.relative(rootDir, file);
|
|
54
|
+
findings.push(...findForbiddenCwd(content, rel));
|
|
55
|
+
}
|
|
56
|
+
return findings;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function formatFindings(findings) {
|
|
60
|
+
const lines = [
|
|
61
|
+
`ERROR lint-pr-check-project-dir: ${findings.length} forbidden cwd reference(s) found`,
|
|
62
|
+
'',
|
|
63
|
+
'PR checkers must use projectDir for project roots; cwd is forbidden in this layer.',
|
|
64
|
+
'',
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
for (const finding of findings) {
|
|
68
|
+
lines.push(` ${finding.file}:${finding.line}:${finding.column}`);
|
|
69
|
+
lines.push(` ${finding.source}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return `${lines.join('\n')}\n`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function main(argv = process.argv.slice(2)) {
|
|
76
|
+
const files = argv.length > 0 ? argv.map((file) => path.resolve(file)) : defaultFiles();
|
|
77
|
+
const findings = checkFiles(files);
|
|
78
|
+
|
|
79
|
+
if (findings.length === 0) {
|
|
80
|
+
console.log(`ok lint-pr-check-project-dir: ${files.length} PR check files checked`);
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
process.stderr.write(formatFindings(findings));
|
|
85
|
+
return 1;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (require.main === module) {
|
|
89
|
+
runMain(main);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = {
|
|
93
|
+
DEFAULT_RELATIVE_FILES,
|
|
94
|
+
checkFiles,
|
|
95
|
+
defaultFiles,
|
|
96
|
+
findForbiddenCwd,
|
|
97
|
+
formatFindings,
|
|
98
|
+
main,
|
|
99
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Focused drift guard for issue #3442:
|
|
6
|
+
* prevent installer-owned inline shim/wrapper text builders from bypassing the
|
|
7
|
+
* Shell Command Projection Module seam.
|
|
8
|
+
*
|
|
9
|
+
* Scope intentionally excludes subprocess execution helpers (spawnSync /
|
|
10
|
+
* execFileSync) because those are safe internal execution primitives, not
|
|
11
|
+
* serialized shell-command rendering.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
17
|
+
|
|
18
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
19
|
+
|
|
20
|
+
const forbidden = [
|
|
21
|
+
{
|
|
22
|
+
label: 'inline cmd shim builder',
|
|
23
|
+
pattern: /@ECHO OFF\\r\\n@SETLOCAL\\r\\n@node /,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
label: 'inline pwsh shim builder',
|
|
27
|
+
pattern: /#!\/usr\/bin\/env pwsh\\n& node /,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
label: 'inline sh shim builder',
|
|
31
|
+
pattern: /#!\/usr\/bin\/env sh\\nexec node /,
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
function main() {
|
|
36
|
+
const targetArg = process.argv[2] || path.join(ROOT, 'bin', 'install.js');
|
|
37
|
+
const target = path.resolve(targetArg);
|
|
38
|
+
const rel = path.relative(ROOT, target);
|
|
39
|
+
|
|
40
|
+
let content;
|
|
41
|
+
try {
|
|
42
|
+
content = fs.readFileSync(target, 'utf8');
|
|
43
|
+
} catch (error) {
|
|
44
|
+
throw new ExitError(1, `lint-shell-command-projection-drift: failed to read ${target}: ${error.message}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const matches = forbidden.filter((rule) => rule.pattern.test(content));
|
|
48
|
+
if (matches.length === 0) {
|
|
49
|
+
process.stdout.write(`ok shell-projection-drift: ${rel}\n`);
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
process.stderr.write(`ERROR shell-projection-drift: inline serialized shim builders found in ${rel}\n`);
|
|
54
|
+
for (const match of matches) {
|
|
55
|
+
process.stderr.write(` - ${match.label}\n`);
|
|
56
|
+
}
|
|
57
|
+
process.stderr.write('Route shim/wrapper rendering through gsd-core/bin/lib/shell-command-projection.cjs\n');
|
|
58
|
+
process.stderr.write('Safe subprocess execution via spawnSync/execFileSync is intentionally allowed.\n');
|
|
59
|
+
return 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
runMain(main);
|