@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,199 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const cp = require('node:child_process');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
const { parseFragment } = require('./parse.cjs');
|
|
7
|
+
const { packageName, repoSlug: defaultRepoSlug } = require('../../get-shit-done/bin/lib/package-identity.cjs');
|
|
8
|
+
|
|
9
|
+
const SECTION_ORDER = ['Fixed', 'Added', 'Changed', 'Deprecated', 'Removed', 'Security'];
|
|
10
|
+
|
|
11
|
+
const FIXED_GROUPS = [
|
|
12
|
+
{
|
|
13
|
+
title: 'Verification, update & review safety',
|
|
14
|
+
pattern: /\b(verifier|verification|verify|probe|probes|debt|tbd|fixme|xxx|detect-custom-files|review|summary|blocker|critical)\b/i,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
title: 'State, planning & execution',
|
|
18
|
+
pattern: /\b(state|planning|planner|plan-phase|phase|roadmap|execute|executor|worktree|worktrees|resolve-model|init\.progress|model override|human_needed|ship preflight)\b/i,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
title: 'Install & runtime conversion',
|
|
22
|
+
pattern: /\b(install|installer|runtime|windows|powershell|codex|gemini|antigravity|hook|hooks|gsd-sdk|sdk readiness|cjs|model-catalog|path|shim)\b/i,
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const REMOVED_GROUPS = [
|
|
27
|
+
{
|
|
28
|
+
title: 'Intel updater',
|
|
29
|
+
pattern: /\b(intel|gsd-intel-updater|layout detection)\b/i,
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
function runGit(repo, args) {
|
|
34
|
+
return cp.execFileSync('git', args, {
|
|
35
|
+
cwd: repo,
|
|
36
|
+
encoding: 'utf8',
|
|
37
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function validateGitRef({ repo, ref, label }) {
|
|
42
|
+
if (typeof ref !== 'string' || ref.trim() !== ref || ref.length === 0) {
|
|
43
|
+
throw new Error(`Invalid git ref for ${label}: expected a non-empty trimmed string`);
|
|
44
|
+
}
|
|
45
|
+
if (
|
|
46
|
+
ref.startsWith('-') ||
|
|
47
|
+
ref.includes('..') ||
|
|
48
|
+
ref.includes('//') ||
|
|
49
|
+
!/^[A-Za-z0-9._/-]+$/.test(ref)
|
|
50
|
+
) {
|
|
51
|
+
throw new Error(`Invalid git ref for ${label}: ${ref}`);
|
|
52
|
+
}
|
|
53
|
+
runGit(repo, ['rev-parse', '--verify', `${ref}^{commit}`]);
|
|
54
|
+
return ref;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function changedFragmentPaths({ repo, fromRef, toRef }) {
|
|
58
|
+
const from = validateGitRef({ repo, ref: fromRef, label: 'fromRef' });
|
|
59
|
+
const to = validateGitRef({ repo, ref: toRef, label: 'toRef' });
|
|
60
|
+
const out = runGit(repo, ['diff', '--name-only', `${from}..${to}`, '--', '.changeset']);
|
|
61
|
+
return out
|
|
62
|
+
.split(/\r?\n/)
|
|
63
|
+
.filter(Boolean)
|
|
64
|
+
.filter((file) => /^\.changeset\/[^/]+\.md$/.test(file));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function readFileAtRef({ repo, ref, file }) {
|
|
68
|
+
return runGit(repo, ['show', `${ref}:${file}`]);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function loadFragmentsFromRange({ repo, fromRef, toRef }) {
|
|
72
|
+
const files = changedFragmentPaths({ repo, fromRef, toRef });
|
|
73
|
+
const fragments = [];
|
|
74
|
+
const failures = [];
|
|
75
|
+
|
|
76
|
+
for (const file of files) {
|
|
77
|
+
try {
|
|
78
|
+
const src = readFileAtRef({ repo, ref: toRef, file });
|
|
79
|
+
const parsed = parseFragment(src);
|
|
80
|
+
if (parsed.ok) {
|
|
81
|
+
fragments.push({
|
|
82
|
+
...parsed.fragment,
|
|
83
|
+
file,
|
|
84
|
+
slug: path.basename(file, '.md'),
|
|
85
|
+
});
|
|
86
|
+
} else {
|
|
87
|
+
failures.push({ file, reason: parsed.reason, detail: parsed.detail || null });
|
|
88
|
+
}
|
|
89
|
+
} catch (e) {
|
|
90
|
+
failures.push({ file, reason: 'read_failed', detail: e.message });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return { fragments, failures };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function classifyGroup(fragment) {
|
|
98
|
+
const haystack = `${fragment.slug || ''}\n${fragment.body || ''}`;
|
|
99
|
+
const groups = fragment.type === 'Removed' ? REMOVED_GROUPS : FIXED_GROUPS;
|
|
100
|
+
const match = groups.find((group) => group.pattern.test(haystack));
|
|
101
|
+
if (match) return match.title;
|
|
102
|
+
if (fragment.type === 'Removed') return 'Removed';
|
|
103
|
+
if (fragment.type === 'Fixed') return 'Other fixes';
|
|
104
|
+
return fragment.type;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function buildGithubReleaseNotesIr({ fragments }) {
|
|
108
|
+
const sections = [];
|
|
109
|
+
for (const type of SECTION_ORDER) {
|
|
110
|
+
const typed = fragments.filter((fragment) => fragment.type === type);
|
|
111
|
+
if (typed.length === 0) continue;
|
|
112
|
+
|
|
113
|
+
const groupMap = new Map();
|
|
114
|
+
for (const fragment of typed) {
|
|
115
|
+
const groupTitle = classifyGroup(fragment);
|
|
116
|
+
if (!groupMap.has(groupTitle)) groupMap.set(groupTitle, []);
|
|
117
|
+
groupMap.get(groupTitle).push(fragment);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
sections.push({
|
|
121
|
+
type,
|
|
122
|
+
groups: Array.from(groupMap, ([title, bullets]) => ({ title, bullets })),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return { sections };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function formatBullet(fragment) {
|
|
129
|
+
if (!Number.isInteger(fragment.pr) || fragment.pr <= 0) {
|
|
130
|
+
throw new Error(`Fragment ${fragment.slug || fragment.file || '<unknown>'} missing valid pr field`);
|
|
131
|
+
}
|
|
132
|
+
const body = `${fragment.body.trim()} (#${fragment.pr})`;
|
|
133
|
+
const lines = body.split(/\r?\n/);
|
|
134
|
+
return lines.map((line, index) => (index === 0 ? `- ${line}` : ` ${line}`)).join('\n');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function compareUrl({ repoSlug, fromRef, toRef }) {
|
|
138
|
+
const normalizedSlug = String(repoSlug || '').trim();
|
|
139
|
+
if (!/^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/.test(normalizedSlug)) {
|
|
140
|
+
throw new Error(`Invalid repoSlug format: ${repoSlug} (expected "owner/repo")`);
|
|
141
|
+
}
|
|
142
|
+
return `https://github.com/${normalizedSlug}/compare/${fromRef}...${toRef}`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function serializeGithubReleaseNotes({
|
|
146
|
+
ir,
|
|
147
|
+
fromRef,
|
|
148
|
+
toRef,
|
|
149
|
+
repoSlug = defaultRepoSlug,
|
|
150
|
+
installCommand = `npx ${packageName}@latest`,
|
|
151
|
+
}) {
|
|
152
|
+
if (installCommand.includes('`')) {
|
|
153
|
+
throw new Error('installCommand cannot contain backtick characters');
|
|
154
|
+
}
|
|
155
|
+
const lines = [];
|
|
156
|
+
for (const section of ir.sections) {
|
|
157
|
+
lines.push(`## ${section.type}`);
|
|
158
|
+
lines.push('');
|
|
159
|
+
for (const group of section.groups) {
|
|
160
|
+
lines.push(`### ${group.title}`);
|
|
161
|
+
for (const bullet of group.bullets) {
|
|
162
|
+
lines.push(formatBullet(bullet));
|
|
163
|
+
}
|
|
164
|
+
lines.push('');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
lines.push('---');
|
|
168
|
+
lines.push('');
|
|
169
|
+
lines.push(`Install/upgrade: \`${installCommand}\``);
|
|
170
|
+
lines.push('');
|
|
171
|
+
lines.push(`**Full Changelog**: ${compareUrl({ repoSlug, fromRef, toRef })}`);
|
|
172
|
+
lines.push('');
|
|
173
|
+
return lines.join('\n');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function renderGithubReleaseNotes(options) {
|
|
177
|
+
const { fragments, failures } = loadFragmentsFromRange(options);
|
|
178
|
+
if (failures.length > 0) {
|
|
179
|
+
return { ok: false, fragments, failures, body: null };
|
|
180
|
+
}
|
|
181
|
+
const ir = buildGithubReleaseNotesIr({ fragments });
|
|
182
|
+
return {
|
|
183
|
+
ok: true,
|
|
184
|
+
fragments,
|
|
185
|
+
failures: [],
|
|
186
|
+
ir,
|
|
187
|
+
body: serializeGithubReleaseNotes({ ir, ...options }),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
module.exports = {
|
|
192
|
+
changedFragmentPaths,
|
|
193
|
+
loadFragmentsFromRange,
|
|
194
|
+
buildGithubReleaseNotesIr,
|
|
195
|
+
serializeGithubReleaseNotes,
|
|
196
|
+
renderGithubReleaseNotes,
|
|
197
|
+
classifyGroup,
|
|
198
|
+
validateGitRef,
|
|
199
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Changeset-fragment lint (#2975).
|
|
6
|
+
*
|
|
7
|
+
* Pure verdict function evaluateLint({ changedFiles, labels }) returns
|
|
8
|
+
* { ok, reason } using the LINT_REASON enum. The CLI wrapper calls it with
|
|
9
|
+
* the PR diff (via `git diff --name-only origin/main...HEAD` or the GitHub
|
|
10
|
+
* Actions event payload) and the labels list (via the GitHub event).
|
|
11
|
+
*
|
|
12
|
+
* Tests assert on the typed verdict, never on free text.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const LINT_REASON = Object.freeze({
|
|
16
|
+
OK_FRAGMENT_PRESENT: 'ok_fragment_present',
|
|
17
|
+
OK_OPT_OUT_LABEL: 'ok_opt_out_label',
|
|
18
|
+
OK_NO_USER_FACING_CHANGES: 'ok_no_user_facing_changes',
|
|
19
|
+
FAIL_MISSING_FRAGMENT: 'fail_missing_fragment',
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const OPT_OUT_LABEL = 'no-changelog';
|
|
23
|
+
|
|
24
|
+
// Files counted as "user-facing" — touching any of these requires either a
|
|
25
|
+
// fragment or an explicit opt-out label. Test/CI/docs/lock files do not.
|
|
26
|
+
const USER_FACING_PREFIXES = [
|
|
27
|
+
'bin/',
|
|
28
|
+
'get-shit-done/',
|
|
29
|
+
'agents/',
|
|
30
|
+
'commands/',
|
|
31
|
+
'hooks/',
|
|
32
|
+
'sdk/src/',
|
|
33
|
+
'sdk/prompts/',
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
// Exact-match user-facing files. Any direct edit to one of these without a
|
|
37
|
+
// fragment also fails the lint — closes the bypass where a contributor edits
|
|
38
|
+
// CHANGELOG.md directly to sneak past the new workflow.
|
|
39
|
+
const USER_FACING_FILES = new Set(['CHANGELOG.md']);
|
|
40
|
+
|
|
41
|
+
function isUserFacing(file) {
|
|
42
|
+
if (USER_FACING_FILES.has(file)) return true;
|
|
43
|
+
return USER_FACING_PREFIXES.some((p) => file.startsWith(p));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isFragment(file) {
|
|
47
|
+
return /^\.changeset\/[^/]+\.md$/.test(file) && !file.endsWith('/README.md');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function evaluateLint({ changedFiles, labels }) {
|
|
51
|
+
if (changedFiles.some(isFragment)) {
|
|
52
|
+
return { ok: true, reason: LINT_REASON.OK_FRAGMENT_PRESENT };
|
|
53
|
+
}
|
|
54
|
+
if (labels.includes(OPT_OUT_LABEL)) {
|
|
55
|
+
return { ok: true, reason: LINT_REASON.OK_OPT_OUT_LABEL };
|
|
56
|
+
}
|
|
57
|
+
if (!changedFiles.some(isUserFacing)) {
|
|
58
|
+
return { ok: true, reason: LINT_REASON.OK_NO_USER_FACING_CHANGES };
|
|
59
|
+
}
|
|
60
|
+
return { ok: false, reason: LINT_REASON.FAIL_MISSING_FRAGMENT };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function main() {
|
|
64
|
+
const fs = require('node:fs');
|
|
65
|
+
const cp = require('node:child_process');
|
|
66
|
+
// GitHub Actions event payload path
|
|
67
|
+
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
68
|
+
let labels = [];
|
|
69
|
+
if (eventPath && fs.existsSync(eventPath)) {
|
|
70
|
+
try {
|
|
71
|
+
const event = JSON.parse(fs.readFileSync(eventPath, 'utf8'));
|
|
72
|
+
labels = (event.pull_request?.labels || []).map((l) => l.name);
|
|
73
|
+
} catch { /* fall through */ }
|
|
74
|
+
}
|
|
75
|
+
const base = process.env.GITHUB_BASE_REF || 'main';
|
|
76
|
+
let changedFiles = [];
|
|
77
|
+
try {
|
|
78
|
+
// Use execFileSync with an argv array — the base ref is interpolated
|
|
79
|
+
// into a refspec argument, but execFileSync does not invoke a shell, so
|
|
80
|
+
// even a malicious GITHUB_BASE_REF cannot inject shell syntax. The
|
|
81
|
+
// refspec-bound metacharacters that git itself rejects (e.g. spaces in
|
|
82
|
+
// ref names) are caught by git's own arg parser.
|
|
83
|
+
const out = cp.execFileSync(
|
|
84
|
+
'git',
|
|
85
|
+
['diff', '--name-only', `origin/${base}...HEAD`],
|
|
86
|
+
{ encoding: 'utf8' },
|
|
87
|
+
);
|
|
88
|
+
changedFiles = out.split('\n').filter(Boolean);
|
|
89
|
+
} catch (e) {
|
|
90
|
+
process.stderr.write(`could not compute diff: ${e.message}\n`);
|
|
91
|
+
process.exit(2);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const verdict = evaluateLint({ changedFiles, labels });
|
|
95
|
+
if (process.argv.includes('--json')) {
|
|
96
|
+
process.stdout.write(JSON.stringify({ ...verdict, changedFiles, labels }, null, 2) + '\n');
|
|
97
|
+
} else if (verdict.ok) {
|
|
98
|
+
process.stdout.write(`ok changeset-lint: ${verdict.reason}\n`);
|
|
99
|
+
} else {
|
|
100
|
+
process.stderr.write(`\nERROR changeset-lint: ${verdict.reason}\n`);
|
|
101
|
+
process.stderr.write(`PR touches user-facing files but does not include a .changeset/*.md fragment.\n`);
|
|
102
|
+
process.stderr.write(`Run \`npm run changeset\` to create one, or add the \`${OPT_OUT_LABEL}\` label\n`);
|
|
103
|
+
process.stderr.write(`if this PR genuinely has no user-facing impact (test refactor, CI tweak, etc.).\n`);
|
|
104
|
+
}
|
|
105
|
+
process.exit(verdict.ok ? 0 : 1);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (require.main === module) main();
|
|
109
|
+
|
|
110
|
+
module.exports = { evaluateLint, LINT_REASON, OPT_OUT_LABEL, isUserFacing, isFragment };
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Scaffolds a new changeset fragment (#2975).
|
|
6
|
+
*
|
|
7
|
+
* npm run changeset -- --type Fixed --pr 1234 --body "fix the thing"
|
|
8
|
+
*
|
|
9
|
+
* Writes `.changeset/<adjective>-<noun>-<noun>.md` with frontmatter
|
|
10
|
+
* + body. The random three-word filename minimizes filename collision
|
|
11
|
+
* across concurrent PRs.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('node:fs');
|
|
15
|
+
const path = require('node:path');
|
|
16
|
+
|
|
17
|
+
// Small word lists — keep the function simple and dependency-free.
|
|
18
|
+
// Together this gives ~40 * 40 * 40 = 64,000 distinct names. The lint
|
|
19
|
+
// rejects any duplicate filename, so collisions are caught even when
|
|
20
|
+
// the random draw repeats.
|
|
21
|
+
const ADJECTIVES = [
|
|
22
|
+
'silly', 'brave', 'calm', 'eager', 'gentle', 'happy', 'jolly', 'kind',
|
|
23
|
+
'lively', 'merry', 'nimble', 'plucky', 'quick', 'sturdy', 'witty', 'zesty',
|
|
24
|
+
'bold', 'clever', 'daring', 'fierce', 'graceful', 'humble', 'lucky', 'noble',
|
|
25
|
+
'proud', 'rapid', 'sharp', 'tidy', 'vivid', 'wise', 'agile', 'curious',
|
|
26
|
+
'eager', 'gallant', 'mellow', 'patient', 'serene', 'steady', 'sturdy', 'sunny',
|
|
27
|
+
];
|
|
28
|
+
const NOUNS_A = [
|
|
29
|
+
'bears', 'birds', 'cats', 'dogs', 'elks', 'foxes', 'goats', 'hawks',
|
|
30
|
+
'ibex', 'jays', 'koalas', 'lynx', 'moles', 'newts', 'otters', 'pumas',
|
|
31
|
+
'quails', 'rams', 'seals', 'tigers', 'voles', 'wolves', 'yaks', 'zebras',
|
|
32
|
+
'badgers', 'cranes', 'deer', 'eagles', 'finches', 'geese', 'herons', 'jaguars',
|
|
33
|
+
'lemurs', 'mice', 'orcas', 'pandas', 'ravens', 'sloths', 'tunas', 'wasps',
|
|
34
|
+
];
|
|
35
|
+
const NOUNS_B = [
|
|
36
|
+
'dance', 'sing', 'leap', 'run', 'jump', 'climb', 'fly', 'swim',
|
|
37
|
+
'rest', 'wake', 'roam', 'greet', 'wander', 'gather', 'forage', 'travel',
|
|
38
|
+
'glide', 'sprint', 'tumble', 'wave', 'cheer', 'rally', 'parade', 'march',
|
|
39
|
+
'hop', 'frolic', 'caper', 'romp', 'zip', 'dart', 'snooze', 'munch',
|
|
40
|
+
'chatter', 'squeak', 'howl', 'bark', 'purr', 'roar', 'hum', 'click',
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
function pick(arr) {
|
|
44
|
+
return arr[Math.floor(Math.random() * arr.length)];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function generateFragmentName() {
|
|
48
|
+
return `${pick(ADJECTIVES)}-${pick(NOUNS_A)}-${pick(NOUNS_B)}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Allowed Keep-a-Changelog section types. Used by both scaffoldFragment
|
|
52
|
+
// (sanitization at write time) and parse.cjs (validation at consume time).
|
|
53
|
+
const ALLOWED_TYPES = new Set(['Added', 'Changed', 'Deprecated', 'Removed', 'Fixed', 'Security']);
|
|
54
|
+
|
|
55
|
+
function scaffoldFragment({ repo, type, pr, body }) {
|
|
56
|
+
// Sanitize: reject any type value not on the allowlist BEFORE embedding it
|
|
57
|
+
// in frontmatter. A newline in `type` would corrupt the fragment; an
|
|
58
|
+
// unrecognized value would be rejected later by parse.cjs but with a
|
|
59
|
+
// confusing diagnostic. Catch both at the write boundary.
|
|
60
|
+
if (!ALLOWED_TYPES.has(type)) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`scaffoldFragment: type=${JSON.stringify(type)} is not one of [${[...ALLOWED_TYPES].join(', ')}]`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
const dir = path.join(repo, '.changeset');
|
|
66
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
67
|
+
const content = `---\ntype: ${type}\npr: ${pr}\n---\n${body}\n`;
|
|
68
|
+
// Atomic create: writeFileSync with `flag: 'wx'` fails (EEXIST) when the
|
|
69
|
+
// file already exists, so concurrent invocations can't race past
|
|
70
|
+
// `existsSync` and overwrite each other. Re-roll the random name on
|
|
71
|
+
// collision; fail loudly after exhausting the retry budget.
|
|
72
|
+
for (let i = 0; i < 16; i++) {
|
|
73
|
+
const name = generateFragmentName();
|
|
74
|
+
const target = path.join(dir, `${name}.md`);
|
|
75
|
+
try {
|
|
76
|
+
fs.writeFileSync(target, content, { flag: 'wx' });
|
|
77
|
+
return target;
|
|
78
|
+
} catch (e) {
|
|
79
|
+
if (e.code !== 'EEXIST') throw e;
|
|
80
|
+
// collision — try another random draw
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
throw new Error(
|
|
84
|
+
'scaffoldFragment: 16 random filename draws all collided; ' +
|
|
85
|
+
'expand the word lists or investigate corrupted .changeset/ state',
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function parseArgs(argv) {
|
|
90
|
+
const opts = { type: null, pr: null, body: null, repo: process.cwd() };
|
|
91
|
+
// Validate flag values: argv[++i] could be undefined (flag with no value)
|
|
92
|
+
// or another flag (silently misparsed). Match the cli.cjs convention: return
|
|
93
|
+
// { ok: true, opts } on success, { ok: false, error } on malformed input.
|
|
94
|
+
const requireValue = (flag, i) => {
|
|
95
|
+
const v = argv[i + 1];
|
|
96
|
+
if (v === undefined || v.startsWith('--')) {
|
|
97
|
+
return { ok: false, error: `missing value for ${flag}` };
|
|
98
|
+
}
|
|
99
|
+
return { ok: true, value: v };
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
for (let i = 0; i < argv.length; i++) {
|
|
103
|
+
const a = argv[i];
|
|
104
|
+
if (a === '--type' || a === '--pr' || a === '--body' || a === '--repo') {
|
|
105
|
+
const r = requireValue(a, i);
|
|
106
|
+
if (!r.ok) return { ok: false, error: r.error };
|
|
107
|
+
if (a === '--type') opts.type = r.value;
|
|
108
|
+
else if (a === '--pr') opts.pr = Number(r.value);
|
|
109
|
+
else if (a === '--body') opts.body = r.value;
|
|
110
|
+
else if (a === '--repo') opts.repo = r.value;
|
|
111
|
+
i++;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
return { ok: false, error: `unknown argument: ${a}` };
|
|
115
|
+
}
|
|
116
|
+
return { ok: true, opts };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function main() {
|
|
120
|
+
const parsed = parseArgs(process.argv.slice(2));
|
|
121
|
+
if (!parsed.ok) {
|
|
122
|
+
process.stderr.write(`${parsed.error}\n`);
|
|
123
|
+
process.stderr.write('usage: changeset/new.cjs --type <Fixed|Added|...> --pr NNNN --body "..."\n');
|
|
124
|
+
process.exit(2);
|
|
125
|
+
}
|
|
126
|
+
const { opts } = parsed;
|
|
127
|
+
if (!opts.type || !opts.pr || !opts.body) {
|
|
128
|
+
process.stderr.write('usage: changeset/new.cjs --type <Fixed|Added|...> --pr NNNN --body "..."\n');
|
|
129
|
+
process.exit(2);
|
|
130
|
+
}
|
|
131
|
+
const file = scaffoldFragment(opts);
|
|
132
|
+
process.stdout.write(`${path.relative(process.cwd(), file)}\n`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (require.main === module) main();
|
|
136
|
+
|
|
137
|
+
module.exports = { generateFragmentName, scaffoldFragment, parseArgs, ALLOWED_TYPES };
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parses a changeset fragment file (text → typed record).
|
|
5
|
+
*
|
|
6
|
+
* ---
|
|
7
|
+
* type: Fixed
|
|
8
|
+
* pr: 2975
|
|
9
|
+
* ---
|
|
10
|
+
* <markdown body>
|
|
11
|
+
*
|
|
12
|
+
* Returns { ok: true, fragment: { type, pr, body, docsExempt } } on success,
|
|
13
|
+
* { ok: false, reason: FRAGMENT_ERROR.X, detail } on failure.
|
|
14
|
+
*
|
|
15
|
+
* `docsExempt` is `null` when the body contains no docs-exempt marker, or the
|
|
16
|
+
* trimmed reason string when the body contains `<!-- docs-exempt: <reason> -->`
|
|
17
|
+
* (#3213). The marker is stripped from `body` at parse time so it never bleeds
|
|
18
|
+
* into the CHANGELOG.md or GitHub release-notes serializers, which append the
|
|
19
|
+
* `(#NNNN)` PR suffix verbatim to the body's last line.
|
|
20
|
+
*
|
|
21
|
+
* The reason field is a frozen enum so tests assert on stable codes,
|
|
22
|
+
* not free-text error messages (CONTRIBUTING.md: "Prohibited: Raw
|
|
23
|
+
* Text Matching on Test Outputs").
|
|
24
|
+
*/
|
|
25
|
+
const FRAGMENT_ERROR = Object.freeze({
|
|
26
|
+
MISSING_FRONTMATTER: 'missing_frontmatter',
|
|
27
|
+
MISSING_TYPE: 'missing_type',
|
|
28
|
+
INVALID_TYPE: 'invalid_type',
|
|
29
|
+
MISSING_PR: 'missing_pr',
|
|
30
|
+
INVALID_PR: 'invalid_pr',
|
|
31
|
+
EMPTY_BODY: 'empty_body',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const ALLOWED_TYPES = new Set(['Added', 'Changed', 'Deprecated', 'Removed', 'Fixed', 'Security']);
|
|
35
|
+
|
|
36
|
+
// HTML comment marking a fragment as exempt from the docs-required lint (#3213).
|
|
37
|
+
// Form: `<!-- docs-exempt: <reason> -->`. The reason is the *required* human
|
|
38
|
+
// audit trail — without it the exemption has no paper-trail value, so a bare
|
|
39
|
+
// `<!-- docs-exempt -->` or empty `<!-- docs-exempt: -->` is intentionally
|
|
40
|
+
// rejected (the colon and a non-whitespace first reason char are mandatory).
|
|
41
|
+
//
|
|
42
|
+
// Anchored with `^...$` + `m` flag so the marker only counts when it occupies
|
|
43
|
+
// its own line. Inline mentions inside paragraphs (e.g. backtick-wrapped
|
|
44
|
+
// syntax examples in documentation) are not matched — they cannot
|
|
45
|
+
// accidentally exempt a fragment.
|
|
46
|
+
//
|
|
47
|
+
// The trailing `\r?` consumes the CR character of a CRLF line terminator,
|
|
48
|
+
// which the `$` boundary (multiline mode) does not — so Windows-authored
|
|
49
|
+
// fragments produce the same `body` shape as LF-authored ones. The reason
|
|
50
|
+
// character class `[^\r\n>]` excludes `\r` for the same reason: a CRLF
|
|
51
|
+
// fragment's reason text never carries a trailing `\r`.
|
|
52
|
+
//
|
|
53
|
+
// Bounded character class `[^\r\n>]` keeps the regex linear-time — no
|
|
54
|
+
// catastrophic backtracking on adversarial input. The leading `\S` anchor
|
|
55
|
+
// inside the capture group forces at least one non-whitespace character in
|
|
56
|
+
// the reason; trailing whitespace before `-->` is consumed by the outer
|
|
57
|
+
// `[ \t]*-->` and is not part of the captured reason.
|
|
58
|
+
const DOCS_EXEMPT_RE = /^[ \t]*<!--[ \t]*docs-exempt[ \t]*:[ \t]*(\S[^\r\n>]*?)[ \t]*-->[ \t]*\r?$/im;
|
|
59
|
+
|
|
60
|
+
function extractDocsExempt(body) {
|
|
61
|
+
const m = body.match(DOCS_EXEMPT_RE);
|
|
62
|
+
if (!m) return { docsExempt: null, body };
|
|
63
|
+
const reason = (m[1] || '').trim();
|
|
64
|
+
// Strip the marker line and tidy up the surrounding whitespace. The cleanup
|
|
65
|
+
// is CRLF-aware so Windows-authored fragments don't leave residual `\r`
|
|
66
|
+
// characters that would shift the `(#NNNN)` PR suffix to a blank line in
|
|
67
|
+
// the rendered CHANGELOG.md / GitHub release-notes bullet.
|
|
68
|
+
const cleaned = body
|
|
69
|
+
.replace(DOCS_EXEMPT_RE, '')
|
|
70
|
+
.replace(/[ \t\r]+$/gm, '') // strip trailing \r/spaces on each line
|
|
71
|
+
.replace(/(?:\r?\n){3,}/g, '\n\n') // collapse 3+ blank lines (CRLF-aware)
|
|
72
|
+
.replace(/[\r\n]+$/, ''); // strip every trailing line terminator
|
|
73
|
+
return { docsExempt: reason, body: cleaned };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function parseFragment(src) {
|
|
77
|
+
const fmMatch = src.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
|
|
78
|
+
if (!fmMatch) return { ok: false, reason: FRAGMENT_ERROR.MISSING_FRONTMATTER };
|
|
79
|
+
const [, fmBlock, body] = fmMatch;
|
|
80
|
+
|
|
81
|
+
const fields = {};
|
|
82
|
+
for (const line of fmBlock.split(/\r?\n/)) {
|
|
83
|
+
const m = line.match(/^([a-zA-Z0-9_-]+):\s*(.*)$/);
|
|
84
|
+
if (m) fields[m[1]] = m[2].trim();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!fields.type) return { ok: false, reason: FRAGMENT_ERROR.MISSING_TYPE };
|
|
88
|
+
if (!ALLOWED_TYPES.has(fields.type)) {
|
|
89
|
+
return { ok: false, reason: FRAGMENT_ERROR.INVALID_TYPE, detail: fields.type };
|
|
90
|
+
}
|
|
91
|
+
if (!fields.pr) return { ok: false, reason: FRAGMENT_ERROR.MISSING_PR };
|
|
92
|
+
const pr = Number(fields.pr);
|
|
93
|
+
if (!Number.isInteger(pr) || pr <= 0) {
|
|
94
|
+
return { ok: false, reason: FRAGMENT_ERROR.INVALID_PR, detail: fields.pr };
|
|
95
|
+
}
|
|
96
|
+
// Use trim() only for the emptiness check; preserve the body verbatim
|
|
97
|
+
// (including significant leading/trailing whitespace, code blocks, etc.)
|
|
98
|
+
// so render → serialize round-trips exactly. Strip the single trailing
|
|
99
|
+
// line terminator added by editors so byte-equality holds for typical
|
|
100
|
+
// fragments. CRLF-aware: a Windows-authored fragment trims `\r\n` so the
|
|
101
|
+
// marker line in extractDocsExempt does not leave residual `\r` characters
|
|
102
|
+
// for downstream serializers to attach `(#NNNN)` to (#3213).
|
|
103
|
+
if (!body.trim()) return { ok: false, reason: FRAGMENT_ERROR.EMPTY_BODY };
|
|
104
|
+
let verbatimBody;
|
|
105
|
+
if (body.endsWith('\r\n')) verbatimBody = body.slice(0, -2);
|
|
106
|
+
else if (body.endsWith('\n')) verbatimBody = body.slice(0, -1);
|
|
107
|
+
else verbatimBody = body;
|
|
108
|
+
const { docsExempt, body: visibleBody } = extractDocsExempt(verbatimBody);
|
|
109
|
+
if (!visibleBody.trim()) return { ok: false, reason: FRAGMENT_ERROR.EMPTY_BODY };
|
|
110
|
+
|
|
111
|
+
return { ok: true, fragment: { type: fields.type, pr, body: visibleBody, docsExempt } };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = { parseFragment, extractDocsExempt, FRAGMENT_ERROR, ALLOWED_TYPES, DOCS_EXEMPT_RE };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pure renderer for the changeset-fragment workflow (#2975).
|
|
5
|
+
*
|
|
6
|
+
* Returns a typed Changelog IR — no file I/O. The IR is the contract that
|
|
7
|
+
* tests assert on; the markdown serializer is a separate concern.
|
|
8
|
+
*
|
|
9
|
+
* IR shape: {
|
|
10
|
+
* releaseHeader: { version: string, date: string },
|
|
11
|
+
* sections: [{ type: string, bullets: [{ pr: number, body: string }] }],
|
|
12
|
+
* priorChangelog: string | null,
|
|
13
|
+
* }
|
|
14
|
+
*/
|
|
15
|
+
// Keep a Changelog (https://keepachangelog.com) standard section order.
|
|
16
|
+
const SECTION_ORDER = ['Added', 'Changed', 'Deprecated', 'Removed', 'Fixed', 'Security'];
|
|
17
|
+
|
|
18
|
+
function renderChangelog({ fragments, version, date, priorChangelog }) {
|
|
19
|
+
const byType = new Map();
|
|
20
|
+
for (const f of fragments) {
|
|
21
|
+
if (!byType.has(f.type)) byType.set(f.type, []);
|
|
22
|
+
byType.get(f.type).push({ pr: f.pr, body: f.body });
|
|
23
|
+
}
|
|
24
|
+
const sections = SECTION_ORDER
|
|
25
|
+
.filter((type) => byType.has(type))
|
|
26
|
+
.map((type) => ({ type, bullets: byType.get(type) }));
|
|
27
|
+
return {
|
|
28
|
+
releaseHeader: { version, date },
|
|
29
|
+
sections,
|
|
30
|
+
priorChangelog: priorChangelog || null,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = { renderChangelog };
|