@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.
Files changed (503) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja-JP.md +870 -0
  3. package/README.ko-KR.md +861 -0
  4. package/README.md +301 -0
  5. package/README.pt-BR.md +492 -0
  6. package/README.zh-CN.md +842 -0
  7. package/agents/gsd-advisor-researcher.md +127 -0
  8. package/agents/gsd-ai-researcher.md +133 -0
  9. package/agents/gsd-assumptions-analyzer.md +105 -0
  10. package/agents/gsd-code-fixer.md +668 -0
  11. package/agents/gsd-code-reviewer.md +387 -0
  12. package/agents/gsd-codebase-mapper.md +853 -0
  13. package/agents/gsd-debug-session-manager.md +314 -0
  14. package/agents/gsd-debugger.md +1452 -0
  15. package/agents/gsd-doc-classifier.md +168 -0
  16. package/agents/gsd-doc-synthesizer.md +204 -0
  17. package/agents/gsd-doc-verifier.md +217 -0
  18. package/agents/gsd-doc-writer.md +615 -0
  19. package/agents/gsd-domain-researcher.md +153 -0
  20. package/agents/gsd-eval-auditor.md +191 -0
  21. package/agents/gsd-eval-planner.md +154 -0
  22. package/agents/gsd-executor.md +772 -0
  23. package/agents/gsd-framework-selector.md +160 -0
  24. package/agents/gsd-integration-checker.md +470 -0
  25. package/agents/gsd-intel-updater.md +342 -0
  26. package/agents/gsd-nyquist-auditor.md +203 -0
  27. package/agents/gsd-pattern-mapper.md +335 -0
  28. package/agents/gsd-phase-researcher.md +928 -0
  29. package/agents/gsd-plan-checker.md +978 -0
  30. package/agents/gsd-planner.md +1218 -0
  31. package/agents/gsd-project-researcher.md +677 -0
  32. package/agents/gsd-research-synthesizer.md +255 -0
  33. package/agents/gsd-roadmapper.md +688 -0
  34. package/agents/gsd-security-auditor.md +155 -0
  35. package/agents/gsd-ui-auditor.md +495 -0
  36. package/agents/gsd-ui-checker.md +309 -0
  37. package/agents/gsd-ui-researcher.md +380 -0
  38. package/agents/gsd-user-profiler.md +171 -0
  39. package/agents/gsd-verifier.md +917 -0
  40. package/bin/install.js +10936 -0
  41. package/bin/lib/ui-safety-gate.cjs +107 -0
  42. package/commands/gsd/add-tests.md +42 -0
  43. package/commands/gsd/ai-integration-phase.md +37 -0
  44. package/commands/gsd/audit-fix.md +34 -0
  45. package/commands/gsd/audit-milestone.md +37 -0
  46. package/commands/gsd/audit-uat.md +24 -0
  47. package/commands/gsd/autonomous.md +46 -0
  48. package/commands/gsd/capture.md +62 -0
  49. package/commands/gsd/cleanup.md +24 -0
  50. package/commands/gsd/code-review.md +59 -0
  51. package/commands/gsd/complete-milestone.md +143 -0
  52. package/commands/gsd/config.md +56 -0
  53. package/commands/gsd/debug.md +52 -0
  54. package/commands/gsd/discuss-phase.md +76 -0
  55. package/commands/gsd/docs-update.md +49 -0
  56. package/commands/gsd/eval-review.md +33 -0
  57. package/commands/gsd/execute-phase.md +64 -0
  58. package/commands/gsd/explore.md +27 -0
  59. package/commands/gsd/extract-learnings.md +23 -0
  60. package/commands/gsd/fast.md +31 -0
  61. package/commands/gsd/forensics.md +57 -0
  62. package/commands/gsd/graphify.md +199 -0
  63. package/commands/gsd/health.md +31 -0
  64. package/commands/gsd/help.md +28 -0
  65. package/commands/gsd/import.md +41 -0
  66. package/commands/gsd/inbox.md +39 -0
  67. package/commands/gsd/ingest-docs.md +42 -0
  68. package/commands/gsd/manager.md +45 -0
  69. package/commands/gsd/map-codebase.md +83 -0
  70. package/commands/gsd/milestone-summary.md +51 -0
  71. package/commands/gsd/mvp-phase.md +45 -0
  72. package/commands/gsd/new-milestone.md +45 -0
  73. package/commands/gsd/new-project.md +47 -0
  74. package/commands/gsd/ns-context.md +23 -0
  75. package/commands/gsd/ns-ideate.md +24 -0
  76. package/commands/gsd/ns-manage.md +29 -0
  77. package/commands/gsd/ns-project.md +22 -0
  78. package/commands/gsd/ns-review.md +26 -0
  79. package/commands/gsd/ns-workflow.md +28 -0
  80. package/commands/gsd/pause-work.md +43 -0
  81. package/commands/gsd/phase.md +56 -0
  82. package/commands/gsd/plan-phase.md +62 -0
  83. package/commands/gsd/plan-review-convergence.md +59 -0
  84. package/commands/gsd/pr-branch.md +26 -0
  85. package/commands/gsd/profile-user.md +46 -0
  86. package/commands/gsd/progress.md +47 -0
  87. package/commands/gsd/quick.md +174 -0
  88. package/commands/gsd/resume-work.md +30 -0
  89. package/commands/gsd/review-backlog.md +63 -0
  90. package/commands/gsd/review.md +41 -0
  91. package/commands/gsd/secure-phase.md +36 -0
  92. package/commands/gsd/settings.md +29 -0
  93. package/commands/gsd/ship.md +24 -0
  94. package/commands/gsd/sketch.md +60 -0
  95. package/commands/gsd/spec-phase.md +63 -0
  96. package/commands/gsd/spike.md +57 -0
  97. package/commands/gsd/stats.md +19 -0
  98. package/commands/gsd/surface.md +155 -0
  99. package/commands/gsd/thread.md +24 -0
  100. package/commands/gsd/ui-phase.md +35 -0
  101. package/commands/gsd/ui-review.md +33 -0
  102. package/commands/gsd/ultraplan-phase.md +34 -0
  103. package/commands/gsd/undo.md +35 -0
  104. package/commands/gsd/update.md +48 -0
  105. package/commands/gsd/validate-phase.md +36 -0
  106. package/commands/gsd/verify-work.md +39 -0
  107. package/commands/gsd/workspace.md +52 -0
  108. package/commands/gsd/workstreams.md +70 -0
  109. package/get-shit-done/bin/check-latest-version.cjs +106 -0
  110. package/get-shit-done/bin/gsd-tools.cjs +1676 -0
  111. package/get-shit-done/bin/lib/active-workstream-store.cjs +302 -0
  112. package/get-shit-done/bin/lib/adr-parser.cjs +394 -0
  113. package/get-shit-done/bin/lib/agent-command-router.cjs +65 -0
  114. package/get-shit-done/bin/lib/artifacts.cjs +53 -0
  115. package/get-shit-done/bin/lib/audit.cjs +755 -0
  116. package/get-shit-done/bin/lib/check-command-router.cjs +333 -0
  117. package/get-shit-done/bin/lib/cjs-command-router-adapter.cjs +118 -0
  118. package/get-shit-done/bin/lib/clock.cjs +96 -0
  119. package/get-shit-done/bin/lib/clusters.cjs +135 -0
  120. package/get-shit-done/bin/lib/code-review-flags.cjs +74 -0
  121. package/get-shit-done/bin/lib/command-aliases.cjs +815 -0
  122. package/get-shit-done/bin/lib/command-arg-projection.cjs +62 -0
  123. package/get-shit-done/bin/lib/command-routing-hub.cjs +388 -0
  124. package/get-shit-done/bin/lib/commands.cjs +1188 -0
  125. package/get-shit-done/bin/lib/config-schema.cjs +31 -0
  126. package/get-shit-done/bin/lib/config.cjs +728 -0
  127. package/get-shit-done/bin/lib/configuration.cjs +248 -0
  128. package/get-shit-done/bin/lib/context-utilization.cjs +47 -0
  129. package/get-shit-done/bin/lib/core.cjs +2121 -0
  130. package/get-shit-done/bin/lib/decisions.cjs +116 -0
  131. package/get-shit-done/bin/lib/docs.cjs +270 -0
  132. package/get-shit-done/bin/lib/drift.cjs +388 -0
  133. package/get-shit-done/bin/lib/fallow-runner.cjs +109 -0
  134. package/get-shit-done/bin/lib/frontmatter.cjs +389 -0
  135. package/get-shit-done/bin/lib/gap-checker.cjs +205 -0
  136. package/get-shit-done/bin/lib/graphify.cjs +592 -0
  137. package/get-shit-done/bin/lib/gsd2-import.cjs +514 -0
  138. package/get-shit-done/bin/lib/init-command-router.cjs +58 -0
  139. package/get-shit-done/bin/lib/init.cjs +2112 -0
  140. package/get-shit-done/bin/lib/install-profiles.cjs +603 -0
  141. package/get-shit-done/bin/lib/installer-migration-authoring.cjs +117 -0
  142. package/get-shit-done/bin/lib/installer-migration-report.cjs +354 -0
  143. package/get-shit-done/bin/lib/installer-migrations/000-first-time-baseline.cjs +220 -0
  144. package/get-shit-done/bin/lib/installer-migrations/001-legacy-orphan-files.cjs +41 -0
  145. package/get-shit-done/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs +80 -0
  146. package/get-shit-done/bin/lib/installer-migrations.cjs +778 -0
  147. package/get-shit-done/bin/lib/intel.cjs +708 -0
  148. package/get-shit-done/bin/lib/learnings.cjs +421 -0
  149. package/get-shit-done/bin/lib/milestone.cjs +314 -0
  150. package/get-shit-done/bin/lib/model-catalog.cjs +212 -0
  151. package/get-shit-done/bin/lib/model-profiles.cjs +31 -0
  152. package/get-shit-done/bin/lib/observability/event.cjs +82 -0
  153. package/get-shit-done/bin/lib/observability/logger.cjs +174 -0
  154. package/get-shit-done/bin/lib/observability/redaction.cjs +50 -0
  155. package/get-shit-done/bin/lib/package-identity.cjs +31 -0
  156. package/get-shit-done/bin/lib/phase-command-router.cjs +191 -0
  157. package/get-shit-done/bin/lib/phase-lifecycle.cjs +80 -0
  158. package/get-shit-done/bin/lib/phase.cjs +1607 -0
  159. package/get-shit-done/bin/lib/phases-command-router.cjs +39 -0
  160. package/get-shit-done/bin/lib/plan-scan.cjs +97 -0
  161. package/get-shit-done/bin/lib/planning-workspace.cjs +238 -0
  162. package/get-shit-done/bin/lib/profile-output.cjs +1141 -0
  163. package/get-shit-done/bin/lib/profile-pipeline.cjs +539 -0
  164. package/get-shit-done/bin/lib/project-root.cjs +112 -0
  165. package/get-shit-done/bin/lib/prompt-budget.cjs +399 -0
  166. package/get-shit-done/bin/lib/review-reviewer-selection.cjs +125 -0
  167. package/get-shit-done/bin/lib/roadmap-command-router.cjs +28 -0
  168. package/get-shit-done/bin/lib/roadmap.cjs +650 -0
  169. package/get-shit-done/bin/lib/runtime-artifact-layout.cjs +301 -0
  170. package/get-shit-done/bin/lib/runtime-homes.cjs +222 -0
  171. package/get-shit-done/bin/lib/runtime-name-policy.cjs +83 -0
  172. package/get-shit-done/bin/lib/runtime-slash.cjs +112 -0
  173. package/get-shit-done/bin/lib/schema-detect.cjs +165 -0
  174. package/get-shit-done/bin/lib/secrets.cjs +32 -0
  175. package/get-shit-done/bin/lib/security.cjs +600 -0
  176. package/get-shit-done/bin/lib/semver-compare.cjs +35 -0
  177. package/get-shit-done/bin/lib/shell-command-projection.cjs +500 -0
  178. package/get-shit-done/bin/lib/state-command-router.cjs +252 -0
  179. package/get-shit-done/bin/lib/state-document.cjs +263 -0
  180. package/get-shit-done/bin/lib/state.cjs +2038 -0
  181. package/get-shit-done/bin/lib/surface.cjs +470 -0
  182. package/get-shit-done/bin/lib/task-command-router.cjs +81 -0
  183. package/get-shit-done/bin/lib/template.cjs +228 -0
  184. package/get-shit-done/bin/lib/uat.cjs +289 -0
  185. package/get-shit-done/bin/lib/update-context.cjs +209 -0
  186. package/get-shit-done/bin/lib/validate-command-router.cjs +83 -0
  187. package/get-shit-done/bin/lib/validate.cjs +92 -0
  188. package/get-shit-done/bin/lib/verify-command-router.cjs +40 -0
  189. package/get-shit-done/bin/lib/verify.cjs +1511 -0
  190. package/get-shit-done/bin/lib/workstream-inventory-builder.cjs +74 -0
  191. package/get-shit-done/bin/lib/workstream-inventory.cjs +146 -0
  192. package/get-shit-done/bin/lib/workstream-name-policy.cjs +94 -0
  193. package/get-shit-done/bin/lib/workstream.cjs +389 -0
  194. package/get-shit-done/bin/lib/worktree-safety.cjs +985 -0
  195. package/get-shit-done/bin/shared/config-defaults.manifest.json +97 -0
  196. package/get-shit-done/bin/shared/config-schema.manifest.json +175 -0
  197. package/get-shit-done/bin/shared/model-catalog.json +122 -0
  198. package/get-shit-done/bin/shared/runtime-aliases.manifest.json +75 -0
  199. package/get-shit-done/bin/verify-reapply-patches.cjs +352 -0
  200. package/get-shit-done/contexts/dev.md +21 -0
  201. package/get-shit-done/contexts/research.md +22 -0
  202. package/get-shit-done/contexts/review.md +23 -0
  203. package/get-shit-done/references/agent-contracts.md +79 -0
  204. package/get-shit-done/references/ai-evals.md +156 -0
  205. package/get-shit-done/references/ai-frameworks.md +186 -0
  206. package/get-shit-done/references/artifact-types.md +131 -0
  207. package/get-shit-done/references/autonomous-smart-discuss.md +277 -0
  208. package/get-shit-done/references/checkpoints.md +814 -0
  209. package/get-shit-done/references/common-bug-patterns.md +114 -0
  210. package/get-shit-done/references/context-budget.md +85 -0
  211. package/get-shit-done/references/continuation-format.md +253 -0
  212. package/get-shit-done/references/debugger-philosophy.md +76 -0
  213. package/get-shit-done/references/decimal-phase-calculation.md +64 -0
  214. package/get-shit-done/references/doc-conflict-engine.md +91 -0
  215. package/get-shit-done/references/domain-probes.md +125 -0
  216. package/get-shit-done/references/execute-mvp-tdd.md +81 -0
  217. package/get-shit-done/references/executor-examples.md +110 -0
  218. package/get-shit-done/references/few-shot-examples/plan-checker.md +73 -0
  219. package/get-shit-done/references/few-shot-examples/verifier.md +109 -0
  220. package/get-shit-done/references/gate-prompts.md +100 -0
  221. package/get-shit-done/references/gates.md +70 -0
  222. package/get-shit-done/references/git-integration.md +298 -0
  223. package/get-shit-done/references/git-planning-commit.md +40 -0
  224. package/get-shit-done/references/ios-scaffold.md +123 -0
  225. package/get-shit-done/references/mandatory-initial-read.md +2 -0
  226. package/get-shit-done/references/model-profile-resolution.md +38 -0
  227. package/get-shit-done/references/model-profiles.md +245 -0
  228. package/get-shit-done/references/mvp-concepts.md +49 -0
  229. package/get-shit-done/references/phase-argument-parsing.md +61 -0
  230. package/get-shit-done/references/planner-antipatterns.md +89 -0
  231. package/get-shit-done/references/planner-chunked.md +49 -0
  232. package/get-shit-done/references/planner-gap-closure.md +62 -0
  233. package/get-shit-done/references/planner-graphify-auto-update.md +67 -0
  234. package/get-shit-done/references/planner-human-verify-mode.md +57 -0
  235. package/get-shit-done/references/planner-interface-context.md +62 -0
  236. package/get-shit-done/references/planner-mvp-mode.md +53 -0
  237. package/get-shit-done/references/planner-reviews.md +39 -0
  238. package/get-shit-done/references/planner-revision.md +87 -0
  239. package/get-shit-done/references/planner-source-audit.md +73 -0
  240. package/get-shit-done/references/planning-config.md +471 -0
  241. package/get-shit-done/references/project-skills-discovery.md +19 -0
  242. package/get-shit-done/references/questioning.md +162 -0
  243. package/get-shit-done/references/revision-loop.md +97 -0
  244. package/get-shit-done/references/scout-codebase.md +51 -0
  245. package/get-shit-done/references/skeleton-template.md +48 -0
  246. package/get-shit-done/references/sketch-interactivity.md +41 -0
  247. package/get-shit-done/references/sketch-theme-system.md +94 -0
  248. package/get-shit-done/references/sketch-tooling.md +45 -0
  249. package/get-shit-done/references/sketch-variant-patterns.md +81 -0
  250. package/get-shit-done/references/spidr-splitting.md +69 -0
  251. package/get-shit-done/references/tdd.md +330 -0
  252. package/get-shit-done/references/thinking-models-debug.md +44 -0
  253. package/get-shit-done/references/thinking-models-execution.md +50 -0
  254. package/get-shit-done/references/thinking-models-planning.md +62 -0
  255. package/get-shit-done/references/thinking-models-research.md +50 -0
  256. package/get-shit-done/references/thinking-models-verification.md +55 -0
  257. package/get-shit-done/references/thinking-partner.md +96 -0
  258. package/get-shit-done/references/ui-brand.md +160 -0
  259. package/get-shit-done/references/universal-anti-patterns.md +63 -0
  260. package/get-shit-done/references/user-profiling.md +681 -0
  261. package/get-shit-done/references/user-story-template.md +58 -0
  262. package/get-shit-done/references/verification-overrides.md +227 -0
  263. package/get-shit-done/references/verification-patterns.md +612 -0
  264. package/get-shit-done/references/verify-mvp-mode.md +85 -0
  265. package/get-shit-done/references/workstream-flag.md +111 -0
  266. package/get-shit-done/references/worktree-path-safety.md +89 -0
  267. package/get-shit-done/templates/AI-SPEC.md +246 -0
  268. package/get-shit-done/templates/DEBUG.md +169 -0
  269. package/get-shit-done/templates/README.md +77 -0
  270. package/get-shit-done/templates/SECURITY.md +61 -0
  271. package/get-shit-done/templates/UAT.md +265 -0
  272. package/get-shit-done/templates/UI-SPEC.md +100 -0
  273. package/get-shit-done/templates/VALIDATION.md +76 -0
  274. package/get-shit-done/templates/claude-md.md +145 -0
  275. package/get-shit-done/templates/codebase/architecture.md +255 -0
  276. package/get-shit-done/templates/codebase/concerns.md +310 -0
  277. package/get-shit-done/templates/codebase/conventions.md +307 -0
  278. package/get-shit-done/templates/codebase/integrations.md +280 -0
  279. package/get-shit-done/templates/codebase/stack.md +186 -0
  280. package/get-shit-done/templates/codebase/structure.md +285 -0
  281. package/get-shit-done/templates/codebase/testing.md +480 -0
  282. package/get-shit-done/templates/config.json +62 -0
  283. package/get-shit-done/templates/context.md +352 -0
  284. package/get-shit-done/templates/continue-here.md +78 -0
  285. package/get-shit-done/templates/copilot-instructions.md +7 -0
  286. package/get-shit-done/templates/debug-subagent-prompt.md +91 -0
  287. package/get-shit-done/templates/dev-preferences.md +21 -0
  288. package/get-shit-done/templates/discovery.md +146 -0
  289. package/get-shit-done/templates/discussion-log.md +63 -0
  290. package/get-shit-done/templates/milestone-archive.md +123 -0
  291. package/get-shit-done/templates/milestone.md +115 -0
  292. package/get-shit-done/templates/phase-prompt.md +610 -0
  293. package/get-shit-done/templates/planner-subagent-prompt.md +117 -0
  294. package/get-shit-done/templates/project.md +186 -0
  295. package/get-shit-done/templates/requirements.md +231 -0
  296. package/get-shit-done/templates/research-project/ARCHITECTURE.md +204 -0
  297. package/get-shit-done/templates/research-project/FEATURES.md +147 -0
  298. package/get-shit-done/templates/research-project/PITFALLS.md +200 -0
  299. package/get-shit-done/templates/research-project/STACK.md +120 -0
  300. package/get-shit-done/templates/research-project/SUMMARY.md +170 -0
  301. package/get-shit-done/templates/research.md +592 -0
  302. package/get-shit-done/templates/retrospective.md +54 -0
  303. package/get-shit-done/templates/roadmap.md +202 -0
  304. package/get-shit-done/templates/spec.md +307 -0
  305. package/get-shit-done/templates/state.md +195 -0
  306. package/get-shit-done/templates/summary-complex.md +59 -0
  307. package/get-shit-done/templates/summary-minimal.md +41 -0
  308. package/get-shit-done/templates/summary-standard.md +48 -0
  309. package/get-shit-done/templates/summary.md +248 -0
  310. package/get-shit-done/templates/user-profile.md +146 -0
  311. package/get-shit-done/templates/user-setup.md +311 -0
  312. package/get-shit-done/templates/verification-report.md +322 -0
  313. package/get-shit-done/workflows/_runtime-launcher.snippet.sh +1 -0
  314. package/get-shit-done/workflows/add-backlog.md +91 -0
  315. package/get-shit-done/workflows/add-phase.md +113 -0
  316. package/get-shit-done/workflows/add-tests.md +355 -0
  317. package/get-shit-done/workflows/add-todo.md +161 -0
  318. package/get-shit-done/workflows/ai-integration-phase.md +295 -0
  319. package/get-shit-done/workflows/analyze-dependencies.md +96 -0
  320. package/get-shit-done/workflows/audit-fix.md +178 -0
  321. package/get-shit-done/workflows/audit-milestone.md +358 -0
  322. package/get-shit-done/workflows/audit-uat.md +110 -0
  323. package/get-shit-done/workflows/autonomous.md +795 -0
  324. package/get-shit-done/workflows/check-todos.md +180 -0
  325. package/get-shit-done/workflows/cleanup.md +155 -0
  326. package/get-shit-done/workflows/code-review-fix.md +502 -0
  327. package/get-shit-done/workflows/code-review.md +656 -0
  328. package/get-shit-done/workflows/complete-milestone.md +855 -0
  329. package/get-shit-done/workflows/debug.md +232 -0
  330. package/get-shit-done/workflows/diagnose-issues.md +241 -0
  331. package/get-shit-done/workflows/discovery-phase.md +291 -0
  332. package/get-shit-done/workflows/discuss-phase/modes/advisor.md +176 -0
  333. package/get-shit-done/workflows/discuss-phase/modes/all.md +28 -0
  334. package/get-shit-done/workflows/discuss-phase/modes/analyze.md +44 -0
  335. package/get-shit-done/workflows/discuss-phase/modes/auto.md +57 -0
  336. package/get-shit-done/workflows/discuss-phase/modes/batch.md +52 -0
  337. package/get-shit-done/workflows/discuss-phase/modes/chain.md +98 -0
  338. package/get-shit-done/workflows/discuss-phase/modes/default.md +141 -0
  339. package/get-shit-done/workflows/discuss-phase/modes/power.md +44 -0
  340. package/get-shit-done/workflows/discuss-phase/modes/text.md +55 -0
  341. package/get-shit-done/workflows/discuss-phase/templates/checkpoint.json +18 -0
  342. package/get-shit-done/workflows/discuss-phase/templates/context.md +136 -0
  343. package/get-shit-done/workflows/discuss-phase/templates/discussion-log.md +50 -0
  344. package/get-shit-done/workflows/discuss-phase-assumptions.md +675 -0
  345. package/get-shit-done/workflows/discuss-phase-power.md +291 -0
  346. package/get-shit-done/workflows/discuss-phase.md +499 -0
  347. package/get-shit-done/workflows/do.md +111 -0
  348. package/get-shit-done/workflows/docs-update.md +1162 -0
  349. package/get-shit-done/workflows/edit-phase.md +295 -0
  350. package/get-shit-done/workflows/eval-review.md +156 -0
  351. package/get-shit-done/workflows/execute-phase/steps/codebase-drift-gate.md +82 -0
  352. package/get-shit-done/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
  353. package/get-shit-done/workflows/execute-phase/steps/post-merge-gate.md +117 -0
  354. package/get-shit-done/workflows/execute-phase.md +1709 -0
  355. package/get-shit-done/workflows/execute-plan.md +526 -0
  356. package/get-shit-done/workflows/explore.md +144 -0
  357. package/get-shit-done/workflows/extract-learnings.md +243 -0
  358. package/get-shit-done/workflows/fast.md +124 -0
  359. package/get-shit-done/workflows/forensics.md +279 -0
  360. package/get-shit-done/workflows/graduation.md +196 -0
  361. package/get-shit-done/workflows/health.md +224 -0
  362. package/get-shit-done/workflows/help/modes/brief.md +22 -0
  363. package/get-shit-done/workflows/help/modes/default.md +50 -0
  364. package/get-shit-done/workflows/help/modes/full.md +784 -0
  365. package/get-shit-done/workflows/help/modes/topic.md +74 -0
  366. package/get-shit-done/workflows/help.md +24 -0
  367. package/get-shit-done/workflows/import.md +254 -0
  368. package/get-shit-done/workflows/inbox.md +387 -0
  369. package/get-shit-done/workflows/ingest-docs.md +339 -0
  370. package/get-shit-done/workflows/insert-phase.md +152 -0
  371. package/get-shit-done/workflows/list-phase-assumptions.md +178 -0
  372. package/get-shit-done/workflows/list-workspaces.md +57 -0
  373. package/get-shit-done/workflows/manager.md +393 -0
  374. package/get-shit-done/workflows/map-codebase.md +444 -0
  375. package/get-shit-done/workflows/milestone-summary.md +224 -0
  376. package/get-shit-done/workflows/mvp-phase.md +222 -0
  377. package/get-shit-done/workflows/new-milestone.md +635 -0
  378. package/get-shit-done/workflows/new-project.md +1555 -0
  379. package/get-shit-done/workflows/new-workspace.md +240 -0
  380. package/get-shit-done/workflows/next.md +299 -0
  381. package/get-shit-done/workflows/node-repair.md +92 -0
  382. package/get-shit-done/workflows/note.md +158 -0
  383. package/get-shit-done/workflows/pause-work.md +244 -0
  384. package/get-shit-done/workflows/plan-milestone-gaps.md +281 -0
  385. package/get-shit-done/workflows/plan-phase.md +1809 -0
  386. package/get-shit-done/workflows/plan-review-convergence.md +346 -0
  387. package/get-shit-done/workflows/plant-seed.md +230 -0
  388. package/get-shit-done/workflows/pr-branch.md +157 -0
  389. package/get-shit-done/workflows/profile-user.md +453 -0
  390. package/get-shit-done/workflows/progress.md +699 -0
  391. package/get-shit-done/workflows/quick.md +1039 -0
  392. package/get-shit-done/workflows/reapply-patches.md +426 -0
  393. package/get-shit-done/workflows/remove-phase.md +156 -0
  394. package/get-shit-done/workflows/remove-workspace.md +108 -0
  395. package/get-shit-done/workflows/resume-project.md +332 -0
  396. package/get-shit-done/workflows/review.md +623 -0
  397. package/get-shit-done/workflows/scan.md +105 -0
  398. package/get-shit-done/workflows/secure-phase.md +180 -0
  399. package/get-shit-done/workflows/session-report.md +146 -0
  400. package/get-shit-done/workflows/settings-advanced.md +620 -0
  401. package/get-shit-done/workflows/settings-integrations.md +312 -0
  402. package/get-shit-done/workflows/settings.md +552 -0
  403. package/get-shit-done/workflows/ship.md +356 -0
  404. package/get-shit-done/workflows/sketch-wrap-up.md +286 -0
  405. package/get-shit-done/workflows/sketch.md +361 -0
  406. package/get-shit-done/workflows/spec-phase.md +262 -0
  407. package/get-shit-done/workflows/spike-wrap-up.md +307 -0
  408. package/get-shit-done/workflows/spike.md +453 -0
  409. package/get-shit-done/workflows/stats.md +80 -0
  410. package/get-shit-done/workflows/sync-skills.md +182 -0
  411. package/get-shit-done/workflows/thread.md +222 -0
  412. package/get-shit-done/workflows/transition.md +694 -0
  413. package/get-shit-done/workflows/ui-phase.md +328 -0
  414. package/get-shit-done/workflows/ui-review.md +193 -0
  415. package/get-shit-done/workflows/ultraplan-phase.md +199 -0
  416. package/get-shit-done/workflows/undo.md +314 -0
  417. package/get-shit-done/workflows/update.md +443 -0
  418. package/get-shit-done/workflows/validate-phase.md +179 -0
  419. package/get-shit-done/workflows/verify-phase.md +544 -0
  420. package/get-shit-done/workflows/verify-work.md +781 -0
  421. package/hooks/dist/gsd-check-update-worker.js +95 -0
  422. package/hooks/dist/gsd-check-update.js +64 -0
  423. package/hooks/dist/gsd-context-monitor.js +195 -0
  424. package/hooks/dist/gsd-graphify-update.sh +158 -0
  425. package/hooks/dist/gsd-phase-boundary.sh +47 -0
  426. package/hooks/dist/gsd-prompt-guard.js +97 -0
  427. package/hooks/dist/gsd-read-guard.js +101 -0
  428. package/hooks/dist/gsd-read-injection-scanner.js +203 -0
  429. package/hooks/dist/gsd-session-state.sh +59 -0
  430. package/hooks/dist/gsd-statusline.js +548 -0
  431. package/hooks/dist/gsd-update-banner.js +134 -0
  432. package/hooks/dist/gsd-validate-commit.sh +57 -0
  433. package/hooks/dist/gsd-workflow-guard.js +166 -0
  434. package/hooks/dist/lib/git-cmd.js +150 -0
  435. package/hooks/dist/lib/gsd-graphify-rebuild.sh +65 -0
  436. package/hooks/gsd-check-update-worker.js +95 -0
  437. package/hooks/gsd-check-update.js +64 -0
  438. package/hooks/gsd-context-monitor.js +195 -0
  439. package/hooks/gsd-graphify-update.sh +158 -0
  440. package/hooks/gsd-phase-boundary.sh +47 -0
  441. package/hooks/gsd-prompt-guard.js +97 -0
  442. package/hooks/gsd-read-guard.js +101 -0
  443. package/hooks/gsd-read-injection-scanner.js +203 -0
  444. package/hooks/gsd-session-state.sh +59 -0
  445. package/hooks/gsd-statusline.js +548 -0
  446. package/hooks/gsd-update-banner.js +134 -0
  447. package/hooks/gsd-validate-commit.sh +57 -0
  448. package/hooks/gsd-workflow-guard.js +166 -0
  449. package/hooks/lib/git-cmd.js +150 -0
  450. package/hooks/lib/gsd-graphify-rebuild.sh +65 -0
  451. package/hooks/managed-hooks-registry.cjs +34 -0
  452. package/package.json +102 -0
  453. package/scripts/affected-tests-lib.cjs +541 -0
  454. package/scripts/audit-workflow-script-paths.cjs +73 -0
  455. package/scripts/base64-scan.sh +339 -0
  456. package/scripts/build-hooks.js +236 -0
  457. package/scripts/changeset/README.md +129 -0
  458. package/scripts/changeset/cli.cjs +392 -0
  459. package/scripts/changeset/github-release-notes.cjs +199 -0
  460. package/scripts/changeset/lint.cjs +110 -0
  461. package/scripts/changeset/new.cjs +137 -0
  462. package/scripts/changeset/parse.cjs +114 -0
  463. package/scripts/changeset/render.cjs +34 -0
  464. package/scripts/changeset/serialize.cjs +130 -0
  465. package/scripts/check-alias-drift.cjs +108 -0
  466. package/scripts/check-env.cjs +302 -0
  467. package/scripts/check-npm-integrity.cjs +209 -0
  468. package/scripts/ci-guard-runner.cjs +16 -0
  469. package/scripts/ci-prepare-test-scope.cjs +46 -0
  470. package/scripts/ci-rebase-check.cjs +85 -0
  471. package/scripts/ci-test-scope.cjs +302 -0
  472. package/scripts/command-contract-helpers.cjs +64 -0
  473. package/scripts/diff-touches-shipped-paths.cjs +147 -0
  474. package/scripts/fix-slash-commands.cjs +147 -0
  475. package/scripts/gen-inventory-manifest.cjs +109 -0
  476. package/scripts/generate-package-identity.cjs +104 -0
  477. package/scripts/lint-command-contract.cjs +108 -0
  478. package/scripts/lint-descriptions.cjs +83 -0
  479. package/scripts/lint-docs-required.cjs +222 -0
  480. package/scripts/lint-no-source-grep-extras.cjs +81 -0
  481. package/scripts/lint-no-source-grep.cjs +174 -0
  482. package/scripts/lint-package-identity-drift.cjs +141 -0
  483. package/scripts/lint-pr-check-project-dir.cjs +98 -0
  484. package/scripts/lint-shared-module-handsync.cjs +388 -0
  485. package/scripts/lint-shell-command-projection-drift.cjs +57 -0
  486. package/scripts/lint-skill-deps.cjs +180 -0
  487. package/scripts/lint-test-file-count.allowlist.json +36 -0
  488. package/scripts/lint-test-file-count.cjs +190 -0
  489. package/scripts/pr-template-policy.cjs +268 -0
  490. package/scripts/prompt-injection-scan.sh +203 -0
  491. package/scripts/release-tarball-smoke.cjs +627 -0
  492. package/scripts/run-affected-tests.cjs +6 -0
  493. package/scripts/run-cross-platform-tests.cjs +63 -0
  494. package/scripts/run-tests.cjs +282 -0
  495. package/scripts/secret-scan-lint.sh +231 -0
  496. package/scripts/secret-scan.sh +358 -0
  497. package/scripts/setup-branch-protection.sh +236 -0
  498. package/scripts/shared-module-handsync-allowlist.json +183 -0
  499. package/scripts/strip-prose-atrefs.cjs +106 -0
  500. package/scripts/sync-rulesets.sh +34 -0
  501. package/scripts/sync-runtime-launcher.cjs +402 -0
  502. package/scripts/test-failure-reasons.cjs +34 -0
  503. package/scripts/workflow-policy.cjs +450 -0
