@opengsd/gsd-core 1.2.0-rc.1
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/LICENSE +21 -0
- package/README.ja-JP.md +870 -0
- package/README.ko-KR.md +861 -0
- package/README.md +301 -0
- package/README.pt-BR.md +492 -0
- package/README.zh-CN.md +842 -0
- package/agents/gsd-advisor-researcher.md +127 -0
- package/agents/gsd-ai-researcher.md +133 -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 +615 -0
- package/agents/gsd-domain-researcher.md +153 -0
- package/agents/gsd-eval-auditor.md +191 -0
- package/agents/gsd-eval-planner.md +154 -0
- package/agents/gsd-executor.md +772 -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 +928 -0
- package/agents/gsd-plan-checker.md +978 -0
- package/agents/gsd-planner.md +1218 -0
- package/agents/gsd-project-researcher.md +677 -0
- package/agents/gsd-research-synthesizer.md +255 -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 +380 -0
- package/agents/gsd-user-profiler.md +171 -0
- package/agents/gsd-verifier.md +917 -0
- package/bin/install.js +10936 -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 +46 -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/discuss-phase.md +76 -0
- package/commands/gsd/docs-update.md +49 -0
- package/commands/gsd/eval-review.md +33 -0
- package/commands/gsd/execute-phase.md +64 -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 +199 -0
- package/commands/gsd/health.md +31 -0
- package/commands/gsd/help.md +28 -0
- package/commands/gsd/import.md +41 -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/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 +62 -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 +47 -0
- package/commands/gsd/quick.md +174 -0
- package/commands/gsd/resume-work.md +30 -0
- package/commands/gsd/review-backlog.md +63 -0
- package/commands/gsd/review.md +41 -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 +19 -0
- package/commands/gsd/surface.md +155 -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 +48 -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/get-shit-done/bin/check-latest-version.cjs +106 -0
- package/get-shit-done/bin/gsd-tools.cjs +1676 -0
- package/get-shit-done/bin/lib/active-workstream-store.cjs +302 -0
- package/get-shit-done/bin/lib/adr-parser.cjs +394 -0
- package/get-shit-done/bin/lib/agent-command-router.cjs +65 -0
- package/get-shit-done/bin/lib/artifacts.cjs +53 -0
- package/get-shit-done/bin/lib/audit.cjs +755 -0
- package/get-shit-done/bin/lib/check-command-router.cjs +333 -0
- package/get-shit-done/bin/lib/cjs-command-router-adapter.cjs +118 -0
- package/get-shit-done/bin/lib/clock.cjs +96 -0
- package/get-shit-done/bin/lib/clusters.cjs +135 -0
- package/get-shit-done/bin/lib/code-review-flags.cjs +74 -0
- package/get-shit-done/bin/lib/command-aliases.cjs +815 -0
- package/get-shit-done/bin/lib/command-arg-projection.cjs +62 -0
- package/get-shit-done/bin/lib/command-routing-hub.cjs +388 -0
- package/get-shit-done/bin/lib/commands.cjs +1188 -0
- package/get-shit-done/bin/lib/config-schema.cjs +31 -0
- package/get-shit-done/bin/lib/config.cjs +728 -0
- package/get-shit-done/bin/lib/configuration.cjs +248 -0
- package/get-shit-done/bin/lib/context-utilization.cjs +47 -0
- package/get-shit-done/bin/lib/core.cjs +2121 -0
- package/get-shit-done/bin/lib/decisions.cjs +116 -0
- package/get-shit-done/bin/lib/docs.cjs +270 -0
- package/get-shit-done/bin/lib/drift.cjs +388 -0
- package/get-shit-done/bin/lib/fallow-runner.cjs +109 -0
- package/get-shit-done/bin/lib/frontmatter.cjs +389 -0
- package/get-shit-done/bin/lib/gap-checker.cjs +205 -0
- package/get-shit-done/bin/lib/graphify.cjs +592 -0
- package/get-shit-done/bin/lib/gsd2-import.cjs +514 -0
- package/get-shit-done/bin/lib/init-command-router.cjs +58 -0
- package/get-shit-done/bin/lib/init.cjs +2112 -0
- package/get-shit-done/bin/lib/install-profiles.cjs +603 -0
- package/get-shit-done/bin/lib/installer-migration-authoring.cjs +117 -0
- package/get-shit-done/bin/lib/installer-migration-report.cjs +354 -0
- package/get-shit-done/bin/lib/installer-migrations/000-first-time-baseline.cjs +220 -0
- package/get-shit-done/bin/lib/installer-migrations/001-legacy-orphan-files.cjs +41 -0
- package/get-shit-done/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs +80 -0
- package/get-shit-done/bin/lib/installer-migrations.cjs +778 -0
- package/get-shit-done/bin/lib/intel.cjs +708 -0
- package/get-shit-done/bin/lib/learnings.cjs +421 -0
- package/get-shit-done/bin/lib/milestone.cjs +314 -0
- package/get-shit-done/bin/lib/model-catalog.cjs +212 -0
- package/get-shit-done/bin/lib/model-profiles.cjs +31 -0
- package/get-shit-done/bin/lib/observability/event.cjs +82 -0
- package/get-shit-done/bin/lib/observability/logger.cjs +174 -0
- package/get-shit-done/bin/lib/observability/redaction.cjs +50 -0
- package/get-shit-done/bin/lib/package-identity.cjs +31 -0
- package/get-shit-done/bin/lib/phase-command-router.cjs +191 -0
- package/get-shit-done/bin/lib/phase-lifecycle.cjs +80 -0
- package/get-shit-done/bin/lib/phase.cjs +1607 -0
- package/get-shit-done/bin/lib/phases-command-router.cjs +39 -0
- package/get-shit-done/bin/lib/plan-scan.cjs +97 -0
- package/get-shit-done/bin/lib/planning-workspace.cjs +238 -0
- package/get-shit-done/bin/lib/profile-output.cjs +1141 -0
- package/get-shit-done/bin/lib/profile-pipeline.cjs +539 -0
- package/get-shit-done/bin/lib/project-root.cjs +112 -0
- package/get-shit-done/bin/lib/prompt-budget.cjs +399 -0
- package/get-shit-done/bin/lib/review-reviewer-selection.cjs +125 -0
- package/get-shit-done/bin/lib/roadmap-command-router.cjs +28 -0
- package/get-shit-done/bin/lib/roadmap.cjs +650 -0
- package/get-shit-done/bin/lib/runtime-artifact-layout.cjs +301 -0
- package/get-shit-done/bin/lib/runtime-homes.cjs +222 -0
- package/get-shit-done/bin/lib/runtime-name-policy.cjs +83 -0
- package/get-shit-done/bin/lib/runtime-slash.cjs +112 -0
- package/get-shit-done/bin/lib/schema-detect.cjs +165 -0
- package/get-shit-done/bin/lib/secrets.cjs +32 -0
- package/get-shit-done/bin/lib/security.cjs +600 -0
- package/get-shit-done/bin/lib/semver-compare.cjs +35 -0
- package/get-shit-done/bin/lib/shell-command-projection.cjs +500 -0
- package/get-shit-done/bin/lib/state-command-router.cjs +252 -0
- package/get-shit-done/bin/lib/state-document.cjs +263 -0
- package/get-shit-done/bin/lib/state.cjs +2038 -0
- package/get-shit-done/bin/lib/surface.cjs +470 -0
- package/get-shit-done/bin/lib/task-command-router.cjs +81 -0
- package/get-shit-done/bin/lib/template.cjs +228 -0
- package/get-shit-done/bin/lib/uat.cjs +289 -0
- package/get-shit-done/bin/lib/update-context.cjs +209 -0
- package/get-shit-done/bin/lib/validate-command-router.cjs +83 -0
- package/get-shit-done/bin/lib/validate.cjs +92 -0
- package/get-shit-done/bin/lib/verify-command-router.cjs +40 -0
- package/get-shit-done/bin/lib/verify.cjs +1511 -0
- package/get-shit-done/bin/lib/workstream-inventory-builder.cjs +74 -0
- package/get-shit-done/bin/lib/workstream-inventory.cjs +146 -0
- package/get-shit-done/bin/lib/workstream-name-policy.cjs +94 -0
- package/get-shit-done/bin/lib/workstream.cjs +389 -0
- package/get-shit-done/bin/lib/worktree-safety.cjs +985 -0
- package/get-shit-done/bin/shared/config-defaults.manifest.json +97 -0
- package/get-shit-done/bin/shared/config-schema.manifest.json +175 -0
- package/get-shit-done/bin/shared/model-catalog.json +122 -0
- package/get-shit-done/bin/shared/runtime-aliases.manifest.json +75 -0
- package/get-shit-done/bin/verify-reapply-patches.cjs +352 -0
- package/get-shit-done/contexts/dev.md +21 -0
- package/get-shit-done/contexts/research.md +22 -0
- package/get-shit-done/contexts/review.md +23 -0
- package/get-shit-done/references/agent-contracts.md +79 -0
- package/get-shit-done/references/ai-evals.md +156 -0
- package/get-shit-done/references/ai-frameworks.md +186 -0
- package/get-shit-done/references/artifact-types.md +131 -0
- package/get-shit-done/references/autonomous-smart-discuss.md +277 -0
- package/get-shit-done/references/checkpoints.md +814 -0
- package/get-shit-done/references/common-bug-patterns.md +114 -0
- package/get-shit-done/references/context-budget.md +85 -0
- package/get-shit-done/references/continuation-format.md +253 -0
- package/get-shit-done/references/debugger-philosophy.md +76 -0
- package/get-shit-done/references/decimal-phase-calculation.md +64 -0
- package/get-shit-done/references/doc-conflict-engine.md +91 -0
- package/get-shit-done/references/domain-probes.md +125 -0
- package/get-shit-done/references/execute-mvp-tdd.md +81 -0
- package/get-shit-done/references/executor-examples.md +110 -0
- package/get-shit-done/references/few-shot-examples/plan-checker.md +73 -0
- package/get-shit-done/references/few-shot-examples/verifier.md +109 -0
- package/get-shit-done/references/gate-prompts.md +100 -0
- package/get-shit-done/references/gates.md +70 -0
- package/get-shit-done/references/git-integration.md +298 -0
- package/get-shit-done/references/git-planning-commit.md +40 -0
- package/get-shit-done/references/ios-scaffold.md +123 -0
- package/get-shit-done/references/mandatory-initial-read.md +2 -0
- package/get-shit-done/references/model-profile-resolution.md +38 -0
- package/get-shit-done/references/model-profiles.md +245 -0
- package/get-shit-done/references/mvp-concepts.md +49 -0
- package/get-shit-done/references/phase-argument-parsing.md +61 -0
- package/get-shit-done/references/planner-antipatterns.md +89 -0
- package/get-shit-done/references/planner-chunked.md +49 -0
- package/get-shit-done/references/planner-gap-closure.md +62 -0
- package/get-shit-done/references/planner-graphify-auto-update.md +67 -0
- package/get-shit-done/references/planner-human-verify-mode.md +57 -0
- package/get-shit-done/references/planner-interface-context.md +62 -0
- package/get-shit-done/references/planner-mvp-mode.md +53 -0
- package/get-shit-done/references/planner-reviews.md +39 -0
- package/get-shit-done/references/planner-revision.md +87 -0
- package/get-shit-done/references/planner-source-audit.md +73 -0
- package/get-shit-done/references/planning-config.md +471 -0
- package/get-shit-done/references/project-skills-discovery.md +19 -0
- package/get-shit-done/references/questioning.md +162 -0
- package/get-shit-done/references/revision-loop.md +97 -0
- package/get-shit-done/references/scout-codebase.md +51 -0
- package/get-shit-done/references/skeleton-template.md +48 -0
- package/get-shit-done/references/sketch-interactivity.md +41 -0
- package/get-shit-done/references/sketch-theme-system.md +94 -0
- package/get-shit-done/references/sketch-tooling.md +45 -0
- package/get-shit-done/references/sketch-variant-patterns.md +81 -0
- package/get-shit-done/references/spidr-splitting.md +69 -0
- package/get-shit-done/references/tdd.md +330 -0
- package/get-shit-done/references/thinking-models-debug.md +44 -0
- package/get-shit-done/references/thinking-models-execution.md +50 -0
- package/get-shit-done/references/thinking-models-planning.md +62 -0
- package/get-shit-done/references/thinking-models-research.md +50 -0
- package/get-shit-done/references/thinking-models-verification.md +55 -0
- package/get-shit-done/references/thinking-partner.md +96 -0
- package/get-shit-done/references/ui-brand.md +160 -0
- package/get-shit-done/references/universal-anti-patterns.md +63 -0
- package/get-shit-done/references/user-profiling.md +681 -0
- package/get-shit-done/references/user-story-template.md +58 -0
- package/get-shit-done/references/verification-overrides.md +227 -0
- package/get-shit-done/references/verification-patterns.md +612 -0
- package/get-shit-done/references/verify-mvp-mode.md +85 -0
- package/get-shit-done/references/workstream-flag.md +111 -0
- package/get-shit-done/references/worktree-path-safety.md +89 -0
- package/get-shit-done/templates/AI-SPEC.md +246 -0
- package/get-shit-done/templates/DEBUG.md +169 -0
- package/get-shit-done/templates/README.md +77 -0
- package/get-shit-done/templates/SECURITY.md +61 -0
- package/get-shit-done/templates/UAT.md +265 -0
- package/get-shit-done/templates/UI-SPEC.md +100 -0
- package/get-shit-done/templates/VALIDATION.md +76 -0
- package/get-shit-done/templates/claude-md.md +145 -0
- package/get-shit-done/templates/codebase/architecture.md +255 -0
- package/get-shit-done/templates/codebase/concerns.md +310 -0
- package/get-shit-done/templates/codebase/conventions.md +307 -0
- package/get-shit-done/templates/codebase/integrations.md +280 -0
- package/get-shit-done/templates/codebase/stack.md +186 -0
- package/get-shit-done/templates/codebase/structure.md +285 -0
- package/get-shit-done/templates/codebase/testing.md +480 -0
- package/get-shit-done/templates/config.json +62 -0
- package/get-shit-done/templates/context.md +352 -0
- package/get-shit-done/templates/continue-here.md +78 -0
- package/get-shit-done/templates/copilot-instructions.md +7 -0
- package/get-shit-done/templates/debug-subagent-prompt.md +91 -0
- package/get-shit-done/templates/dev-preferences.md +21 -0
- package/get-shit-done/templates/discovery.md +146 -0
- package/get-shit-done/templates/discussion-log.md +63 -0
- package/get-shit-done/templates/milestone-archive.md +123 -0
- package/get-shit-done/templates/milestone.md +115 -0
- package/get-shit-done/templates/phase-prompt.md +610 -0
- package/get-shit-done/templates/planner-subagent-prompt.md +117 -0
- package/get-shit-done/templates/project.md +186 -0
- package/get-shit-done/templates/requirements.md +231 -0
- package/get-shit-done/templates/research-project/ARCHITECTURE.md +204 -0
- package/get-shit-done/templates/research-project/FEATURES.md +147 -0
- package/get-shit-done/templates/research-project/PITFALLS.md +200 -0
- package/get-shit-done/templates/research-project/STACK.md +120 -0
- package/get-shit-done/templates/research-project/SUMMARY.md +170 -0
- package/get-shit-done/templates/research.md +592 -0
- package/get-shit-done/templates/retrospective.md +54 -0
- package/get-shit-done/templates/roadmap.md +202 -0
- package/get-shit-done/templates/spec.md +307 -0
- package/get-shit-done/templates/state.md +195 -0
- package/get-shit-done/templates/summary-complex.md +59 -0
- package/get-shit-done/templates/summary-minimal.md +41 -0
- package/get-shit-done/templates/summary-standard.md +48 -0
- package/get-shit-done/templates/summary.md +248 -0
- package/get-shit-done/templates/user-profile.md +146 -0
- package/get-shit-done/templates/user-setup.md +311 -0
- package/get-shit-done/templates/verification-report.md +322 -0
- package/get-shit-done/workflows/_runtime-launcher.snippet.sh +1 -0
- package/get-shit-done/workflows/add-backlog.md +91 -0
- package/get-shit-done/workflows/add-phase.md +113 -0
- package/get-shit-done/workflows/add-tests.md +355 -0
- package/get-shit-done/workflows/add-todo.md +161 -0
- package/get-shit-done/workflows/ai-integration-phase.md +295 -0
- package/get-shit-done/workflows/analyze-dependencies.md +96 -0
- package/get-shit-done/workflows/audit-fix.md +178 -0
- package/get-shit-done/workflows/audit-milestone.md +358 -0
- package/get-shit-done/workflows/audit-uat.md +110 -0
- package/get-shit-done/workflows/autonomous.md +795 -0
- package/get-shit-done/workflows/check-todos.md +180 -0
- package/get-shit-done/workflows/cleanup.md +155 -0
- package/get-shit-done/workflows/code-review-fix.md +502 -0
- package/get-shit-done/workflows/code-review.md +656 -0
- package/get-shit-done/workflows/complete-milestone.md +855 -0
- package/get-shit-done/workflows/debug.md +232 -0
- package/get-shit-done/workflows/diagnose-issues.md +241 -0
- package/get-shit-done/workflows/discovery-phase.md +291 -0
- package/get-shit-done/workflows/discuss-phase/modes/advisor.md +176 -0
- package/get-shit-done/workflows/discuss-phase/modes/all.md +28 -0
- package/get-shit-done/workflows/discuss-phase/modes/analyze.md +44 -0
- package/get-shit-done/workflows/discuss-phase/modes/auto.md +57 -0
- package/get-shit-done/workflows/discuss-phase/modes/batch.md +52 -0
- package/get-shit-done/workflows/discuss-phase/modes/chain.md +98 -0
- package/get-shit-done/workflows/discuss-phase/modes/default.md +141 -0
- package/get-shit-done/workflows/discuss-phase/modes/power.md +44 -0
- package/get-shit-done/workflows/discuss-phase/modes/text.md +55 -0
- package/get-shit-done/workflows/discuss-phase/templates/checkpoint.json +18 -0
- package/get-shit-done/workflows/discuss-phase/templates/context.md +136 -0
- package/get-shit-done/workflows/discuss-phase/templates/discussion-log.md +50 -0
- package/get-shit-done/workflows/discuss-phase-assumptions.md +675 -0
- package/get-shit-done/workflows/discuss-phase-power.md +291 -0
- package/get-shit-done/workflows/discuss-phase.md +499 -0
- package/get-shit-done/workflows/do.md +111 -0
- package/get-shit-done/workflows/docs-update.md +1162 -0
- package/get-shit-done/workflows/edit-phase.md +295 -0
- package/get-shit-done/workflows/eval-review.md +156 -0
- package/get-shit-done/workflows/execute-phase/steps/codebase-drift-gate.md +82 -0
- package/get-shit-done/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
- package/get-shit-done/workflows/execute-phase/steps/post-merge-gate.md +117 -0
- package/get-shit-done/workflows/execute-phase.md +1709 -0
- package/get-shit-done/workflows/execute-plan.md +526 -0
- package/get-shit-done/workflows/explore.md +144 -0
- package/get-shit-done/workflows/extract-learnings.md +243 -0
- package/get-shit-done/workflows/fast.md +124 -0
- package/get-shit-done/workflows/forensics.md +279 -0
- package/get-shit-done/workflows/graduation.md +196 -0
- package/get-shit-done/workflows/health.md +224 -0
- package/get-shit-done/workflows/help/modes/brief.md +22 -0
- package/get-shit-done/workflows/help/modes/default.md +50 -0
- package/get-shit-done/workflows/help/modes/full.md +784 -0
- package/get-shit-done/workflows/help/modes/topic.md +74 -0
- package/get-shit-done/workflows/help.md +24 -0
- package/get-shit-done/workflows/import.md +254 -0
- package/get-shit-done/workflows/inbox.md +387 -0
- package/get-shit-done/workflows/ingest-docs.md +339 -0
- package/get-shit-done/workflows/insert-phase.md +152 -0
- package/get-shit-done/workflows/list-phase-assumptions.md +178 -0
- package/get-shit-done/workflows/list-workspaces.md +57 -0
- package/get-shit-done/workflows/manager.md +393 -0
- package/get-shit-done/workflows/map-codebase.md +444 -0
- package/get-shit-done/workflows/milestone-summary.md +224 -0
- package/get-shit-done/workflows/mvp-phase.md +222 -0
- package/get-shit-done/workflows/new-milestone.md +635 -0
- package/get-shit-done/workflows/new-project.md +1555 -0
- package/get-shit-done/workflows/new-workspace.md +240 -0
- package/get-shit-done/workflows/next.md +299 -0
- package/get-shit-done/workflows/node-repair.md +92 -0
- package/get-shit-done/workflows/note.md +158 -0
- package/get-shit-done/workflows/pause-work.md +244 -0
- package/get-shit-done/workflows/plan-milestone-gaps.md +281 -0
- package/get-shit-done/workflows/plan-phase.md +1809 -0
- package/get-shit-done/workflows/plan-review-convergence.md +346 -0
- package/get-shit-done/workflows/plant-seed.md +230 -0
- package/get-shit-done/workflows/pr-branch.md +157 -0
- package/get-shit-done/workflows/profile-user.md +453 -0
- package/get-shit-done/workflows/progress.md +699 -0
- package/get-shit-done/workflows/quick.md +1039 -0
- package/get-shit-done/workflows/reapply-patches.md +426 -0
- package/get-shit-done/workflows/remove-phase.md +156 -0
- package/get-shit-done/workflows/remove-workspace.md +108 -0
- package/get-shit-done/workflows/resume-project.md +332 -0
- package/get-shit-done/workflows/review.md +623 -0
- package/get-shit-done/workflows/scan.md +105 -0
- package/get-shit-done/workflows/secure-phase.md +180 -0
- package/get-shit-done/workflows/session-report.md +146 -0
- package/get-shit-done/workflows/settings-advanced.md +620 -0
- package/get-shit-done/workflows/settings-integrations.md +312 -0
- package/get-shit-done/workflows/settings.md +552 -0
- package/get-shit-done/workflows/ship.md +356 -0
- package/get-shit-done/workflows/sketch-wrap-up.md +286 -0
- package/get-shit-done/workflows/sketch.md +361 -0
- package/get-shit-done/workflows/spec-phase.md +262 -0
- package/get-shit-done/workflows/spike-wrap-up.md +307 -0
- package/get-shit-done/workflows/spike.md +453 -0
- package/get-shit-done/workflows/stats.md +80 -0
- package/get-shit-done/workflows/sync-skills.md +182 -0
- package/get-shit-done/workflows/thread.md +222 -0
- package/get-shit-done/workflows/transition.md +694 -0
- package/get-shit-done/workflows/ui-phase.md +328 -0
- package/get-shit-done/workflows/ui-review.md +193 -0
- package/get-shit-done/workflows/ultraplan-phase.md +199 -0
- package/get-shit-done/workflows/undo.md +314 -0
- package/get-shit-done/workflows/update.md +443 -0
- package/get-shit-done/workflows/validate-phase.md +179 -0
- package/get-shit-done/workflows/verify-phase.md +544 -0
- package/get-shit-done/workflows/verify-work.md +781 -0
- package/hooks/dist/gsd-check-update-worker.js +95 -0
- package/hooks/dist/gsd-check-update.js +64 -0
- package/hooks/dist/gsd-context-monitor.js +195 -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 +548 -0
- package/hooks/dist/gsd-update-banner.js +134 -0
- package/hooks/dist/gsd-validate-commit.sh +57 -0
- package/hooks/dist/gsd-workflow-guard.js +166 -0
- package/hooks/dist/lib/git-cmd.js +150 -0
- package/hooks/dist/lib/gsd-graphify-rebuild.sh +65 -0
- package/hooks/gsd-check-update-worker.js +95 -0
- package/hooks/gsd-check-update.js +64 -0
- package/hooks/gsd-context-monitor.js +195 -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 +548 -0
- package/hooks/gsd-update-banner.js +134 -0
- package/hooks/gsd-validate-commit.sh +57 -0
- package/hooks/gsd-workflow-guard.js +166 -0
- package/hooks/lib/git-cmd.js +150 -0
- package/hooks/lib/gsd-graphify-rebuild.sh +65 -0
- package/hooks/managed-hooks-registry.cjs +34 -0
- package/package.json +102 -0
- package/scripts/affected-tests-lib.cjs +541 -0
- package/scripts/audit-workflow-script-paths.cjs +73 -0
- package/scripts/base64-scan.sh +339 -0
- package/scripts/build-hooks.js +236 -0
- package/scripts/changeset/README.md +129 -0
- package/scripts/changeset/cli.cjs +392 -0
- package/scripts/changeset/github-release-notes.cjs +199 -0
- package/scripts/changeset/lint.cjs +110 -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 +108 -0
- package/scripts/check-env.cjs +302 -0
- package/scripts/check-npm-integrity.cjs +209 -0
- package/scripts/ci-guard-runner.cjs +16 -0
- package/scripts/ci-prepare-test-scope.cjs +46 -0
- package/scripts/ci-rebase-check.cjs +85 -0
- package/scripts/ci-test-scope.cjs +302 -0
- package/scripts/command-contract-helpers.cjs +64 -0
- package/scripts/diff-touches-shipped-paths.cjs +147 -0
- package/scripts/fix-slash-commands.cjs +147 -0
- package/scripts/gen-inventory-manifest.cjs +109 -0
- package/scripts/generate-package-identity.cjs +104 -0
- package/scripts/lint-command-contract.cjs +108 -0
- package/scripts/lint-descriptions.cjs +83 -0
- package/scripts/lint-docs-required.cjs +222 -0
- package/scripts/lint-no-source-grep-extras.cjs +81 -0
- package/scripts/lint-no-source-grep.cjs +174 -0
- package/scripts/lint-package-identity-drift.cjs +141 -0
- package/scripts/lint-pr-check-project-dir.cjs +98 -0
- package/scripts/lint-shared-module-handsync.cjs +388 -0
- package/scripts/lint-shell-command-projection-drift.cjs +57 -0
- package/scripts/lint-skill-deps.cjs +180 -0
- package/scripts/lint-test-file-count.allowlist.json +36 -0
- package/scripts/lint-test-file-count.cjs +190 -0
- package/scripts/pr-template-policy.cjs +268 -0
- package/scripts/prompt-injection-scan.sh +203 -0
- package/scripts/release-tarball-smoke.cjs +627 -0
- package/scripts/run-affected-tests.cjs +6 -0
- package/scripts/run-cross-platform-tests.cjs +63 -0
- package/scripts/run-tests.cjs +282 -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/shared-module-handsync-allowlist.json +183 -0
- package/scripts/strip-prose-atrefs.cjs +106 -0
- package/scripts/sync-rulesets.sh +34 -0
- package/scripts/sync-runtime-launcher.cjs +402 -0
- package/scripts/test-failure-reasons.cjs +34 -0
- package/scripts/workflow-policy.cjs +450 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* lint-skill-deps.cjs
|
|
4
|
+
*
|
|
5
|
+
* Two checks:
|
|
6
|
+
*
|
|
7
|
+
* a) Frontmatter to body consistency:
|
|
8
|
+
* For each commands/gsd/*.md, parse requires: and walk the body for
|
|
9
|
+
* references to other GSD skills (pattern: /gsd:<stem> or gsd:<stem>).
|
|
10
|
+
* Fail if a skill body references a skill not listed in requires:.
|
|
11
|
+
*
|
|
12
|
+
* b) Profile closure satisfaction:
|
|
13
|
+
* Load PROFILES from install-profiles.cjs. For each non-full profile,
|
|
14
|
+
* compute closure(profile.base) using the manifest. If any skill in
|
|
15
|
+
* the closure references a skill NOT in the closure, fail.
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* node scripts/lint-skill-deps.cjs # scans commands/gsd/
|
|
19
|
+
* node scripts/lint-skill-deps.cjs --dir <path> # scan a custom dir (testing)
|
|
20
|
+
*
|
|
21
|
+
* Exits 0 if all pass; exits 1 if any violation.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
'use strict';
|
|
25
|
+
|
|
26
|
+
const fs = require('fs');
|
|
27
|
+
const path = require('path');
|
|
28
|
+
|
|
29
|
+
const PROFILES_MODULE = path.join(__dirname, '..', 'get-shit-done', 'bin', 'lib', 'install-profiles.cjs');
|
|
30
|
+
const { PROFILES, loadSkillsManifest, resolveProfile } = require(PROFILES_MODULE);
|
|
31
|
+
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Argument parsing
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
let commandsDir = path.join(__dirname, '..', 'commands', 'gsd');
|
|
37
|
+
const args = process.argv.slice(2);
|
|
38
|
+
for (let i = 0; i < args.length; i++) {
|
|
39
|
+
if (args[i] === '--dir' && args[i + 1]) {
|
|
40
|
+
commandsDir = args[i + 1];
|
|
41
|
+
i++;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Reference extraction from skill body
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
function extractBodyReferences(body) {
|
|
50
|
+
const refs = new Set();
|
|
51
|
+
const re = /(?:\/?)gsd:([a-z0-9_-]+)/g;
|
|
52
|
+
let m;
|
|
53
|
+
while ((m = re.exec(body)) !== null) {
|
|
54
|
+
refs.add(m[1]);
|
|
55
|
+
}
|
|
56
|
+
return refs;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function extractBody(content) {
|
|
60
|
+
const fmEnd = content.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n/m);
|
|
61
|
+
if (!fmEnd) return content;
|
|
62
|
+
return content.slice(fmEnd[0].length);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Check a: frontmatter to body consistency
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
function checkFrontmatterBodyConsistency(manifest, allStems) {
|
|
70
|
+
const violations = [];
|
|
71
|
+
|
|
72
|
+
for (const [stem, declared] of manifest) {
|
|
73
|
+
if (stem.startsWith('_calls_agents_')) continue;
|
|
74
|
+
const filePath = path.join(commandsDir, stem + '.md');
|
|
75
|
+
let content;
|
|
76
|
+
try {
|
|
77
|
+
content = fs.readFileSync(filePath, 'utf8');
|
|
78
|
+
} catch {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const body = extractBody(content);
|
|
82
|
+
const referenced = extractBodyReferences(body);
|
|
83
|
+
const declaredSet = new Set(declared);
|
|
84
|
+
for (const ref of referenced) {
|
|
85
|
+
if (ref === stem) continue;
|
|
86
|
+
if (!allStems.has(ref)) {
|
|
87
|
+
violations.push({
|
|
88
|
+
stem,
|
|
89
|
+
filePath,
|
|
90
|
+
undeclared: ref,
|
|
91
|
+
message: 'body references unknown skill gsd:' + ref,
|
|
92
|
+
});
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (!declaredSet.has(ref)) {
|
|
96
|
+
violations.push({
|
|
97
|
+
stem,
|
|
98
|
+
filePath,
|
|
99
|
+
undeclared: ref,
|
|
100
|
+
message: 'body references gsd:' + ref + ' but requires: does not list it',
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return violations;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Check b: profile closure satisfaction
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
function checkProfileClosure(manifest) {
|
|
114
|
+
const violations = [];
|
|
115
|
+
|
|
116
|
+
for (const profileName of Object.keys(PROFILES)) {
|
|
117
|
+
const base = PROFILES[profileName];
|
|
118
|
+
if (base === '*') continue;
|
|
119
|
+
const resolved = resolveProfile({ modes: [profileName], manifest });
|
|
120
|
+
if (resolved.skills === '*') continue;
|
|
121
|
+
|
|
122
|
+
const closure = resolved.skills;
|
|
123
|
+
for (const stem of closure) {
|
|
124
|
+
const deps = manifest.get(stem) || [];
|
|
125
|
+
for (const dep of deps) {
|
|
126
|
+
if (!closure.has(dep)) {
|
|
127
|
+
violations.push({
|
|
128
|
+
profile: profileName,
|
|
129
|
+
stem,
|
|
130
|
+
missingDep: dep,
|
|
131
|
+
message: 'profile "' + profileName + '" includes "' + stem + '" which requires "' + dep + '", but "' + dep + '" is not in the closure',
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return violations;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// Main
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
const manifest = loadSkillsManifest(commandsDir);
|
|
146
|
+
const allStems = new Set(
|
|
147
|
+
[...manifest.keys()].filter((k) => !k.startsWith('_calls_agents_'))
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const consistencyViolations = checkFrontmatterBodyConsistency(manifest, allStems);
|
|
151
|
+
const closureViolations = checkProfileClosure(manifest);
|
|
152
|
+
|
|
153
|
+
const totalViolations = consistencyViolations.length + closureViolations.length;
|
|
154
|
+
|
|
155
|
+
if (totalViolations === 0) {
|
|
156
|
+
const checked = manifest.size;
|
|
157
|
+
process.stdout.write('ok lint-skill-deps: ' + checked + ' skill(s) checked, 0 violations\n');
|
|
158
|
+
process.exit(0);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
process.stderr.write('\nERROR lint-skill-deps: ' + totalViolations + ' violation(s) found\n\n');
|
|
162
|
+
|
|
163
|
+
if (consistencyViolations.length > 0) {
|
|
164
|
+
process.stderr.write('Frontmatter to body consistency violations (' + consistencyViolations.length + '):\n\n');
|
|
165
|
+
for (const v of consistencyViolations) {
|
|
166
|
+
process.stderr.write(' ' + v.filePath + '\n');
|
|
167
|
+
process.stderr.write(' ' + v.message + '\n\n');
|
|
168
|
+
}
|
|
169
|
+
process.stderr.write('Fix: add missing deps to requires: in the skill frontmatter.\n\n');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (closureViolations.length > 0) {
|
|
173
|
+
process.stderr.write('Profile closure violations (' + closureViolations.length + '):\n\n');
|
|
174
|
+
for (const v of closureViolations) {
|
|
175
|
+
process.stderr.write(' ' + v.message + '\n');
|
|
176
|
+
}
|
|
177
|
+
process.stderr.write('\nFix: add the missing skills to the profile base set in install-profiles.cjs.\n\n');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
process.exit(1);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_doc": "Baseline of modules currently exceeding the 2-test-file limit. Each entry locks in TODAY's count as the ceiling. Reductions are ratcheted automatically — when a cluster drops to ≤ 2, remove its entry. New entries require justification in PR description.",
|
|
3
|
+
"modules": {
|
|
4
|
+
"phase": { "current": 5, "issue": "3788" },
|
|
5
|
+
"worktree": { "current": 13, "issue": "TBD" },
|
|
6
|
+
"milestone": { "current": 10, "issue": "TBD" },
|
|
7
|
+
"roadmap": { "current": 9, "issue": "TBD" },
|
|
8
|
+
"verify": { "current": 10, "issue": "3767" },
|
|
9
|
+
"install": { "current": 9, "issue": "TBD" },
|
|
10
|
+
"init": { "current": 8, "issue": "TBD" },
|
|
11
|
+
"state": { "current": 11, "issue": "180" },
|
|
12
|
+
"config": { "current": 8, "issue": "TBD" },
|
|
13
|
+
"graphify": { "current": 7, "issue": "TBD" },
|
|
14
|
+
"progress": { "current": 6, "issue": "14" },
|
|
15
|
+
"cli": { "current": 5, "issue": "TBD" },
|
|
16
|
+
"surface": { "current": 5, "issue": "TBD" },
|
|
17
|
+
"commit": { "current": 4, "issue": "TBD" },
|
|
18
|
+
"frontmatter": { "current": 4, "issue": "TBD" },
|
|
19
|
+
"index": { "current": 5, "issue": "180" },
|
|
20
|
+
"intel": { "current": 4, "issue": "TBD" },
|
|
21
|
+
"mvp": { "current": 4, "issue": "TBD" },
|
|
22
|
+
"install-profiles": { "current": 4, "issue": "TBD" },
|
|
23
|
+
"audit": { "current": 4, "issue": "192" },
|
|
24
|
+
"audit-open": { "current": 3, "issue": "TBD" },
|
|
25
|
+
"config-schema": { "current": 3, "issue": "TBD" },
|
|
26
|
+
"profile": { "current": 3, "issue": "TBD" },
|
|
27
|
+
"prompt-budget": { "current": 3, "issue": "TBD" },
|
|
28
|
+
"uat": { "current": 3, "issue": "TBD" },
|
|
29
|
+
"validate": { "current": 3, "issue": "TBD" },
|
|
30
|
+
"workstream": { "current": 3, "issue": "TBD" },
|
|
31
|
+
"gsd-tools": { "current": 3, "issue": "TBD" },
|
|
32
|
+
"runtime-artifact-layout":{ "current": 3, "issue": "TBD" },
|
|
33
|
+
"security": { "current": 3, "issue": "TBD" },
|
|
34
|
+
"gsd-sdk": { "current": 3, "issue": "TBD" }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* lint-test-file-count.cjs — max 2 test files per production module.
|
|
4
|
+
*
|
|
5
|
+
* Scans sdk/src/query/, sdk/src/, get-shit-done/bin/lib/, bin/ for production
|
|
6
|
+
* modules, then counts matching test files in tests/ and sdk/src (recursive). Cap is 2
|
|
7
|
+
* (primary + one integration). Over-limit clusters must be in the allowlist at
|
|
8
|
+
* their frozen count (ratchet: may only decrease). --json emits structured output.
|
|
9
|
+
*
|
|
10
|
+
* Verdicts: OK_UNDER_LIMIT | OK_IN_ALLOWLIST | FAIL_EXCEEDS_LIMIT |
|
|
11
|
+
* FAIL_EXCEEDS_ALLOWLIST | HINT_CAN_REMOVE_FROM_ALLOWLIST
|
|
12
|
+
*/
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
const ROOT = path.join(__dirname, '..');
|
|
19
|
+
const PROD_DIRS = [
|
|
20
|
+
path.join(ROOT, 'sdk', 'src', 'query'),
|
|
21
|
+
path.join(ROOT, 'sdk', 'src'),
|
|
22
|
+
path.join(ROOT, 'get-shit-done', 'bin', 'lib'),
|
|
23
|
+
path.join(ROOT, 'bin'),
|
|
24
|
+
];
|
|
25
|
+
const TEST_DIRS = [
|
|
26
|
+
path.join(ROOT, 'tests'),
|
|
27
|
+
path.join(ROOT, 'sdk', 'src'),
|
|
28
|
+
];
|
|
29
|
+
const ALLOWLIST_PATH = path.join(__dirname, 'lint-test-file-count.allowlist.json');
|
|
30
|
+
const MAX_FILES = 2;
|
|
31
|
+
|
|
32
|
+
const Verdict = Object.freeze({
|
|
33
|
+
OK_UNDER_LIMIT: 'OK_UNDER_LIMIT',
|
|
34
|
+
OK_IN_ALLOWLIST: 'OK_IN_ALLOWLIST',
|
|
35
|
+
FAIL_EXCEEDS_LIMIT: 'FAIL_EXCEEDS_LIMIT',
|
|
36
|
+
FAIL_EXCEEDS_ALLOWLIST: 'FAIL_EXCEEDS_ALLOWLIST',
|
|
37
|
+
HINT_CAN_REMOVE_FROM_ALLOWLIST: 'HINT_CAN_REMOVE_FROM_ALLOWLIST',
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
function isTestFile(name) {
|
|
41
|
+
return name.endsWith('.test.ts') || name.endsWith('.test.cjs');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function listFiles(dir, pred) {
|
|
45
|
+
try {
|
|
46
|
+
return fs.readdirSync(dir, { withFileTypes: true })
|
|
47
|
+
.filter(e => e.isFile() && pred(e.name))
|
|
48
|
+
.map(e => path.join(dir, e.name));
|
|
49
|
+
} catch (_) { return []; }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function findTestFilesRecursive(dir) {
|
|
53
|
+
const out = [];
|
|
54
|
+
let entries;
|
|
55
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); }
|
|
56
|
+
catch (_) { return out; }
|
|
57
|
+
for (const e of entries) {
|
|
58
|
+
const full = path.join(dir, e.name);
|
|
59
|
+
if (e.isDirectory()) out.push(...findTestFilesRecursive(full));
|
|
60
|
+
else if (isTestFile(e.name)) out.push(full);
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function prodPrefix(filename) {
|
|
66
|
+
return filename.replace(/\.(cjs|ts|js)$/, '');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Strip .test.{cjs,ts} and .integration.test.ts, then strip issue stamps.
|
|
70
|
+
function testEffectivePrefix(testName) {
|
|
71
|
+
const bare = testName
|
|
72
|
+
.replace(/\.integration\.test\.(ts|cjs)$/, '')
|
|
73
|
+
.replace(/\.test\.(ts|cjs)$/, '');
|
|
74
|
+
const m = bare.match(/^(?:feat|bug|enh|fix)-\d+(?:-\d+)*-(.+)$/);
|
|
75
|
+
return m ? m[1] : bare;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function collectProdPrefixes() {
|
|
79
|
+
const map = new Map();
|
|
80
|
+
for (const dir of PROD_DIRS) {
|
|
81
|
+
for (const f of listFiles(dir, n =>
|
|
82
|
+
!isTestFile(n) &&
|
|
83
|
+
!/\.(generated|md|json)(\.|$)/.test(n) &&
|
|
84
|
+
/\.(ts|cjs|js)$/.test(n)
|
|
85
|
+
)) {
|
|
86
|
+
const prefix = prodPrefix(path.basename(f));
|
|
87
|
+
if (!map.has(prefix)) map.set(prefix, f);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return map;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function collectAllTestFiles() {
|
|
94
|
+
const seen = new Set();
|
|
95
|
+
const all = [];
|
|
96
|
+
for (const dir of TEST_DIRS) {
|
|
97
|
+
for (const f of findTestFilesRecursive(dir)) {
|
|
98
|
+
if (!seen.has(f)) { seen.add(f); all.push(f); }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return all;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function buildTestMap(prodPrefixes, allTestFiles) {
|
|
105
|
+
const map = new Map([...prodPrefixes.keys()].map(p => [p, []]));
|
|
106
|
+
for (const tf of allTestFiles) {
|
|
107
|
+
const ep = testEffectivePrefix(path.basename(tf));
|
|
108
|
+
for (const prefix of prodPrefixes.keys()) {
|
|
109
|
+
if (ep === prefix || ep.startsWith(prefix + '-')) {
|
|
110
|
+
map.get(prefix).push(tf);
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return map;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function loadAllowlist() {
|
|
119
|
+
try { return JSON.parse(fs.readFileSync(ALLOWLIST_PATH, 'utf-8')).modules || {}; }
|
|
120
|
+
catch (_) { return {}; }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function evaluateLint({ prefix, testFiles, allowlist }) {
|
|
124
|
+
const count = testFiles.length;
|
|
125
|
+
const entry = allowlist[prefix];
|
|
126
|
+
const ceiling = entry ? entry.current : null;
|
|
127
|
+
if (entry !== undefined) {
|
|
128
|
+
if (count <= MAX_FILES) return { verdict: Verdict.HINT_CAN_REMOVE_FROM_ALLOWLIST, prefix, count, ceiling, files: testFiles };
|
|
129
|
+
if (count <= ceiling) return { verdict: Verdict.OK_IN_ALLOWLIST, prefix, count, ceiling, files: testFiles };
|
|
130
|
+
return { verdict: Verdict.FAIL_EXCEEDS_ALLOWLIST, prefix, count, ceiling, files: testFiles };
|
|
131
|
+
}
|
|
132
|
+
if (count <= MAX_FILES) return { verdict: Verdict.OK_UNDER_LIMIT, prefix, count, ceiling: null, files: testFiles };
|
|
133
|
+
return { verdict: Verdict.FAIL_EXCEEDS_LIMIT, prefix, count, ceiling: null, files: testFiles };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function run() {
|
|
137
|
+
const jsonMode = process.argv.includes('--json');
|
|
138
|
+
const prodPrefixes = collectProdPrefixes();
|
|
139
|
+
const allTestFiles = collectAllTestFiles();
|
|
140
|
+
const testMap = buildTestMap(prodPrefixes, allTestFiles);
|
|
141
|
+
const allowlist = loadAllowlist();
|
|
142
|
+
|
|
143
|
+
const results = [];
|
|
144
|
+
for (const [prefix, files] of testMap) {
|
|
145
|
+
if (files.length === 0) continue;
|
|
146
|
+
results.push(evaluateLint({ prefix, testFiles: files, allowlist }));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const failures = results.filter(r =>
|
|
150
|
+
r.verdict === Verdict.FAIL_EXCEEDS_LIMIT || r.verdict === Verdict.FAIL_EXCEEDS_ALLOWLIST);
|
|
151
|
+
const hints = results.filter(r => r.verdict === Verdict.HINT_CAN_REMOVE_FROM_ALLOWLIST);
|
|
152
|
+
|
|
153
|
+
if (jsonMode) {
|
|
154
|
+
console.log(JSON.stringify({ ok: failures.length === 0, results, failures, hints }, null, 2));
|
|
155
|
+
process.exit(failures.length > 0 ? 1 : 0);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (failures.length === 0) {
|
|
159
|
+
const inAllowlist = results.filter(r => r.verdict === Verdict.OK_IN_ALLOWLIST).length;
|
|
160
|
+
console.log(`ok lint-test-file-count: ${results.length} module(s) checked, 0 failures` +
|
|
161
|
+
(inAllowlist > 0 ? `, ${inAllowlist} allowlisted` : '') +
|
|
162
|
+
(hints.length > 0 ? `, ${hints.length} hint(s)` : ''));
|
|
163
|
+
for (const h of hints) {
|
|
164
|
+
console.log(` hint: "${h.prefix}" is allowlisted at ${h.ceiling} but now has ${h.count} — remove from allowlist`);
|
|
165
|
+
}
|
|
166
|
+
process.exit(0);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
process.stderr.write(`\nERROR lint-test-file-count: ${failures.length} module(s) exceed the test-file limit\n\n`);
|
|
170
|
+
for (const f of failures) {
|
|
171
|
+
const tag = f.verdict === Verdict.FAIL_EXCEEDS_LIMIT
|
|
172
|
+
? `${f.count} files (limit ${MAX_FILES})`
|
|
173
|
+
: `${f.count} files (allowlist ceiling ${f.ceiling})`;
|
|
174
|
+
process.stderr.write(` ${f.prefix}: ${tag}\n`);
|
|
175
|
+
for (const tf of f.files) process.stderr.write(` ${path.relative(ROOT, tf)}\n`);
|
|
176
|
+
}
|
|
177
|
+
process.stderr.write('\nFix: consolidate test files per module (one primary + one integration).\n');
|
|
178
|
+
process.stderr.write('Or add the module to scripts/lint-test-file-count.allowlist.json with PR justification.\n\n');
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
module.exports = {
|
|
183
|
+
Verdict, evaluateLint, testEffectivePrefix, prodPrefix,
|
|
184
|
+
_collectProdPrefixes: collectProdPrefixes,
|
|
185
|
+
_collectAllTestFiles: collectAllTestFiles,
|
|
186
|
+
_buildTestMap: buildTestMap,
|
|
187
|
+
_loadAllowlist: loadAllowlist,
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
if (require.main === module) run();
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { matchesGlob } = require('path');
|
|
4
|
+
|
|
5
|
+
const TRUSTED_AUTHOR_ASSOCIATIONS = new Set([
|
|
6
|
+
'CONTRIBUTOR',
|
|
7
|
+
'COLLABORATOR',
|
|
8
|
+
'MEMBER',
|
|
9
|
+
'OWNER',
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
const DEFAULT_TEMPLATE_MARKERS = [
|
|
13
|
+
'Wrong template',
|
|
14
|
+
'Every PR must use a typed template',
|
|
15
|
+
'Select the template that matches your PR',
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Glob patterns for files that are considered CI/tooling/docs scope.
|
|
20
|
+
* If ALL changed files in a PR match at least one of these patterns,
|
|
21
|
+
* template enforcement is skipped automatically.
|
|
22
|
+
*/
|
|
23
|
+
const TOOLING_PATH_ALLOWLIST = [
|
|
24
|
+
'.github/**',
|
|
25
|
+
'scripts/**',
|
|
26
|
+
'docs/**',
|
|
27
|
+
'*.md',
|
|
28
|
+
'.changeset/**',
|
|
29
|
+
'package.json',
|
|
30
|
+
'package-lock.json',
|
|
31
|
+
'pnpm-lock.yaml',
|
|
32
|
+
'yarn.lock',
|
|
33
|
+
'Pipfile',
|
|
34
|
+
'Pipfile.lock',
|
|
35
|
+
'requirements.txt',
|
|
36
|
+
'requirements*.txt',
|
|
37
|
+
'poetry.lock',
|
|
38
|
+
'Gemfile',
|
|
39
|
+
'Gemfile.lock',
|
|
40
|
+
'go.sum',
|
|
41
|
+
'go.mod',
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Matches an explicit PR-template exemption marker of the form:
|
|
46
|
+
* <!-- pr-template-exempt: <non-empty reason> -->
|
|
47
|
+
*
|
|
48
|
+
* Capture group 1 is the reason (trimmed). Empty/whitespace-only reasons
|
|
49
|
+
* do NOT match because the pattern requires at least one non-whitespace
|
|
50
|
+
* character in the captured body.
|
|
51
|
+
*/
|
|
52
|
+
const EXEMPT_MARKER_REGEX = /<!--\s*pr-template-exempt:\s*([\s\S]*?\S)\s*-->/;
|
|
53
|
+
|
|
54
|
+
const TEMPLATES = [
|
|
55
|
+
{
|
|
56
|
+
name: 'fix',
|
|
57
|
+
heading: 'Fix PR',
|
|
58
|
+
requiredHeadings: [
|
|
59
|
+
'Fix PR',
|
|
60
|
+
'Linked Issue',
|
|
61
|
+
'What was broken',
|
|
62
|
+
'What this fix does',
|
|
63
|
+
'Testing',
|
|
64
|
+
'Checklist',
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'enhancement',
|
|
69
|
+
heading: 'Enhancement PR',
|
|
70
|
+
requiredHeadings: [
|
|
71
|
+
'Enhancement PR',
|
|
72
|
+
'Linked Issue',
|
|
73
|
+
'What this enhancement improves',
|
|
74
|
+
'Before / After',
|
|
75
|
+
'How it was implemented',
|
|
76
|
+
'Testing',
|
|
77
|
+
'Scope confirmation',
|
|
78
|
+
'Checklist',
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'feature',
|
|
83
|
+
heading: 'Feature PR',
|
|
84
|
+
requiredHeadings: [
|
|
85
|
+
'Feature PR',
|
|
86
|
+
'Linked Issue',
|
|
87
|
+
'Feature summary',
|
|
88
|
+
'What changed',
|
|
89
|
+
'Implementation notes',
|
|
90
|
+
'Spec compliance',
|
|
91
|
+
'Testing',
|
|
92
|
+
'Scope confirmation',
|
|
93
|
+
'Checklist',
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
function stripMarkdownDecoration(value) {
|
|
99
|
+
return value
|
|
100
|
+
.replace(/^\s*#+\s*/, '')
|
|
101
|
+
.replace(/\s*#+\s*$/, '')
|
|
102
|
+
.replace(/\*\*/g, '')
|
|
103
|
+
.trim()
|
|
104
|
+
.toLowerCase();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function extractHeadings(body) {
|
|
108
|
+
const headings = new Set();
|
|
109
|
+
for (const line of String(body || '').split(/\r?\n/)) {
|
|
110
|
+
if (/^\s*#{1,6}\s+\S/.test(line)) {
|
|
111
|
+
headings.add(stripMarkdownDecoration(line));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return headings;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function includesDefaultTemplate(body) {
|
|
118
|
+
const text = String(body || '').toLowerCase();
|
|
119
|
+
return DEFAULT_TEMPLATE_MARKERS.some((marker) => text.includes(marker.toLowerCase()));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function matchingTemplate(body) {
|
|
123
|
+
const headings = extractHeadings(body);
|
|
124
|
+
for (const template of TEMPLATES) {
|
|
125
|
+
if (!headings.has(stripMarkdownDecoration(template.heading))) continue;
|
|
126
|
+
const missingHeadings = template.requiredHeadings.filter((heading) => {
|
|
127
|
+
return !headings.has(stripMarkdownDecoration(heading));
|
|
128
|
+
});
|
|
129
|
+
return {
|
|
130
|
+
template: template.name,
|
|
131
|
+
missingHeadings,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
template: null,
|
|
136
|
+
missingHeadings: [],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Returns true iff every path in changedFiles matches at least one glob
|
|
142
|
+
* pattern in the allowlist. Returns false for an empty file list (no
|
|
143
|
+
* files means we cannot confirm it is a tooling-only PR).
|
|
144
|
+
*/
|
|
145
|
+
function allPathsAreTooling(changedFiles, allowlist) {
|
|
146
|
+
if (!Array.isArray(changedFiles) || changedFiles.length === 0) return false;
|
|
147
|
+
return changedFiles.every((file) =>
|
|
148
|
+
allowlist.some((pattern) => matchesGlob(file, pattern)),
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Returns true iff the body contains an explicit exemption marker with a
|
|
154
|
+
* non-empty (non-whitespace) reason.
|
|
155
|
+
*/
|
|
156
|
+
function hasExemptMarker(body, regex) {
|
|
157
|
+
const match = regex.exec(String(body || ''));
|
|
158
|
+
if (!match) return false;
|
|
159
|
+
return match[1].trim().length > 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function evaluatePrTemplate(body, authorAssociation, changedFiles) {
|
|
163
|
+
const association = String(authorAssociation || '').toUpperCase();
|
|
164
|
+
const trusted = TRUSTED_AUTHOR_ASSOCIATIONS.has(association);
|
|
165
|
+
const normalizedBody = String(body || '').trim();
|
|
166
|
+
|
|
167
|
+
// --- Carve-out 1: all changed files are in the tooling allowlist ---
|
|
168
|
+
if (allPathsAreTooling(changedFiles, TOOLING_PATH_ALLOWLIST)) {
|
|
169
|
+
return {
|
|
170
|
+
valid: true,
|
|
171
|
+
action: 'pass',
|
|
172
|
+
trusted,
|
|
173
|
+
authorAssociation: association || 'UNKNOWN',
|
|
174
|
+
template: null,
|
|
175
|
+
reason: null,
|
|
176
|
+
missingHeadings: [],
|
|
177
|
+
skipped: 'tooling-paths',
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// --- Carve-out 2: explicit exemption marker in the PR body ---
|
|
182
|
+
if (hasExemptMarker(normalizedBody, EXEMPT_MARKER_REGEX)) {
|
|
183
|
+
return {
|
|
184
|
+
valid: true,
|
|
185
|
+
action: 'pass',
|
|
186
|
+
trusted,
|
|
187
|
+
authorAssociation: association || 'UNKNOWN',
|
|
188
|
+
template: null,
|
|
189
|
+
reason: null,
|
|
190
|
+
missingHeadings: [],
|
|
191
|
+
skipped: 'exempt-marker',
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
let valid = true;
|
|
196
|
+
let reason = 'PR body uses a typed pull request template.';
|
|
197
|
+
let template = null;
|
|
198
|
+
let missingHeadings = [];
|
|
199
|
+
|
|
200
|
+
if (!normalizedBody) {
|
|
201
|
+
valid = false;
|
|
202
|
+
reason = 'PR body is empty; a typed pull request template is required.';
|
|
203
|
+
} else {
|
|
204
|
+
const match = matchingTemplate(normalizedBody);
|
|
205
|
+
template = match.template;
|
|
206
|
+
missingHeadings = match.missingHeadings;
|
|
207
|
+
if (template && missingHeadings.length === 0) {
|
|
208
|
+
valid = true;
|
|
209
|
+
} else if (template && missingHeadings.length > 0) {
|
|
210
|
+
valid = false;
|
|
211
|
+
reason = `PR body appears to use the ${template} template but is missing required headings.`;
|
|
212
|
+
} else if (includesDefaultTemplate(normalizedBody)) {
|
|
213
|
+
valid = false;
|
|
214
|
+
reason = 'PR body still contains the default wrong-template guidance.';
|
|
215
|
+
} else {
|
|
216
|
+
valid = false;
|
|
217
|
+
reason = 'PR body does not match the fix, enhancement, or feature template.';
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
let action = 'pass';
|
|
222
|
+
if (!valid) {
|
|
223
|
+
action = trusted ? 'warn' : 'close';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
valid,
|
|
228
|
+
action,
|
|
229
|
+
trusted,
|
|
230
|
+
authorAssociation: association || 'UNKNOWN',
|
|
231
|
+
template,
|
|
232
|
+
reason,
|
|
233
|
+
missingHeadings,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function main() {
|
|
238
|
+
const changedFiles = process.env.CHANGED_FILES
|
|
239
|
+
? process.env.CHANGED_FILES.split('\n').map((f) => f.trim()).filter(Boolean)
|
|
240
|
+
: undefined;
|
|
241
|
+
const result = evaluatePrTemplate(
|
|
242
|
+
process.env.PR_BODY || '',
|
|
243
|
+
process.env.AUTHOR_ASSOCIATION || '',
|
|
244
|
+
changedFiles,
|
|
245
|
+
);
|
|
246
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
247
|
+
if (process.env.GITHUB_OUTPUT) {
|
|
248
|
+
const fs = require('fs');
|
|
249
|
+
fs.appendFileSync(process.env.GITHUB_OUTPUT, `result=${JSON.stringify(result)}\n`);
|
|
250
|
+
fs.appendFileSync(process.env.GITHUB_OUTPUT, `action=${result.action}\n`);
|
|
251
|
+
fs.appendFileSync(process.env.GITHUB_OUTPUT, `valid=${result.valid ? 'true' : 'false'}\n`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (require.main === module) {
|
|
256
|
+
main();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
module.exports = {
|
|
260
|
+
evaluatePrTemplate,
|
|
261
|
+
extractHeadings,
|
|
262
|
+
includesDefaultTemplate,
|
|
263
|
+
matchingTemplate,
|
|
264
|
+
allPathsAreTooling,
|
|
265
|
+
hasExemptMarker,
|
|
266
|
+
TOOLING_PATH_ALLOWLIST,
|
|
267
|
+
EXEMPT_MARKER_REGEX,
|
|
268
|
+
};
|