@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,339 @@
1
+ #!/usr/bin/env bash
2
+ # base64-scan.sh — Detect base64-obfuscated prompt injection in source files
3
+ #
4
+ # Extracts base64 blobs >= 40 chars, decodes them, and checks decoded content
5
+ # against the same injection patterns used by prompt-injection-scan.sh.
6
+ #
7
+ # Usage:
8
+ # scripts/base64-scan.sh --diff origin/main # CI mode: scan changed files
9
+ # scripts/base64-scan.sh --file path/to/file # Scan a single file
10
+ # scripts/base64-scan.sh --dir agents/ # Scan all files in a directory
11
+ #
12
+ # Exit codes:
13
+ # 0 = clean
14
+ # 1 = findings detected
15
+ # 2 = usage error
16
+ set -euo pipefail
17
+
18
+ # ── Locale hardening (#116) ───────────────────────────────────────────────────
19
+ # BSD tr (macOS) treats input bytes as multi-byte characters under any UTF-8
20
+ # locale. When the input to `tr -cd '[:print:]'` contains bytes that are not
21
+ # valid UTF-8 start sequences (e.g. lone continuation bytes 0x80–0x9F), BSD tr
22
+ # emits "Illegal byte sequence" to stderr and exits non-zero. Setting LC_ALL=C
23
+ # forces the C locale throughout the script so every byte 0x00–0xFF is a valid
24
+ # character — no multi-byte interpretation, no illegal-byte errors.
25
+ #
26
+ # This is safe for our use-case: all injection patterns are ASCII; base64 -d
27
+ # and grep -E POSIX classes ([:space:], [:print:]) behave correctly in C locale.
28
+ #
29
+ # Source: `man tr` on macOS 26.5 — ENVIRONMENT section states LC_ALL / LC_CTYPE
30
+ # control character interpretation; BSD tr rejects invalid multi-byte sequences.
31
+ # Empirically verified: `printf '\x80\x81hello' | LC_ALL=C tr -cd '[:print:]'`
32
+ # exits 0 and strips the high bytes cleanly.
33
+ export LC_ALL=C
34
+
35
+ MIN_BLOB_LENGTH=40
36
+ # Lines longer than this byte count are skipped with a partial-scan warning.
37
+ # This prevents `grep -oE` from spending unbounded time on e.g. minified JS or
38
+ # single-line binary blobs. 1 MiB is large enough for any realistic text file
39
+ # line but small enough to bound blob-extraction cost.
40
+ MAX_LINE_BYTES=1048576
41
+
42
+ # -- Portable timeout wrapper (#116) ------------------------------------------
43
+ # macOS does not ship GNU coreutils timeout. We probe for it (or gtimeout
44
+ # from homebrew coreutils), falling back to perl alarm(N)+exec.
45
+ # Exit codes: GNU timeout uses 124 on timeout; perl SIGALRM produces 142.
46
+ # Both are treated as timeout exits by is_timeout_exit() below.
47
+ # Usage: run_with_timeout <seconds> <command> [args...]
48
+ _TIMEOUT_CMD=""
49
+ # shellcheck disable=SC2329 # intentionally defined for use by callers; not called in main loop
50
+ _init_timeout_cmd() {
51
+ if [[ -n "$_TIMEOUT_CMD" ]]; then return; fi
52
+ if command -v timeout >/dev/null 2>&1; then
53
+ _TIMEOUT_CMD="timeout"
54
+ elif command -v gtimeout >/dev/null 2>&1; then
55
+ _TIMEOUT_CMD="gtimeout"
56
+ else
57
+ _TIMEOUT_CMD="perl_alarm"
58
+ fi
59
+ }
60
+
61
+ # shellcheck disable=SC2329 # intentionally defined for use by callers; not called in main loop
62
+ run_with_timeout() {
63
+ local secs="$1"; shift
64
+ _init_timeout_cmd
65
+ case "$_TIMEOUT_CMD" in
66
+ timeout|gtimeout)
67
+ "$_TIMEOUT_CMD" "$secs" "$@"
68
+ ;;
69
+ perl_alarm)
70
+ # perl sets SIGALRM after N seconds, then exec()s the command.
71
+ # Exit 142 (SIGALRM) when timed out.
72
+ perl -e '
73
+ my $secs = shift @ARGV;
74
+ alarm($secs);
75
+ exec(@ARGV) or die "exec: $!\n";
76
+ ' -- "$secs" "$@"
77
+ ;;
78
+ esac
79
+ }
80
+
81
+ # is_timeout_exit: returns 0 (true) if rc indicates a timeout kill.
82
+ # shellcheck disable=SC2329 # intentionally defined for use by callers; not called in main loop
83
+ is_timeout_exit() { [[ "$1" -eq 124 || "$1" -eq 142 ]]; }
84
+
85
+
86
+ # ─── Injection Patterns (decoded content) ────────────────────────────────────
87
+ # Subset of patterns — if someone base64-encoded something, check for the
88
+ # most common injection indicators.
89
+ DECODED_PATTERNS=(
90
+ 'ignore[[:space:]]+(all[[:space:]]+)?previous[[:space:]]+instructions'
91
+ 'you[[:space:]]+are[[:space:]]+now[[:space:]]+'
92
+ 'system[[:space:]]+prompt'
93
+ '</?system>'
94
+ '</?assistant>'
95
+ '\[SYSTEM\]'
96
+ '\[INST\]'
97
+ '<<SYS>>'
98
+ 'override[[:space:]]+(system|safety|security)'
99
+ 'pretend[[:space:]]+(you|to)[[:space:]]'
100
+ 'act[[:space:]]+as[[:space:]]+(a|an|if)'
101
+ 'jailbreak'
102
+ 'bypass[[:space:]]+(safety|content|security)'
103
+ 'eval[[:space:]]*\('
104
+ 'exec[[:space:]]*\('
105
+ 'rm[[:space:]]+-rf'
106
+ 'curl[[:space:]].*\|[[:space:]]*sh'
107
+ 'wget[[:space:]].*\|[[:space:]]*sh'
108
+ )
109
+
110
+ # ─── Ignorelist ──────────────────────────────────────────────────────────────
111
+
112
+ IGNOREFILE=".base64scanignore"
113
+ IGNORED_PATTERNS=()
114
+
115
+ load_ignorelist() {
116
+ if [[ -f "$IGNOREFILE" ]]; then
117
+ while IFS= read -r line; do
118
+ # Skip comments and empty lines
119
+ [[ "$line" =~ ^[[:space:]]*# ]] && continue
120
+ [[ -z "${line// }" ]] && continue
121
+ IGNORED_PATTERNS+=("$line")
122
+ done < "$IGNOREFILE"
123
+ fi
124
+ }
125
+
126
+ is_ignored() {
127
+ local blob="$1"
128
+ if [[ ${#IGNORED_PATTERNS[@]} -eq 0 ]]; then
129
+ return 1
130
+ fi
131
+ for pattern in "${IGNORED_PATTERNS[@]}"; do
132
+ if [[ "$blob" == "$pattern" ]]; then
133
+ return 0
134
+ fi
135
+ done
136
+ return 1
137
+ }
138
+
139
+ # ─── Skip Rules ──────────────────────────────────────────────────────────────
140
+
141
+ should_skip_file() {
142
+ local file="$1"
143
+ # Skip binary files
144
+ case "$file" in
145
+ *.png|*.jpg|*.jpeg|*.gif|*.ico|*.woff|*.woff2|*.ttf|*.eot|*.otf) return 0 ;;
146
+ *.zip|*.tar|*.gz|*.bz2|*.xz|*.7z) return 0 ;;
147
+ *.pdf|*.doc|*.docx|*.xls|*.xlsx) return 0 ;;
148
+ esac
149
+ # Skip lockfiles and node_modules
150
+ case "$file" in
151
+ */node_modules/*) return 0 ;;
152
+ */package-lock.json) return 0 ;;
153
+ */yarn.lock) return 0 ;;
154
+ */pnpm-lock.yaml) return 0 ;;
155
+ esac
156
+ # Skip the scan scripts themselves and test files
157
+ case "$file" in
158
+ */base64-scan.sh) return 0 ;;
159
+ */security-scan.test.cjs) return 0 ;;
160
+ esac
161
+ # Skip scanner fixture directories — they contain deliberate injection samples
162
+ case "$file" in
163
+ tests/fixtures/*) return 0 ;;
164
+ esac
165
+ return 1
166
+ }
167
+
168
+ is_data_uri() {
169
+ local context="$1"
170
+ # data:image/png;base64,... or data:application/font-woff;base64,...
171
+ echo "$context" | grep -qE 'data:[a-zA-Z]+/[a-zA-Z0-9.+-]+;base64,' 2>/dev/null
172
+ }
173
+
174
+ # ─── File Collection ─────────────────────────────────────────────────────────
175
+
176
+ collect_files() {
177
+ local mode="$1"
178
+ shift
179
+
180
+ case "$mode" in
181
+ --diff)
182
+ local base="${1:-origin/main}"
183
+ git diff --name-only --diff-filter=ACMR "$base"...HEAD 2>/dev/null \
184
+ | grep -vE '\.(png|jpg|jpeg|gif|ico|woff|woff2|ttf|eot|otf|zip|tar|gz|pdf)$' || true
185
+ ;;
186
+ --file)
187
+ if [[ -f "$1" ]]; then
188
+ echo "$1"
189
+ else
190
+ echo "Error: file not found: $1" >&2
191
+ exit 2
192
+ fi
193
+ ;;
194
+ --dir)
195
+ local dir="$1"
196
+ if [[ ! -d "$dir" ]]; then
197
+ echo "Error: directory not found: $dir" >&2
198
+ exit 2
199
+ fi
200
+ find "$dir" -type f ! -path '*/node_modules/*' ! -path '*/.git/*' ! -path '*/dist/*' \
201
+ ! -name '*.png' ! -name '*.jpg' ! -name '*.gif' ! -name '*.woff*' 2>/dev/null || true
202
+ ;;
203
+ --stdin)
204
+ cat
205
+ ;;
206
+ *)
207
+ echo "Usage: $0 --diff [base] | --file <path> | --dir <path> | --stdin" >&2
208
+ exit 2
209
+ ;;
210
+ esac
211
+ }
212
+
213
+ # ─── Scanner ─────────────────────────────────────────────────────────────────
214
+
215
+ extract_and_check_blobs() {
216
+ local file="$1"
217
+ local found=0
218
+ local line_num=0
219
+
220
+ while IFS= read -r line; do
221
+ line_num=$((line_num + 1))
222
+
223
+ # Guard: skip lines that exceed MAX_LINE_BYTES. Very long lines (e.g. a
224
+ # minified JS bundle stored as one line, or a binary file with no newlines)
225
+ # would cause `grep -oE` to spend unbounded time. We emit a partial-scan
226
+ # warning to stderr so the caller can see coverage was reduced.
227
+ if [[ ${#line} -gt $MAX_LINE_BYTES ]]; then
228
+ echo "SKIP: $file line $line_num (${#line} bytes > ${MAX_LINE_BYTES} limit — partial scan)" >&2
229
+ continue
230
+ fi
231
+
232
+ # Skip data URIs — legitimate base64 usage
233
+ if is_data_uri "$line"; then
234
+ continue
235
+ fi
236
+
237
+ # Extract base64-like blobs (alphanumeric + / + = padding, >= MIN_BLOB_LENGTH)
238
+ local blobs
239
+ blobs=$(echo "$line" | grep -oE '[A-Za-z0-9+/]{'"$MIN_BLOB_LENGTH"',}={0,3}' 2>/dev/null || true)
240
+
241
+ if [[ -z "$blobs" ]]; then
242
+ continue
243
+ fi
244
+
245
+ while IFS= read -r blob; do
246
+ [[ -z "$blob" ]] && continue
247
+
248
+ # Check ignorelist
249
+ if [[ ${#IGNORED_PATTERNS[@]} -gt 0 ]] && is_ignored "$blob"; then
250
+ continue
251
+ fi
252
+
253
+ # Try to decode — if it fails, not valid base64
254
+ local decoded
255
+ decoded=$(echo "$blob" | base64 -d 2>/dev/null || echo "")
256
+
257
+ if [[ -z "$decoded" ]]; then
258
+ continue
259
+ fi
260
+
261
+ # Check if decoded content is mostly printable text (not random binary)
262
+ local total_chars=${#decoded}
263
+ if [[ $total_chars -eq 0 ]]; then
264
+ continue
265
+ fi
266
+
267
+ # Count printable ASCII characters
268
+ local printable_count
269
+ printable_count=$(echo -n "$decoded" | tr -cd '[:print:]' | wc -c | tr -d ' ')
270
+ # Skip if less than 70% printable (likely binary data, not obfuscated text)
271
+ if [[ $((printable_count * 100 / total_chars)) -lt 70 ]]; then
272
+ continue
273
+ fi
274
+
275
+ # Scan decoded content against injection patterns
276
+ for pattern in "${DECODED_PATTERNS[@]}"; do
277
+ if echo "$decoded" | grep -iqE "$pattern" 2>/dev/null; then
278
+ if [[ $found -eq 0 ]]; then
279
+ echo "FAIL: $file"
280
+ found=1
281
+ fi
282
+ echo " line $line_num: base64 blob decodes to suspicious content"
283
+ echo " blob: ${blob:0:60}..."
284
+ echo " decoded: ${decoded:0:120}"
285
+ echo " matched: $pattern"
286
+ break
287
+ fi
288
+ done
289
+ done <<< "$blobs"
290
+ done < "$file"
291
+
292
+ return $found
293
+ }
294
+
295
+ # ─── Main ────────────────────────────────────────────────────────────────────
296
+
297
+ main() {
298
+ if [[ $# -eq 0 ]]; then
299
+ echo "Usage: $0 --diff [base] | --file <path> | --dir <path>" >&2
300
+ exit 2
301
+ fi
302
+
303
+ load_ignorelist
304
+
305
+ local mode="$1"
306
+ shift
307
+
308
+ local files
309
+ files=$(collect_files "$mode" "$@")
310
+
311
+ if [[ -z "$files" ]]; then
312
+ echo "base64-scan: no files to scan"
313
+ exit 0
314
+ fi
315
+
316
+ local total=0
317
+ local failed=0
318
+
319
+ while IFS= read -r file; do
320
+ [[ -z "$file" ]] && continue
321
+ if should_skip_file "$file"; then
322
+ continue
323
+ fi
324
+ total=$((total + 1))
325
+ if ! extract_and_check_blobs "$file"; then
326
+ failed=$((failed + 1))
327
+ fi
328
+ done <<< "$files"
329
+
330
+ echo ""
331
+ echo "base64-scan: scanned $total files, $failed with findings"
332
+
333
+ if [[ $failed -gt 0 ]]; then
334
+ exit 1
335
+ fi
336
+ exit 0
337
+ }
338
+
339
+ main "$@"
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Copy GSD hooks to dist for installation.
4
+ * Validates JavaScript syntax before copying to prevent shipping broken hooks.
5
+ * See #1107, #1109, #1125, #1161 — a duplicate const declaration shipped
6
+ * in dist and caused PostToolUse hook errors for all users.
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const vm = require('vm');
12
+
13
+ const HOOKS_DIR = path.join(__dirname, '..', 'hooks');
14
+ const DIST_DIR = path.join(HOOKS_DIR, 'dist');
15
+ // Per-process staging directory for atomic writes. Using process.pid in the
16
+ // name eliminates all contention between concurrent builders: each process
17
+ // owns its own staging dir and never races with another builder's cleanup.
18
+ // Lives under hooks/ so it shares a filesystem with DIST_DIR (POSIX
19
+ // rename(2) is only atomic within the same filesystem) but is NOT inside
20
+ // DIST_DIR — so readers that readdirSync(DIST_DIR) (e.g. bin/install.js,
21
+ // install-hooks-copy tests) never observe a transient ".tmp" sibling.
22
+ // The parent pattern hooks/.dist-staging-*/ is gitignored.
23
+ const STAGE_DIR = path.join(HOOKS_DIR, `.dist-staging-${process.pid}`);
24
+
25
+ // Hooks to copy (pure Node.js, no bundling needed)
26
+ const HOOKS_TO_COPY = [
27
+ 'gsd-check-update-worker.js',
28
+ 'gsd-check-update.js',
29
+ 'gsd-context-monitor.js',
30
+ 'gsd-prompt-guard.js',
31
+ 'gsd-read-guard.js',
32
+ 'gsd-read-injection-scanner.js',
33
+ 'gsd-statusline.js',
34
+ 'gsd-update-banner.js',
35
+ 'gsd-workflow-guard.js',
36
+ // Community hooks (bash, opt-in via .planning/config.json hooks.community)
37
+ 'gsd-session-state.sh',
38
+ 'gsd-validate-commit.sh',
39
+ 'gsd-phase-boundary.sh',
40
+ // Graphify auto-update hook (#3347 / PR #3557 / #3579). Opt-in via
41
+ // .planning/config.json graphify.auto_update; off by default.
42
+ 'gsd-graphify-update.sh'
43
+ ];
44
+
45
+ // Subdirectories under hooks/ whose contents must also ship to dist. Each
46
+ // entry is copied as `hooks/<dir>/*` → `hooks/dist/<dir>/*` so detached
47
+ // helpers (e.g. hooks/lib/gsd-graphify-rebuild.sh) resolve from the hook's
48
+ // installed runtime path. See #3579.
49
+ const HOOKS_SUBDIRS_TO_COPY = ['lib'];
50
+
51
+ // Sync millisecond sleep using Atomics.wait on a throwaway SharedArrayBuffer.
52
+ // Used between Windows rename retries; this script is sync end-to-end so
53
+ // setTimeout would not work. Total worst-case backoff across MAX_ATTEMPTS
54
+ // is bounded (~400ms) — acceptable for a one-shot build script.
55
+ function sleepSync(ms) {
56
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
57
+ }
58
+
59
+ /**
60
+ * Atomic-replace via fs.renameSync, with Windows-only retry and fallback.
61
+ *
62
+ * POSIX rename(2) atomically replaces dest even when readers hold open
63
+ * handles on it. Windows MoveFileEx (which fs.renameSync uses with
64
+ * MOVEFILE_REPLACE_EXISTING) cannot — it throws EPERM/EBUSY when another
65
+ * process has the destination open. Concurrent install.js readers and
66
+ * antivirus scanners are the realistic triggers; both release handles
67
+ * within milliseconds, so a short backoff resolves the race. After
68
+ * retries are exhausted, fall back to copy-then-unlink (re-introduces
69
+ * the truncate-then-write race for this single file but keeps the build
70
+ * moving rather than crashing). If even copy fails because dest is hard-
71
+ * locked, log a non-fatal warning and leave the prior dest in place — a
72
+ * subsequent build invocation will retry from a fresh state.
73
+ */
74
+ function renameAtomicWithRetry(stagedDest, dest, hook) {
75
+ if (process.platform !== 'win32') {
76
+ fs.renameSync(stagedDest, dest);
77
+ return;
78
+ }
79
+ const BACKOFFS_MS = [10, 30, 90, 270];
80
+ for (let attempt = 0; attempt <= BACKOFFS_MS.length; attempt++) {
81
+ try {
82
+ fs.renameSync(stagedDest, dest);
83
+ return;
84
+ } catch (e) {
85
+ const transient = e && (e.code === 'EPERM' || e.code === 'EBUSY');
86
+ if (!transient) throw e;
87
+ if (attempt < BACKOFFS_MS.length) {
88
+ sleepSync(BACKOFFS_MS[attempt]);
89
+ continue;
90
+ }
91
+ // Retries exhausted; fall back to copy-then-unlink.
92
+ try {
93
+ fs.copyFileSync(stagedDest, dest);
94
+ try { fs.unlinkSync(stagedDest); } catch (_) { /* tolerate */ }
95
+ console.warn(`\x1b[33m! ${hook}: rename failed (${e.code}) after ${BACKOFFS_MS.length} retries; used copy-fallback\x1b[0m`);
96
+ return;
97
+ } catch (fallbackErr) {
98
+ try { fs.unlinkSync(stagedDest); } catch (_) { /* tolerate */ }
99
+ console.warn(`\x1b[33m! ${hook}: rename + copy fallback both failed (${e.code} → ${fallbackErr.code || fallbackErr.message}); leaving prior dest in place\x1b[0m`);
100
+ return;
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Validate JavaScript syntax without executing the file.
108
+ * Catches SyntaxError (duplicate const, missing brackets, etc.)
109
+ * before the hook gets shipped to users.
110
+ */
111
+ function validateSyntax(filePath) {
112
+ const content = fs.readFileSync(filePath, 'utf8');
113
+ try {
114
+ // Use vm.compileFunction to check syntax without executing
115
+ new vm.Script(content, { filename: path.basename(filePath) });
116
+ return null; // No error
117
+ } catch (e) {
118
+ if (e instanceof SyntaxError) {
119
+ return e.message;
120
+ }
121
+ throw e;
122
+ }
123
+ }
124
+
125
+ function build() {
126
+ // Ensure dist and staging directories exist (staging is a sibling of dist
127
+ // used to make writes atomic — see STAGE_DIR comment above).
128
+ if (!fs.existsSync(DIST_DIR)) {
129
+ fs.mkdirSync(DIST_DIR, { recursive: true });
130
+ }
131
+ if (!fs.existsSync(STAGE_DIR)) {
132
+ fs.mkdirSync(STAGE_DIR, { recursive: true });
133
+ }
134
+
135
+ let hasErrors = false;
136
+
137
+ // Copy hooks to dist with syntax validation
138
+ for (const hook of HOOKS_TO_COPY) {
139
+ const src = path.join(HOOKS_DIR, hook);
140
+ const dest = path.join(DIST_DIR, hook);
141
+
142
+ if (!fs.existsSync(src)) {
143
+ console.warn(`Warning: ${hook} not found, skipping`);
144
+ continue;
145
+ }
146
+
147
+ // Validate JS syntax before copying (.sh files skip — not Node.js)
148
+ if (hook.endsWith('.js')) {
149
+ const syntaxError = validateSyntax(src);
150
+ if (syntaxError) {
151
+ console.error(`\x1b[31m✗ ${hook}: SyntaxError — ${syntaxError}\x1b[0m`);
152
+ hasErrors = true;
153
+ continue;
154
+ }
155
+ }
156
+
157
+ console.log(`\x1b[32m✓\x1b[0m Copying ${hook}...`);
158
+ // Atomic write: copy to a per-process staging file in the per-PID sibling
159
+ // STAGE_DIR (same filesystem as DIST_DIR so rename(2) is atomic), then
160
+ // rename into place. Multiple test files invoke this script concurrently
161
+ // from their before() hooks; fs.copyFileSync truncates then writes the
162
+ // destination — readers (install.js subprocesses spawned by parallel
163
+ // install tests) can observe the dest empty or partial mid-write,
164
+ // producing flaky failures such as bug-2136 part 4 where installed .sh
165
+ // hooks lacked their "# gsd-hook-version:" header. POSIX rename(2)
166
+ // makes the swap atomic so readers see either the old file or the new
167
+ // file. The staging file lives outside DIST_DIR so readdirSync(DIST_DIR)
168
+ // (in install.js and tests) never observes a transient ".tmp" sibling.
169
+ // Each process uses its own STAGE_DIR (keyed by PID) so concurrent
170
+ // builders never race on staging-dir creation or cleanup.
171
+ const stagedDest = path.join(STAGE_DIR, `${hook}.${Date.now()}`);
172
+ fs.copyFileSync(src, stagedDest);
173
+ // Preserve executable bit for shell scripts before rename so the
174
+ // installed file is executable from the very first observation.
175
+ if (hook.endsWith('.sh')) {
176
+ try { fs.chmodSync(stagedDest, 0o755); } catch (e) { /* Windows */ }
177
+ }
178
+ renameAtomicWithRetry(stagedDest, dest, hook);
179
+ }
180
+
181
+ // Copy whitelisted hook subdirectories (e.g. hooks/lib/) into dist so the
182
+ // installer's readdir-and-isFile loop in bin/install.js sees them and
183
+ // detached hook helpers resolve from the installed runtime path (#3579).
184
+ for (const subdir of HOOKS_SUBDIRS_TO_COPY) {
185
+ const srcDir = path.join(HOOKS_DIR, subdir);
186
+ if (!fs.existsSync(srcDir)) continue;
187
+ const destDir = path.join(DIST_DIR, subdir);
188
+ fs.mkdirSync(destDir, { recursive: true });
189
+ const entries = fs.readdirSync(srcDir, { withFileTypes: true });
190
+ for (const ent of entries) {
191
+ if (!ent.isFile()) continue;
192
+ const srcFile = path.join(srcDir, ent.name);
193
+ const destFile = path.join(destDir, ent.name);
194
+ if (ent.name.endsWith('.js')) {
195
+ const syntaxError = validateSyntax(srcFile);
196
+ if (syntaxError) {
197
+ console.error(`\x1b[31m✗ ${subdir}/${ent.name}: SyntaxError — ${syntaxError}\x1b[0m`);
198
+ hasErrors = true;
199
+ continue;
200
+ }
201
+ }
202
+ console.log(`\x1b[32m✓\x1b[0m Copying ${subdir}/${ent.name}...`);
203
+ const stagedDest = path.join(STAGE_DIR, `${subdir}__${ent.name}.${Date.now()}`);
204
+ fs.copyFileSync(srcFile, stagedDest);
205
+ if (ent.name.endsWith('.sh')) {
206
+ try { fs.chmodSync(stagedDest, 0o755); } catch (e) { /* Windows */ }
207
+ }
208
+ renameAtomicWithRetry(stagedDest, destFile, `${subdir}/${ent.name}`);
209
+ }
210
+ }
211
+
212
+ // Best-effort cleanup of this process's own staging dir. Since STAGE_DIR
213
+ // is per-PID (`.dist-staging-<pid>/`), no other builder touches it — so
214
+ // rmSync with recursive:true is safe and leaves no race window.
215
+ try {
216
+ fs.rmSync(STAGE_DIR, { recursive: true, force: true });
217
+ } catch (e) { /* tolerate ENOENT if the dir was never created (e.g. all hooks skipped) */ }
218
+
219
+ if (hasErrors) {
220
+ console.error('\n\x1b[31mBuild failed: fix syntax errors above before publishing.\x1b[0m');
221
+ process.exit(1);
222
+ }
223
+
224
+ console.log('\nBuild complete.');
225
+ }
226
+
227
+ // Export HOOKS_TO_COPY so tests can require() this file and assert against
228
+ // the typed value instead of regex-parsing the source text (retires
229
+ // pending-migration-to-typed-ir for orphaned-hooks.test.cjs, per #455).
230
+ // Guard the build() call so requiring this file as a module does not trigger
231
+ // a full build run (which copies files and writes to disk).
232
+ if (require.main === module) {
233
+ build();
234
+ }
235
+
236
+ module.exports = { HOOKS_TO_COPY };