@@ -0,0 +1,627 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * scripts/release-tarball-smoke.cjs
4
+ *
5
+ * Release tarball smoke test for issue #3686.
6
+ *
7
+ * Guards against the class of bugs that can't be caught by working-tree tests:
8
+ * - #3684: maskIfSecret import/export mismatch shipped in v1.42.3 (runtime
9
+ * crash on installed package, invisible to unit tests)
10
+ *
11
+ * Strategy: pack the working tree, install into a temp prefix, invoke the
12
+ * installed binary, assert the version matches package.json. Exercises the
13
+ * INSTALLED package, not the working tree.
14
+ *
15
+ * Exports:
16
+ * SMOKE — frozen enum of result codes
17
+ * runSmoke({ tarballPath, installPrefix, expectedVersion, fixtureDir,
18
+ * lifecycleCommands, dryRun })
19
+ * → { code: SMOKE.*, details: { version, tarball, ... } }
20
+ *
21
+ * CLI entry: node scripts/release-tarball-smoke.cjs --json
22
+ * Packs working tree, installs to a temp prefix, checks version.
23
+ * Exits 0 on SMOKE.OK, 1 otherwise.
24
+ * Always prints JSON to stdout when --json flag is present.
25
+ *
26
+ * Lifecycle command checks (Cycle 2):
27
+ * For each command name (other than 'init') in lifecycleCommands:
28
+ * - Assert commands/gsd/<cmd>.md exists in the installed package
29
+ * - Parse the .md for a workflow @-import or inline reference
30
+ * - Assert the referenced workflow .md exists in the installed package
31
+ * If 'init' is in lifecycleCommands, runs `gsd-core --local --claude`
32
+ * in fixtureDir to verify the installer is callable (INIT_FAILED on crash).
33
+ * Non-interactive: --local --claude flags skip all prompts.
34
+ *
35
+ * Workflow-body checks (Cycle 3 — informational):
36
+ * - Scans all installed get-shit-done/workflows/*.md for /gsd:<known-cmd>
37
+ * colon-namespace leaks (WORKFLOW_BODY_COLON_LEAK).
38
+ * This check populates result.details with counters but does NOT return a
39
+ * failure code by default; it is informational until enforcement is enabled.
40
+ */
41
+
42
+ 'use strict';
43
+
44
+ const { execFileSync, spawnSync } = require('child_process');
45
+ const fs = require('fs');
46
+ const os = require('os');
47
+ const path = require('path');
48
+ const { PACKAGE_NAME } = require('../get-shit-done/bin/lib/package-identity.cjs');
49
+ // 120 s proved too tight on Windows GitHub-hosted runners: cold-cache
50
+ // `npm install -g` with a 1499-file tarball took ~120 s exactly, causing
51
+ // spawnSync to fire SIGTERM and return { status: null, stdout: '', stderr: '' }
52
+ // (Node docs: status is null when subprocess terminated due to a signal).
53
+ // The INSTALL_FAILED branch checks `status !== 0`, which null satisfies, so the
54
+ // test saw empty stdout/stderr and a spurious INSTALL_FAILED. Windows runners
55
+ // are slower than Linux/macOS for filesystem-heavy operations (
56
+ // https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories
57
+ // ). Raise to 600 s (the same ceiling the before() helper uses for pack+install).
58
+ const CHILD_TIMEOUT_MS = process.platform === 'win32' ? 600_000 : 120_000;
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // Frozen result-code enum
62
+ // ---------------------------------------------------------------------------
63
+
64
+ const SMOKE = Object.freeze({
65
+ OK: 'ok',
66
+ VERSION_MISMATCH: 'version_mismatch',
67
+ PACK_FAILED: 'pack_failed',
68
+ INSTALL_FAILED: 'install_failed',
69
+ BIN_NOT_CALLABLE: 'bin_not_callable',
70
+ // Cycle 2 codes
71
+ COMMAND_FILE_MISSING: 'command_file_missing',
72
+ WORKFLOW_FILE_MISSING: 'workflow_file_missing',
73
+ INIT_FAILED: 'init_failed',
74
+ // Cycle 3 code
75
+ WORKFLOW_BODY_COLON_LEAK: 'workflow_body_colon_leak',
76
+ });
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // Exported helper: binInvocation
80
+ // ---------------------------------------------------------------------------
81
+
82
+ /**
83
+ * Build the { command, args, shell } descriptor needed to spawn an installed
84
+ * npm bin correctly on both Windows and POSIX.
85
+ *
86
+ * On Windows, npm installs a `.cmd` (or `.bat`) shim in .bin/. Node ≥18.20.2
87
+ * / ≥20.12.2 throws EINVAL when you try to spawnSync a .cmd/.bat without
88
+ * shell:true (CVE-2024-27980 mitigation). With shell:true, Node does NOT
89
+ * auto-quote argv, so a bin path that contains spaces must be wrapped in
90
+ * double-quotes to arrive at the shell as one token.
91
+ *
92
+ * On POSIX the bin is a regular shebang JS file; we invoke it directly via
93
+ * process.execPath (the same Node binary) without a shell.
94
+ *
95
+ * @param {string} binPath - Absolute path to the resolved bin file.
96
+ * @param {string[]} [args] - Additional arguments (e.g. ['--help']).
97
+ * @returns {{ command: string, args: string[], shell: boolean }}
98
+ */
99
+ function binInvocation(binPath, args = []) {
100
+ const lower = binPath.toLowerCase();
101
+ // Note: .ps1 shims are intentionally NOT handled here. The bin-resolution
102
+ // helpers (findGsdToolsBin / findInstallerBin) only ever surface a .cmd path
103
+ // on Windows — npm does not write .ps1 shims into .bin/ by default — so a
104
+ // .ps1 path never reaches this function in practice.
105
+ if (lower.endsWith('.cmd') || lower.endsWith('.bat')) {
106
+ // Quote the path if it contains a space so the Windows shell treats it as
107
+ // a single token. Simple double-quote wrap is sufficient because npm-
108
+ // generated shim paths don't contain embedded double-quotes.
109
+ const command = binPath.includes(' ') ? `"${binPath}"` : binPath;
110
+ return { command, args: [...args], shell: true };
111
+ }
112
+ // POSIX: invoke via node, no shell needed.
113
+ return { command: process.execPath, args: [binPath, ...args], shell: false };
114
+ }
115
+
116
+ // ---------------------------------------------------------------------------
117
+ // Internal helpers
118
+ // ---------------------------------------------------------------------------
119
+
120
+ /**
121
+ * Locate the lib/node_modules/@opengsd/gsd-core package root inside
122
+ * an npm --prefix install directory.
123
+ */
124
+ function pkgRoot(installPrefix) {
125
+ // POSIX: <prefix>/lib/node_modules/<scope>/<pkg>
126
+ // Windows: <prefix>/node_modules/<scope>/<pkg>
127
+ // PACKAGE_NAME is scoped (@scope/pkg), so split('/') yields the two path segments.
128
+ const pkgSegments = PACKAGE_NAME.split('/');
129
+ const posix = path.join(installPrefix, 'lib', 'node_modules', ...pkgSegments);
130
+ const win = path.join(installPrefix, 'node_modules', ...pkgSegments);
131
+ return fs.existsSync(posix) ? posix : win;
132
+ }
133
+
134
+ /**
135
+ * Return the ordered list of candidate paths to check when locating an npm
136
+ * global bin named `name` under `installPrefix`.
137
+ *
138
+ * On Windows, `npm install -g --prefix X` writes shims (*.cmd, *.ps1, bare)
139
+ * to the PREFIX ROOT (X\), NOT to X\node_modules\.bin\. We therefore probe
140
+ * the prefix root first, then fall back to node_modules\.bin in case a
141
+ * non-standard layout puts them there.
142
+ *
143
+ * On POSIX the shim lands in <prefix>/bin/ as a symlink; only one candidate.
144
+ */
145
+ function binCandidates(installPrefix, name) {
146
+ if (process.platform === 'win32') {
147
+ return [
148
+ // npm global --prefix on Windows writes shims to the prefix ROOT
149
+ path.join(installPrefix, `${name}.cmd`),
150
+ path.join(installPrefix, name),
151
+ // fallback: some layouts use node_modules/.bin
152
+ path.join(installPrefix, 'node_modules', '.bin', `${name}.cmd`),
153
+ path.join(installPrefix, 'node_modules', '.bin', name),
154
+ ];
155
+ }
156
+ return [path.join(installPrefix, 'bin', name)];
157
+ }
158
+
159
+ /**
160
+ * Locate the installed gsd-tools binary (symlink in <prefix>/bin/).
161
+ */
162
+ function findGsdToolsBin(installPrefix) {
163
+ for (const c of binCandidates(installPrefix, 'gsd-tools')) {
164
+ if (fs.existsSync(c)) return c;
165
+ }
166
+ return null;
167
+ }
168
+
169
+ /**
170
+ * Locate the gsd-core installer binary (the symlink in <prefix>/bin/).
171
+ */
172
+ function findInstallerBin(installPrefix) {
173
+ for (const c of binCandidates(installPrefix, 'gsd-core')) {
174
+ if (fs.existsSync(c)) return c;
175
+ }
176
+ return null;
177
+ }
178
+
179
+ /**
180
+ * Parse a command .md file and return the first workflow path it references.
181
+ *
182
+ * Structured parser — only inspects individual lines; never regexes on the
183
+ * whole-file string. Two recognised forms (in priority order):
184
+ *
185
+ * 1. @-import line: `@~/.claude/get-shit-done/workflows/<name>.md`
186
+ * 2. Inline mention: any line containing `~/.claude/get-shit-done/workflows/<name>.md`
187
+ * (takes the LAST occurrence so conditional-dispatch files resolve to the
188
+ * default / unconditional branch, e.g. discuss-phase.md)
189
+ *
190
+ * Returns the bare workflow filename (e.g. `"discuss-phase.md"`) or null.
191
+ */
192
+ function parseWorkflowRef(mdContent) {
193
+ const WORKFLOW_PREFIX = 'get-shit-done/workflows/';
194
+ let atImportResult = null;
195
+ let lastInlineResult = null;
196
+
197
+ const lines = mdContent.split(/\r?\n/);
198
+ for (const line of lines) {
199
+ const trimmed = line.trim();
200
+
201
+ // Form 1: @-import
202
+ if (trimmed.startsWith('@') && trimmed.includes(WORKFLOW_PREFIX)) {
203
+ const idx = trimmed.indexOf(WORKFLOW_PREFIX);
204
+ const rest = trimmed.slice(idx + WORKFLOW_PREFIX.length);
205
+ // rest is like "discuss-phase.md" or "discuss-phase.md end-to-end."
206
+ const name = rest.split(/[\s`"]/)[0];
207
+ if (name.endsWith('.md')) {
208
+ atImportResult = name;
209
+ break; // @-imports are authoritative; stop on first
210
+ }
211
+ }
212
+
213
+ // Form 2: inline mention (collect last)
214
+ if (trimmed.includes(WORKFLOW_PREFIX)) {
215
+ const idx = trimmed.indexOf(WORKFLOW_PREFIX);
216
+ const rest = trimmed.slice(idx + WORKFLOW_PREFIX.length);
217
+ const name = rest.split(/[\s`"]/)[0];
218
+ if (name.endsWith('.md')) {
219
+ lastInlineResult = name;
220
+ }
221
+ }
222
+ }
223
+
224
+ return atImportResult !== null ? atImportResult : lastInlineResult;
225
+ }
226
+
227
+ /**
228
+ * Read the list of known GSD command names from the installed package.
229
+ * Returns an array of strings like `['init', 'discuss-phase', ...]`.
230
+ */
231
+ function readInstalledCmdNames(pkg) {
232
+ const commandsDir = path.join(pkg, 'commands', 'gsd');
233
+ if (!fs.existsSync(commandsDir)) return [];
234
+ return fs.readdirSync(commandsDir)
235
+ .filter((f) => f.endsWith('.md'))
236
+ .map((f) => f.slice(0, -3)); // strip .md
237
+ }
238
+
239
+ /**
240
+ * Scan a single workflow .md file for /gsd:<cmd> colon-namespace leaks.
241
+ *
242
+ * Uses the word-boundary-safe regex shape from scripts/fix-slash-commands.cjs:
243
+ * /gsd-(<cmd1>|<cmd2>|...)(?=[^a-zA-Z0-9_-]|$)/g — forward
244
+ * We check the colon form: /gsd:<cmd> leaking in installed workflow bodies.
245
+ *
246
+ * Returns the first leaking { line, lineNumber } or null.
247
+ */
248
+ function scanWorkflowColonLeak(filePath, cmdNames) {
249
+ if (!cmdNames || cmdNames.length === 0) return null;
250
+ const sorted = [...cmdNames].sort((a, b) => b.length - a.length);
251
+ const pattern = new RegExp(`/gsd:(${sorted.join('|')})(?=[^a-zA-Z0-9_-]|$)`, 'g');
252
+
253
+ const content = fs.readFileSync(filePath, 'utf-8');
254
+ const lines = content.split(/\r?\n/);
255
+ for (let i = 0; i < lines.length; i++) {
256
+ pattern.lastIndex = 0;
257
+ if (pattern.test(lines[i])) {
258
+ return { line: i + 1, content: lines[i].trim() };
259
+ }
260
+ }
261
+ return null;
262
+ }
263
+
264
+ // ---------------------------------------------------------------------------
265
+ // Pure function: runSmoke
266
+ // ---------------------------------------------------------------------------
267
+
268
+ /**
269
+ * @param {object} opts
270
+ * @param {string} opts.tarballPath - Absolute path to a pre-packed .tgz
271
+ * @param {string} opts.installPrefix - Temp directory to use as npm --prefix
272
+ * @param {string} opts.expectedVersion - semver string to assert (e.g. "1.50.0")
273
+ * @param {string} [opts.fixtureDir] - Temp dir to run `init` into (must NOT be HOME)
274
+ * @param {string[]} [opts.lifecycleCommands] - Commands to file-check (default: see below)
275
+ * @param {boolean} [opts.dryRun=false] - If true, skip actual npm install; validate input only
276
+ * @param {object} [opts.npmEnv] - Optional env dict for the internal npm install
277
+ * spawnSync call. Pass an isolated HOME env (e.g. from isolatedNpmEnv() in tests/helpers.cjs)
278
+ * to prevent npm from reading/writing the caller's $HOME — required on Docker hosts where HOME
279
+ * may be unwritable. Defaults to process.env. (#131)
280
+ * @returns {{ code: string, details: object }}
281
+ */
282
+ function runSmoke({
283
+ tarballPath,
284
+ installPrefix,
285
+ expectedVersion,
286
+ fixtureDir,
287
+ lifecycleCommands = ['init', 'discuss-phase', 'plan-phase', 'execute-phase'],
288
+ dryRun = false,
289
+ npmEnv = undefined,
290
+ }) {
291
+ const details = {
292
+ tarball: tarballPath,
293
+ prefix: installPrefix,
294
+ expectedVersion,
295
+ };
296
+
297
+ if (dryRun) {
298
+ return { code: SMOKE.OK, details: { ...details, version: expectedVersion, dryRun: true } };
299
+ }
300
+
301
+ // --- Install the tarball into the temp prefix ----------------------------
302
+ const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
303
+ // Use the caller-supplied npmEnv if provided (allows HOME isolation on Docker
304
+ // hosts where HOME may be unwritable — same pattern as runNpm() in helpers.cjs).
305
+ // Falls back to process.env to preserve existing CLI / programmatic behaviour. (#131)
306
+ const effectiveNpmEnv = npmEnv !== undefined ? npmEnv : process.env;
307
+ const installResult = spawnSync(
308
+ npmCmd,
309
+ ['install', '-g', '--prefix', installPrefix, tarballPath],
310
+ { encoding: 'utf-8', shell: process.platform === 'win32', timeout: CHILD_TIMEOUT_MS, env: effectiveNpmEnv },
311
+ );
312
+
313
+ if (installResult.status !== 0) {
314
+ return {
315
+ code: SMOKE.INSTALL_FAILED,
316
+ details: {
317
+ ...details,
318
+ stderr: installResult.stderr,
319
+ stdout: installResult.stdout,
320
+ // Expose signal + error so a timeout (status=null, signal='SIGTERM',
321
+ // stdout='', stderr='') is immediately diagnosable in CI logs.
322
+ signal: installResult.signal ?? null,
323
+ installError: installResult.error ? String(installResult.error) : null,
324
+ },
325
+ };
326
+ }
327
+
328
+ // --- Locate the installed gsd-tools binary --------------------------------
329
+ const actualBin = findGsdToolsBin(installPrefix);
330
+
331
+ if (!actualBin) {
332
+ const searched = binCandidates(installPrefix, 'gsd-tools');
333
+ return {
334
+ code: SMOKE.BIN_NOT_CALLABLE,
335
+ details: { ...details, searched },
336
+ };
337
+ }
338
+
339
+ // --- Invoke `gsd-tools --help` to assert the shipped binary is callable ---
340
+ // Use effectiveNpmEnv so the installed binary sees an isolated HOME on Docker
341
+ // hosts where HOME may be unwritable (same isolation as the npm install). (#131)
342
+ const versionInvocation = binInvocation(actualBin, ['--help']);
343
+ const versionResult = spawnSync(
344
+ versionInvocation.command,
345
+ versionInvocation.args,
346
+ { encoding: 'utf-8', timeout: CHILD_TIMEOUT_MS, env: effectiveNpmEnv, shell: versionInvocation.shell },
347
+ );
348
+
349
+ if (versionResult.status !== 0) {
350
+ return {
351
+ code: SMOKE.BIN_NOT_CALLABLE,
352
+ details: {
353
+ ...details,
354
+ bin: actualBin,
355
+ stderr: versionResult.stderr,
356
+ stdout: versionResult.stdout,
357
+ },
358
+ };
359
+ }
360
+
361
+ // Source of truth for shipped version is the installed package.json.
362
+ const installedPkgPath = path.join(pkgRoot(installPrefix), 'package.json');
363
+ const installedPkg = JSON.parse(fs.readFileSync(installedPkgPath, 'utf-8'));
364
+ const installedVersion = String(installedPkg.version || '').trim();
365
+
366
+ details.version = installedVersion;
367
+ details.bin = actualBin;
368
+ details.installedPackageJson = installedPkgPath;
369
+
370
+ if (installedVersion !== expectedVersion) {
371
+ return {
372
+ code: SMOKE.VERSION_MISMATCH,
373
+ details: { ...details, installedVersion, expectedVersion },
374
+ };
375
+ }
376
+
377
+ // ─────────────────────────────────────────────────────────────────────────
378
+ // Cycle 2: lifecycle command file-resolution checks
379
+ // ─────────────────────────────────────────────────────────────────────────
380
+
381
+ const pkg = pkgRoot(installPrefix);
382
+ const shouldRunInit = lifecycleCommands.includes('init');
383
+ const commandsToCheck = lifecycleCommands.filter((c) => c !== 'init');
384
+
385
+ // --- Run init if requested -----------------------------------------------
386
+ if (shouldRunInit && fixtureDir) {
387
+ const installerBin = findInstallerBin(installPrefix);
388
+ if (!installerBin) {
389
+ return {
390
+ code: SMOKE.INIT_FAILED,
391
+ details: {
392
+ ...details,
393
+ reason: 'gsd-core binary not found in installPrefix',
394
+ installPrefix,
395
+ },
396
+ };
397
+ }
398
+
399
+ // Non-interactive: --local --claude installs to .claude/ in cwd (fixtureDir).
400
+ // GSD_TEST_MODE must be cleared — install.js skips its main() block when
401
+ // GSD_TEST_MODE is set, which would cause the installer to exit 0 silently
402
+ // without actually creating any files.
403
+ const initEnv = { ...process.env };
404
+ delete initEnv.GSD_TEST_MODE;
405
+
406
+ const initInvocation = binInvocation(installerBin, ['--local', '--claude']);
407
+ const initResult = spawnSync(
408
+ initInvocation.command,
409
+ initInvocation.args,
410
+ {
411
+ encoding: 'utf-8',
412
+ cwd: fixtureDir,
413
+ // Ensure no TTY so the installer's non-interactive fallback fires
414
+ stdio: ['pipe', 'pipe', 'pipe'],
415
+ env: initEnv,
416
+ timeout: CHILD_TIMEOUT_MS,
417
+ shell: initInvocation.shell,
418
+ },
419
+ );
420
+
421
+ if (initResult.status !== 0) {
422
+ return {
423
+ code: SMOKE.INIT_FAILED,
424
+ details: {
425
+ ...details,
426
+ fixtureDir,
427
+ stderr: initResult.stderr,
428
+ stdout: initResult.stdout,
429
+ },
430
+ };
431
+ }
432
+
433
+ // Verify expected dirs were created
434
+ const expectedDirs = [
435
+ path.join(fixtureDir, '.claude', 'commands'),
436
+ path.join(fixtureDir, '.claude', 'get-shit-done'),
437
+ ];
438
+ for (const dir of expectedDirs) {
439
+ if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
440
+ return {
441
+ code: SMOKE.INIT_FAILED,
442
+ details: {
443
+ ...details,
444
+ fixtureDir,
445
+ reason: `expected dir not created: ${dir}`,
446
+ },
447
+ };
448
+ }
449
+ }
450
+ }
451
+
452
+ // --- Check command files and workflow references -------------------------
453
+ const lifecycleResolved = [];
454
+
455
+ for (const cmd of commandsToCheck) {
456
+ const cmdFilePath = path.join(pkg, 'commands', 'gsd', `${cmd}.md`);
457
+
458
+ if (!fs.existsSync(cmdFilePath) || !fs.statSync(cmdFilePath).isFile()) {
459
+ return {
460
+ code: SMOKE.COMMAND_FILE_MISSING,
461
+ details: {
462
+ ...details,
463
+ command: cmd,
464
+ path: cmdFilePath,
465
+ },
466
+ };
467
+ }
468
+
469
+ // Parse workflow reference
470
+ const mdContent = fs.readFileSync(cmdFilePath, 'utf-8');
471
+ const workflowName = parseWorkflowRef(mdContent);
472
+
473
+ let workflowPath = null;
474
+ if (workflowName) {
475
+ // Workflow files live at get-shit-done/workflows/<name> in the package.
476
+ // Some live in subdirectories; try flat first then scan once.
477
+ const flat = path.join(pkg, 'get-shit-done', 'workflows', workflowName);
478
+ workflowPath = fs.existsSync(flat) ? flat : null;
479
+
480
+ if (!workflowPath) {
481
+ return {
482
+ code: SMOKE.WORKFLOW_FILE_MISSING,
483
+ details: {
484
+ ...details,
485
+ command: cmd,
486
+ path: flat,
487
+ },
488
+ };
489
+ }
490
+ }
491
+
492
+ lifecycleResolved.push({
493
+ command: cmd,
494
+ commandPath: cmdFilePath,
495
+ workflowPath,
496
+ });
497
+ }
498
+
499
+ details.lifecycleResolved = lifecycleResolved;
500
+
501
+ // ─────────────────────────────────────────────────────────────────────────
502
+ // Cycle 3: workflow-body validation (informational)
503
+ // ─────────────────────────────────────────────────────────────────────────
504
+
505
+ // --- Workflow-body checks (informational — #3668 not yet fixed) ----------
506
+ const workflowsDir = path.join(pkg, 'get-shit-done', 'workflows');
507
+ const installedCmdNames = readInstalledCmdNames(pkg);
508
+
509
+ let workflowsScanned = 0;
510
+ let colonLeakCount = 0;
511
+ // Store first finding for potential future enforcement mode.
512
+ let firstColonLeak = null;
513
+
514
+ if (fs.existsSync(workflowsDir)) {
515
+ // Collect all .md files (flat only — subdirs contain sub-workflows that
516
+ // follow the same contract, but the top-level .md files are the primary surface)
517
+ const entries = fs.readdirSync(workflowsDir, { withFileTypes: true });
518
+ for (const entry of entries) {
519
+ if (!entry.isFile() || !entry.name.endsWith('.md')) continue;
520
+ const filePath = path.join(workflowsDir, entry.name);
521
+ workflowsScanned++;
522
+
523
+ const leak = scanWorkflowColonLeak(filePath, installedCmdNames);
524
+ if (leak) {
525
+ colonLeakCount++;
526
+ if (!firstColonLeak) {
527
+ firstColonLeak = { file: filePath, line: leak.line };
528
+ }
529
+ }
530
+
531
+ }
532
+ }
533
+
534
+ details.workflowsScanned = workflowsScanned;
535
+ details.colonLeakCount = colonLeakCount;
536
+ if (firstColonLeak) details.firstColonLeak = firstColonLeak;
537
+
538
+ // NOTE: colonLeakCount is informational here. Once the backlog is fixed,
539
+ // a future enforcement mode can fail on non-zero counts.
540
+
541
+ return { code: SMOKE.OK, details };
542
+ }
543
+
544
+ // ---------------------------------------------------------------------------
545
+ // CLI entry
546
+ // ---------------------------------------------------------------------------
547
+
548
+ function cliMain() {
549
+ const args = process.argv.slice(2);
550
+ const isJson = args.includes('--json');
551
+
552
+ const pkgPath = path.join(__dirname, '..', 'package.json');
553
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
554
+ const expectedVersion = process.env.SMOKE_FORCE_EXPECTED_VERSION || pkg.version;
555
+
556
+ // Pack the working tree into a temp directory
557
+ const packDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-smoke-pack-'));
558
+ const installPrefix = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-smoke-prefix-'));
559
+ const fixtureDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-smoke-fixture-'));
560
+
561
+ let tarballPath;
562
+ try {
563
+ const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
564
+ const packOutput = execFileSync(
565
+ npmCmd,
566
+ ['pack', '--pack-destination', packDir],
567
+ {
568
+ cwd: path.join(__dirname, '..'),
569
+ encoding: 'utf-8',
570
+ shell: process.platform === 'win32',
571
+ timeout: CHILD_TIMEOUT_MS,
572
+ },
573
+ ).trim();
574
+ // npm pack outputs the filename on stdout (last line when verbose)
575
+ const lines = packOutput.split(/\r?\n/).filter(Boolean);
576
+ const tgzName = lines[lines.length - 1];
577
+ tarballPath = path.join(packDir, tgzName);
578
+ if (!fs.existsSync(tarballPath)) {
579
+ // npm 7+ may print just the filename without .tgz extension on some platforms
580
+ const found = fs.readdirSync(packDir).find((f) => f.endsWith('.tgz'));
581
+ if (found) {
582
+ tarballPath = path.join(packDir, found);
583
+ } else {
584
+ const result = {
585
+ code: SMOKE.PACK_FAILED,
586
+ details: { packDir, packOutput, reason: 'no .tgz in pack destination' },
587
+ };
588
+ if (isJson) process.stdout.write(JSON.stringify(result) + '\n');
589
+ cleanup(packDir, installPrefix, fixtureDir);
590
+ process.exit(1);
591
+ }
592
+ }
593
+ } catch (err) {
594
+ const result = {
595
+ code: SMOKE.PACK_FAILED,
596
+ details: { error: err.message, stderr: err.stderr },
597
+ };
598
+ if (isJson) process.stdout.write(JSON.stringify(result) + '\n');
599
+ cleanup(packDir, installPrefix, fixtureDir);
600
+ process.exit(1);
601
+ }
602
+
603
+ const result = runSmoke({ tarballPath, installPrefix, expectedVersion, fixtureDir });
604
+ if (isJson) process.stdout.write(JSON.stringify(result) + '\n');
605
+ cleanup(packDir, installPrefix, fixtureDir);
606
+ process.exit(result.code === SMOKE.OK ? 0 : 1);
607
+ }
608
+
609
+ function cleanup(...dirs) {
610
+ for (const dir of dirs) {
611
+ try {
612
+ fs.rmSync(dir, { recursive: true, force: true });
613
+ } catch {
614
+ // best-effort
615
+ }
616
+ }
617
+ }
618
+
619
+ // ---------------------------------------------------------------------------
620
+ // Exports
621
+ // ---------------------------------------------------------------------------
622
+
623
+ module.exports = { SMOKE, runSmoke, binInvocation };
624
+
625
+ if (require.main === module) {
626
+ cliMain();
627
+ }
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { runAffectedTests } = require('./affected-tests-lib.cjs');
5
+
6
+ runAffectedTests();
@@ -0,0 +1,63 @@
1
+ 'use strict';
2
+
3
+ const { spawnSync } = require('child_process');
4
+
5
+ const CROSS_PLATFORM_TEST_REASON = Object.freeze({
6
+ PASS: 'pass',
7
+ TEST_FAILURE: 'test_failure',
8
+ INFRA_FAILURE: 'infra_failure',
9
+ UNKNOWN_FAILURE: 'unknown_failure',
10
+ });
11
+
12
+ function classify(exitCode, output) {
13
+ if (exitCode === 0) return CROSS_PLATFORM_TEST_REASON.PASS;
14
+ if (exitCode === 2 || /infrastructure failure|worktree\.Construct/i.test(output)) {
15
+ return CROSS_PLATFORM_TEST_REASON.INFRA_FAILURE;
16
+ }
17
+ if (/\bFAIL\b|\d+\s+failures?\)/i.test(output)) {
18
+ return CROSS_PLATFORM_TEST_REASON.TEST_FAILURE;
19
+ }
20
+ return CROSS_PLATFORM_TEST_REASON.UNKNOWN_FAILURE;
21
+ }
22
+
23
+ function runCrossPlatformTests(options = {}, deps = {}) {
24
+ const {
25
+ base = 'next',
26
+ head = 'HEAD',
27
+ source = '.',
28
+ targets = 'linux,macos',
29
+ cwd = process.cwd(),
30
+ } = options;
31
+ const runner = deps.spawnSync || spawnSync;
32
+
33
+ const args = ['--targets', targets, '--base', base, '--head', head, '--source', source];
34
+ const result = runner('gsd-test', args, { cwd, encoding: 'utf8' });
35
+
36
+ const stdout = result.stdout || '';
37
+ const stderr = result.stderr || '';
38
+ const output = `${stdout}\n${stderr}`;
39
+ const exitCode = Number(result.status ?? 1);
40
+ const reason = classify(exitCode, output);
41
+
42
+ return {
43
+ ok: exitCode === 0,
44
+ reason,
45
+ exitCode,
46
+ command: ['gsd-test', ...args].join(' '),
47
+ stdout,
48
+ stderr,
49
+ };
50
+ }
51
+
52
+ if (require.main === module) {
53
+ const result = runCrossPlatformTests();
54
+ const line = `[cross-platform-tests] reason=${result.reason} exit=${result.exitCode}`;
55
+ if (result.ok) {
56
+ process.stdout.write(`${line}\n`);
57
+ process.exit(0);
58
+ }
59
+ process.stderr.write(`${line}\n`);
60
+ process.exit(result.exitCode);
61
+ }
62
+
63
+ module.exports = { CROSS_PLATFORM_TEST_REASON, runCrossPlatformTests };