@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,282 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Cross-platform test runner — resolves test file globs via Node
|
|
3
|
+
// instead of relying on shell expansion (which fails on Windows PowerShell/cmd).
|
|
4
|
+
// Propagates NODE_V8_COVERAGE so c8 collects coverage from the child process.
|
|
5
|
+
//
|
|
6
|
+
// Suite filtering (issue #3597):
|
|
7
|
+
// node scripts/run-tests.cjs # default — runs ALL tests (backcompat)
|
|
8
|
+
// node scripts/run-tests.cjs --suite all # explicit "everything"
|
|
9
|
+
// node scripts/run-tests.cjs --suite unit # only files with no other suite marker
|
|
10
|
+
// node scripts/run-tests.cjs --suite security # *.security.test.cjs
|
|
11
|
+
// node scripts/run-tests.cjs --suite integration # *.integration.test.cjs
|
|
12
|
+
// node scripts/run-tests.cjs --suite install # *.install.test.cjs
|
|
13
|
+
// node scripts/run-tests.cjs --suite slow # *.slow.test.cjs
|
|
14
|
+
// node scripts/run-tests.cjs --files "a.test.cjs b.test.cjs"
|
|
15
|
+
// node scripts/run-tests.cjs --files-from /tmp/selected-tests.txt
|
|
16
|
+
//
|
|
17
|
+
// Suite grouping convention: filename suffix marker before `.test.cjs`.
|
|
18
|
+
// A file named `foo.security.test.cjs` belongs to the `security` suite.
|
|
19
|
+
// A file named `foo.test.cjs` (no marker) belongs to the `unit` suite.
|
|
20
|
+
// See docs/TESTING-SUITES.md for full grouping policy.
|
|
21
|
+
'use strict';
|
|
22
|
+
|
|
23
|
+
const { readdirSync } = require('fs');
|
|
24
|
+
const { join } = require('path');
|
|
25
|
+
const { execFileSync } = require('child_process');
|
|
26
|
+
|
|
27
|
+
const SUITES = ['all', 'unit', 'integration', 'install', 'security', 'slow'];
|
|
28
|
+
const MARKED_SUITES = ['integration', 'install', 'security', 'slow'];
|
|
29
|
+
|
|
30
|
+
function parseArgs(argv) {
|
|
31
|
+
let suite = null;
|
|
32
|
+
let seen = false;
|
|
33
|
+
let files = null;
|
|
34
|
+
let filesFrom = null;
|
|
35
|
+
for (let i = 0; i < argv.length; i++) {
|
|
36
|
+
const a = argv[i];
|
|
37
|
+
if (a === '--suite') {
|
|
38
|
+
if (seen) {
|
|
39
|
+
return { error: 'duplicate --suite flag' };
|
|
40
|
+
}
|
|
41
|
+
seen = true;
|
|
42
|
+
const v = argv[i + 1];
|
|
43
|
+
if (!v || v.startsWith('--')) {
|
|
44
|
+
return { error: '--suite requires a value' };
|
|
45
|
+
}
|
|
46
|
+
suite = v;
|
|
47
|
+
i++;
|
|
48
|
+
} else if (a.startsWith('--suite=')) {
|
|
49
|
+
if (seen) {
|
|
50
|
+
return { error: 'duplicate --suite flag' };
|
|
51
|
+
}
|
|
52
|
+
seen = true;
|
|
53
|
+
suite = a.slice('--suite='.length);
|
|
54
|
+
if (!suite) {
|
|
55
|
+
return { error: '--suite requires a value' };
|
|
56
|
+
}
|
|
57
|
+
} else if (a === '--files') {
|
|
58
|
+
if (files !== null) {
|
|
59
|
+
return { error: 'duplicate --files flag' };
|
|
60
|
+
}
|
|
61
|
+
const v = argv[i + 1];
|
|
62
|
+
if (!v || v.startsWith('--')) {
|
|
63
|
+
return { error: '--files requires a value' };
|
|
64
|
+
}
|
|
65
|
+
files = v;
|
|
66
|
+
i++;
|
|
67
|
+
} else if (a.startsWith('--files=')) {
|
|
68
|
+
if (files !== null) {
|
|
69
|
+
return { error: 'duplicate --files flag' };
|
|
70
|
+
}
|
|
71
|
+
files = a.slice('--files='.length);
|
|
72
|
+
if (!files) {
|
|
73
|
+
return { error: '--files requires a value' };
|
|
74
|
+
}
|
|
75
|
+
} else if (a === '--files-from') {
|
|
76
|
+
if (filesFrom !== null) {
|
|
77
|
+
return { error: 'duplicate --files-from flag' };
|
|
78
|
+
}
|
|
79
|
+
const v = argv[i + 1];
|
|
80
|
+
if (!v || v.startsWith('--')) {
|
|
81
|
+
return { error: '--files-from requires a value' };
|
|
82
|
+
}
|
|
83
|
+
filesFrom = v;
|
|
84
|
+
i++;
|
|
85
|
+
} else if (a.startsWith('--files-from=')) {
|
|
86
|
+
if (filesFrom !== null) {
|
|
87
|
+
return { error: 'duplicate --files-from flag' };
|
|
88
|
+
}
|
|
89
|
+
filesFrom = a.slice('--files-from='.length);
|
|
90
|
+
if (!filesFrom) {
|
|
91
|
+
return { error: '--files-from requires a value' };
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
return { error: `unknown argument: ${a}` };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (files !== null && filesFrom !== null) {
|
|
98
|
+
return { error: '--files and --files-from cannot be combined' };
|
|
99
|
+
}
|
|
100
|
+
return { suite, files, filesFrom };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Return the marked suite name embedded in a filename, or null if it's unmarked.
|
|
104
|
+
// foo.security.test.cjs -> "security"
|
|
105
|
+
// foo.test.cjs -> null (unit)
|
|
106
|
+
function suiteOf(filename) {
|
|
107
|
+
if (!filename.endsWith('.test.cjs')) return null;
|
|
108
|
+
const base = filename.slice(0, -'.test.cjs'.length);
|
|
109
|
+
const lastDot = base.lastIndexOf('.');
|
|
110
|
+
if (lastDot === -1) return null;
|
|
111
|
+
const marker = base.slice(lastDot + 1);
|
|
112
|
+
return MARKED_SUITES.includes(marker) ? marker : null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function selectFiles(allFiles, suite) {
|
|
116
|
+
if (suite === null || suite === 'all') {
|
|
117
|
+
return allFiles;
|
|
118
|
+
}
|
|
119
|
+
if (suite === 'unit') {
|
|
120
|
+
return allFiles.filter(f => suiteOf(f) === null);
|
|
121
|
+
}
|
|
122
|
+
return allFiles.filter(f => suiteOf(f) === suite);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function splitFileList(value) {
|
|
126
|
+
if (!value) return [];
|
|
127
|
+
return value
|
|
128
|
+
.split(/[,\s]+/)
|
|
129
|
+
.map(v => v.trim())
|
|
130
|
+
.filter(Boolean)
|
|
131
|
+
.map(v => v.replace(/^tests[\\/]/, ''));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function selectExplicitFiles(allFiles, filesValue, filesFrom) {
|
|
135
|
+
const fs = require('fs');
|
|
136
|
+
const requested = filesFrom
|
|
137
|
+
? splitFileList(fs.readFileSync(filesFrom, 'utf8'))
|
|
138
|
+
: splitFileList(filesValue);
|
|
139
|
+
const available = new Set(allFiles);
|
|
140
|
+
const selected = [];
|
|
141
|
+
const missing = [];
|
|
142
|
+
for (const file of requested) {
|
|
143
|
+
if (available.has(file)) {
|
|
144
|
+
selected.push(file);
|
|
145
|
+
} else {
|
|
146
|
+
missing.push(file);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (missing.length > 0) {
|
|
150
|
+
return {
|
|
151
|
+
error: `requested test file(s) not found: ${missing.join(', ')}`,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return { files: [...new Set(selected)] };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function main() {
|
|
158
|
+
const args = process.argv.slice(2);
|
|
159
|
+
const parsed = parseArgs(args);
|
|
160
|
+
if (parsed.error) {
|
|
161
|
+
console.error(`run-tests: ${parsed.error}`);
|
|
162
|
+
console.error(`Valid suites: ${SUITES.join(', ')}`);
|
|
163
|
+
process.exit(2);
|
|
164
|
+
}
|
|
165
|
+
const suite = parsed.suite;
|
|
166
|
+
if (suite !== null && !SUITES.includes(suite)) {
|
|
167
|
+
console.error(`run-tests: unknown suite "${suite}"`);
|
|
168
|
+
console.error(`Valid suites: ${SUITES.join(', ')}`);
|
|
169
|
+
process.exit(2);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const testDir = process.env.GSD_TEST_DIR
|
|
173
|
+
? process.env.GSD_TEST_DIR
|
|
174
|
+
: join(__dirname, '..', 'tests');
|
|
175
|
+
|
|
176
|
+
const allFiles = readdirSync(testDir)
|
|
177
|
+
.filter(f => f.endsWith('.test.cjs'))
|
|
178
|
+
.sort();
|
|
179
|
+
|
|
180
|
+
if (allFiles.length === 0) {
|
|
181
|
+
console.error(`No test files found in ${testDir}`);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let selectedNames;
|
|
186
|
+
if (parsed.files !== null || parsed.filesFrom !== null) {
|
|
187
|
+
const explicit = selectExplicitFiles(allFiles, parsed.files, parsed.filesFrom);
|
|
188
|
+
if (explicit.error) {
|
|
189
|
+
console.error(`run-tests: ${explicit.error}`);
|
|
190
|
+
process.exit(2);
|
|
191
|
+
}
|
|
192
|
+
selectedNames = explicit.files;
|
|
193
|
+
} else {
|
|
194
|
+
selectedNames = selectFiles(allFiles, suite);
|
|
195
|
+
}
|
|
196
|
+
const selected = selectedNames.map(f => join(testDir, f));
|
|
197
|
+
|
|
198
|
+
if (selected.length === 0) {
|
|
199
|
+
// Empty suite: report and exit 0 so empty lanes (e.g. `security` before
|
|
200
|
+
// adversarial tests land) don't gate CI. CI consumers wanting strictness
|
|
201
|
+
// can grep stderr for "no tests in suite".
|
|
202
|
+
console.error(`run-tests: no tests in suite "${suite || 'all'}"`);
|
|
203
|
+
process.exit(0);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Log selected files to stderr for CI / harness-test visibility.
|
|
207
|
+
// node:test default reporter doesn't echo filenames, so this gives
|
|
208
|
+
// operators a single stable line they can grep.
|
|
209
|
+
console.error(
|
|
210
|
+
`run-tests: suite="${suite || 'all'}" files=${selected.length}: ${selected
|
|
211
|
+
.map(f => f.split(/[\\/]/).pop())
|
|
212
|
+
.join(' ')}`,
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
// Default concurrency: 4 on Linux/macOS, 2 on Windows.
|
|
216
|
+
//
|
|
217
|
+
// Windows has significantly higher per-subprocess overhead than Linux/macOS:
|
|
218
|
+
// - Windows Defender scans each spawned process
|
|
219
|
+
// - NTFS has higher file-system latency under concurrent access
|
|
220
|
+
// - synckit worker_threads (used by the SDK bridge in gsd-tools.cjs) spawn
|
|
221
|
+
// native threads that contend on SharedArrayBuffer + Atomics.wait; under
|
|
222
|
+
// Node 24 on Windows, 4-way concurrent gsd-tools invocations (each spawning
|
|
223
|
+
// a synckit worker) caused intermittent process crashes with empty stderr —
|
|
224
|
+
// a signature of OS-level resource exhaustion killing worker threads before
|
|
225
|
+
// they could flush. Reducing to 2 halves the peak concurrent worker count.
|
|
226
|
+
//
|
|
227
|
+
// Operator override via TEST_CONCURRENCY env var for local debugging.
|
|
228
|
+
const defaultConcurrency = process.platform === 'win32' ? 2 : 4;
|
|
229
|
+
const concurrency = process.env.TEST_CONCURRENCY
|
|
230
|
+
? `--test-concurrency=${process.env.TEST_CONCURRENCY}`
|
|
231
|
+
: `--test-concurrency=${defaultConcurrency}`;
|
|
232
|
+
|
|
233
|
+
// Windows `CreateProcess` caps the full command line at 32,767 chars
|
|
234
|
+
// (lpCommandLine). With 500+ test paths the spawn fails instantly with no
|
|
235
|
+
// test output. Linux/macOS allow ~2 MB (ARG_MAX) so unchunked spawns are
|
|
236
|
+
// fine there. Split into chunks sized for the tightest target so behavior
|
|
237
|
+
// is identical across platforms. (#3597)
|
|
238
|
+
// Operator override (also used by tests to force chunking with short paths).
|
|
239
|
+
const MAX_CMDLINE_CHARS = process.env.RUN_TESTS_MAX_CMDLINE_CHARS
|
|
240
|
+
? Number(process.env.RUN_TESTS_MAX_CMDLINE_CHARS)
|
|
241
|
+
: 28000; // headroom below the 32,767 Windows ceiling
|
|
242
|
+
const FIXED_OVERHEAD = process.execPath.length + '--test'.length + concurrency.length + 8;
|
|
243
|
+
const chunks = [];
|
|
244
|
+
let current = [];
|
|
245
|
+
let currentLen = FIXED_OVERHEAD;
|
|
246
|
+
for (const file of selected) {
|
|
247
|
+
const add = file.length + 1; // +1 for the inter-arg separator
|
|
248
|
+
if (current.length > 0 && currentLen + add > MAX_CMDLINE_CHARS) {
|
|
249
|
+
chunks.push(current);
|
|
250
|
+
current = [];
|
|
251
|
+
currentLen = FIXED_OVERHEAD;
|
|
252
|
+
}
|
|
253
|
+
current.push(file);
|
|
254
|
+
currentLen += add;
|
|
255
|
+
}
|
|
256
|
+
if (current.length > 0) chunks.push(current);
|
|
257
|
+
|
|
258
|
+
let firstFailureExit = 0;
|
|
259
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
260
|
+
if (chunks.length > 1) {
|
|
261
|
+
console.error(`run-tests: chunk ${i + 1}/${chunks.length} — ${chunks[i].length} files`);
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
execFileSync(process.execPath, ['--test', concurrency, ...chunks[i]], {
|
|
265
|
+
stdio: 'inherit',
|
|
266
|
+
env: { ...process.env },
|
|
267
|
+
});
|
|
268
|
+
} catch (err) {
|
|
269
|
+
const code = err.status || 1;
|
|
270
|
+
// Run every chunk so the operator sees all failures in one pass; report
|
|
271
|
+
// the first non-zero exit at the end.
|
|
272
|
+
if (firstFailureExit === 0) firstFailureExit = code;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (firstFailureExit !== 0) process.exit(firstFailureExit);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (require.main === module) {
|
|
279
|
+
main();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
module.exports = { suiteOf };
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# secret-scan-lint.sh — Lint governance policy for .secretscanignore exclusions
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# scripts/secret-scan-lint.sh --file <path-to-.secretscanignore>
|
|
6
|
+
# scripts/secret-scan-lint.sh --file <path-to-.secretscanignore> --strict
|
|
7
|
+
#
|
|
8
|
+
# Exit codes:
|
|
9
|
+
# 0 = every exclusion has full annotation OR is grandfathered (with deprecation warning)
|
|
10
|
+
# 1 = annotation violation: missing required key, expired date, or unguarded wildcard
|
|
11
|
+
# without rule-id; OR (under --strict) any grandfathered entry is present
|
|
12
|
+
# 2 = usage/config error (file not found, invalid arguments)
|
|
13
|
+
#
|
|
14
|
+
# Annotation syntax (sidecar comment, must immediately precede the path line):
|
|
15
|
+
# # allow: <pattern> reason="..." owner="..." expires="YYYY-MM-DD" [rule-id="..."]
|
|
16
|
+
# <pattern>
|
|
17
|
+
#
|
|
18
|
+
# Required annotation keys: reason, owner, expires
|
|
19
|
+
# Optional annotation key: rule-id (REQUIRED when pattern contains '*' wildcards)
|
|
20
|
+
#
|
|
21
|
+
# Grandfathered entries: paths with any preceding plain comment (not a structured
|
|
22
|
+
# annotation) are treated as grandfathered in default mode — exit 0 with a
|
|
23
|
+
# deprecation warning to stderr. Under --strict, grandfathered entries cause exit 1.
|
|
24
|
+
#
|
|
25
|
+
# Design references:
|
|
26
|
+
# - GitGuardian exclusion annotation convention:
|
|
27
|
+
# https://docs.gitguardian.com/internal-repositories-monitoring/integrations/cli/secrets
|
|
28
|
+
# - CNCF Security TAG threat-model exception lifecycle:
|
|
29
|
+
# https://github.com/cncf/tag-security/blob/main/community/working-groups/threat-modeling/templates/threats.md
|
|
30
|
+
#
|
|
31
|
+
# Exit-code alignment with secret-scan.sh:
|
|
32
|
+
# Both scripts use 0=clean, 1=policy-violation/findings, 2=usage/config-error.
|
|
33
|
+
# The symmetry is intentional — CI can treat either non-zero as a gate failure.
|
|
34
|
+
|
|
35
|
+
set -euo pipefail
|
|
36
|
+
|
|
37
|
+
# ─── Argument Parsing ─────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
STRICT=false
|
|
40
|
+
IGNOREFILE=""
|
|
41
|
+
|
|
42
|
+
usage() {
|
|
43
|
+
echo "Usage: $0 --file <path-to-.secretscanignore> [--strict]" >&2
|
|
44
|
+
exit 2
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
while [[ $# -gt 0 ]]; do
|
|
48
|
+
case "$1" in
|
|
49
|
+
--file)
|
|
50
|
+
shift
|
|
51
|
+
[[ $# -eq 0 ]] && usage
|
|
52
|
+
IGNOREFILE="$1"
|
|
53
|
+
shift
|
|
54
|
+
;;
|
|
55
|
+
--strict)
|
|
56
|
+
STRICT=true
|
|
57
|
+
shift
|
|
58
|
+
;;
|
|
59
|
+
-h|--help)
|
|
60
|
+
usage
|
|
61
|
+
;;
|
|
62
|
+
*)
|
|
63
|
+
echo "Error: unknown argument: $1" >&2
|
|
64
|
+
usage
|
|
65
|
+
;;
|
|
66
|
+
esac
|
|
67
|
+
done
|
|
68
|
+
|
|
69
|
+
if [[ -z "$IGNOREFILE" ]]; then
|
|
70
|
+
usage
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
if [[ ! -f "$IGNOREFILE" ]]; then
|
|
74
|
+
echo "Error: file not found: $IGNOREFILE" >&2
|
|
75
|
+
exit 2
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# ─── Date Helpers ─────────────────────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
# Returns today's date as YYYY-MM-DD (portable across macOS and Linux)
|
|
81
|
+
today_date() {
|
|
82
|
+
date +%Y-%m-%d
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Returns true (0) if date1 < date2 (both YYYY-MM-DD strings)
|
|
86
|
+
date_is_past() {
|
|
87
|
+
local check_date="$1"
|
|
88
|
+
local today
|
|
89
|
+
today=$(today_date)
|
|
90
|
+
# Lexicographic comparison works for ISO-8601 dates
|
|
91
|
+
[[ "$check_date" < "$today" ]]
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# ─── Annotation Parser ────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
# Returns value of a key="value" or key='value' pair from a string.
|
|
97
|
+
# Usage: extract_key <string> <key>
|
|
98
|
+
extract_key() {
|
|
99
|
+
local str="$1"
|
|
100
|
+
local key="$2"
|
|
101
|
+
# Match key="value" or key='value'
|
|
102
|
+
local val
|
|
103
|
+
val=$(echo "$str" | grep -oE "${key}=['\"][^'\"]+['\"]" | head -1 | sed "s/${key}=['\"]//;s/['\"]$//") || true
|
|
104
|
+
echo "$val"
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# Returns true (0) if the string contains a wildcard glob character (* or **)
|
|
108
|
+
contains_wildcard() {
|
|
109
|
+
local pattern="$1"
|
|
110
|
+
[[ "$pattern" == *"*"* ]]
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# Returns true (0) if a comment line is a structured annotation
|
|
114
|
+
# (must start with "# allow:" prefix)
|
|
115
|
+
is_structured_annotation() {
|
|
116
|
+
local comment="$1"
|
|
117
|
+
[[ "$comment" =~ ^#[[:space:]]+allow:[[:space:]] ]]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# ─── Main Lint Logic ──────────────────────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
VIOLATIONS=0
|
|
123
|
+
WARNINGS=0
|
|
124
|
+
|
|
125
|
+
# We process the file line-by-line, tracking the comment immediately preceding
|
|
126
|
+
# each path entry. If the preceding line was a comment, we inspect it.
|
|
127
|
+
|
|
128
|
+
prev_comment=""
|
|
129
|
+
lineno=0
|
|
130
|
+
|
|
131
|
+
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
132
|
+
lineno=$((lineno + 1))
|
|
133
|
+
|
|
134
|
+
# Skip empty lines (reset prev_comment to avoid false association)
|
|
135
|
+
if [[ -z "${line// }" ]]; then
|
|
136
|
+
prev_comment=""
|
|
137
|
+
continue
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
# Accumulate comment lines
|
|
141
|
+
if [[ "$line" =~ ^[[:space:]]*# ]]; then
|
|
142
|
+
prev_comment="$line"
|
|
143
|
+
continue
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# This is a path/pattern entry.
|
|
147
|
+
local_path="$line"
|
|
148
|
+
|
|
149
|
+
# ── Case 1: No preceding comment at all ────────────────────────────────────
|
|
150
|
+
if [[ -z "$prev_comment" ]]; then
|
|
151
|
+
echo "VIOLATION (line $lineno): '$local_path' has no annotation comment." >&2
|
|
152
|
+
echo " Required: # allow: <pattern> reason=\"...\" owner=\"...\" expires=\"YYYY-MM-DD\"" >&2
|
|
153
|
+
VIOLATIONS=$((VIOLATIONS + 1))
|
|
154
|
+
prev_comment=""
|
|
155
|
+
continue
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
# ── Case 2: Preceding comment exists — check if it's a structured annotation ─
|
|
159
|
+
if is_structured_annotation "$prev_comment"; then
|
|
160
|
+
# Extract required keys
|
|
161
|
+
reason=$(extract_key "$prev_comment" "reason")
|
|
162
|
+
owner=$(extract_key "$prev_comment" "owner")
|
|
163
|
+
expires=$(extract_key "$prev_comment" "expires")
|
|
164
|
+
rule_id=$(extract_key "$prev_comment" "rule-id")
|
|
165
|
+
|
|
166
|
+
local_ok=true
|
|
167
|
+
|
|
168
|
+
if [[ -z "$reason" ]]; then
|
|
169
|
+
echo "VIOLATION (line $lineno): '$local_path' annotation missing required key: reason" >&2
|
|
170
|
+
local_ok=false
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
if [[ -z "$owner" ]]; then
|
|
174
|
+
echo "VIOLATION (line $lineno): '$local_path' annotation missing required key: owner" >&2
|
|
175
|
+
local_ok=false
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
if [[ -z "$expires" ]]; then
|
|
179
|
+
echo "VIOLATION (line $lineno): '$local_path' annotation missing required key: expires" >&2
|
|
180
|
+
local_ok=false
|
|
181
|
+
elif date_is_past "$expires"; then
|
|
182
|
+
echo "VIOLATION (line $lineno): '$local_path' annotation 'expires' date is in the past: $expires" >&2
|
|
183
|
+
echo " Review this exclusion and update or remove it." >&2
|
|
184
|
+
local_ok=false
|
|
185
|
+
fi
|
|
186
|
+
|
|
187
|
+
if contains_wildcard "$local_path" && [[ -z "$rule_id" ]]; then
|
|
188
|
+
echo "VIOLATION (line $lineno): '$local_path' uses a wildcard but is missing required key: rule-id" >&2
|
|
189
|
+
echo " Wildcard exclusions (** *.ext) require an explicit rule-id for auditability." >&2
|
|
190
|
+
local_ok=false
|
|
191
|
+
fi
|
|
192
|
+
|
|
193
|
+
if [[ "$local_ok" == false ]]; then
|
|
194
|
+
VIOLATIONS=$((VIOLATIONS + 1))
|
|
195
|
+
fi
|
|
196
|
+
|
|
197
|
+
else
|
|
198
|
+
# ── Case 3: Preceding comment is plain (not structured) — grandfathered ──
|
|
199
|
+
if [[ "$STRICT" == true ]]; then
|
|
200
|
+
echo "VIOLATION (line $lineno): '$local_path' is grandfathered (no structured annotation) — rejected under --strict mode." >&2
|
|
201
|
+
echo " Add: # allow: $local_path reason=\"...\" owner=\"...\" expires=\"YYYY-MM-DD\"" >&2
|
|
202
|
+
VIOLATIONS=$((VIOLATIONS + 1))
|
|
203
|
+
else
|
|
204
|
+
echo "WARNING (line $lineno): '$local_path' is grandfathered (missing structured annotation)." >&2
|
|
205
|
+
echo " DEPRECATION: migrate to structured annotation before removing grandfather status." >&2
|
|
206
|
+
echo " Required: # allow: $local_path reason=\"...\" owner=\"...\" expires=\"YYYY-MM-DD\"" >&2
|
|
207
|
+
echo " See: https://docs.gitguardian.com/internal-repositories-monitoring/integrations/cli/secrets" >&2
|
|
208
|
+
WARNINGS=$((WARNINGS + 1))
|
|
209
|
+
fi
|
|
210
|
+
fi
|
|
211
|
+
|
|
212
|
+
prev_comment=""
|
|
213
|
+
|
|
214
|
+
done < "$IGNOREFILE"
|
|
215
|
+
|
|
216
|
+
# ─── Summary ──────────────────────────────────────────────────────────────────
|
|
217
|
+
|
|
218
|
+
if [[ $VIOLATIONS -gt 0 ]]; then
|
|
219
|
+
echo "secret-scan-lint: $VIOLATIONS violation(s) found" >&2
|
|
220
|
+
if [[ $STRICT == true ]]; then
|
|
221
|
+
echo "secret-scan-lint: --strict mode active — grandfathered entries are not permitted" >&2
|
|
222
|
+
fi
|
|
223
|
+
exit 1
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
if [[ $WARNINGS -gt 0 ]]; then
|
|
227
|
+
echo "secret-scan-lint: $WARNINGS grandfathered entry/entries (deprecation warning)" >&2
|
|
228
|
+
fi
|
|
229
|
+
|
|
230
|
+
echo "secret-scan-lint: OK"
|
|
231
|
+
exit 0
|