@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,592 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { execTool, execGit, platformWriteSync } = require('./shell-command-projection.cjs');
6
+
7
+ // ─── Config Gate ─────────────────────────────────────────────────────────────
8
+
9
+ /**
10
+ * Check whether graphify is enabled in the project config.
11
+ * Reads config.json directly via fs. Returns false by default
12
+ * (when no config, no graphify key, or on error).
13
+ *
14
+ * @param {string} planningDir - Path to .planning directory
15
+ * @returns {boolean}
16
+ */
17
+ function isGraphifyEnabled(planningDir) {
18
+ try {
19
+ const configPath = path.join(planningDir, 'config.json');
20
+ if (!fs.existsSync(configPath)) return false;
21
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
22
+ if (config && config.graphify && config.graphify.enabled === true) return true;
23
+ return false;
24
+ } catch (_e) {
25
+ return false;
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Return the standard disabled response object.
31
+ * @returns {{ disabled: true, message: string }}
32
+ */
33
+ function disabledResponse() {
34
+ return { disabled: true, message: 'graphify is not enabled. Enable with: gsd-tools config-set graphify.enabled true' };
35
+ }
36
+
37
+ // ─── Subprocess Helper ───────────────────────────────────────────────────────
38
+
39
+ /**
40
+ * Execute graphify CLI as a subprocess with proper env and timeout handling.
41
+ *
42
+ * @param {string} cwd - Working directory for the subprocess
43
+ * @param {string[]} args - Arguments to pass to graphify
44
+ * @param {{ timeout?: number }} [options={}] - Options (timeout in ms, default 30000)
45
+ * @returns {{ exitCode: number, stdout: string, stderr: string }}
46
+ */
47
+ /**
48
+ * Frozen enum of typed reason codes for execGraphify failures (#2974).
49
+ * Tests assert on result.reason instead of grepping stderr text.
50
+ */
51
+ const GRAPHIFY_REASON = Object.freeze({
52
+ OK: 'ok',
53
+ ENOENT: 'graphify_not_found',
54
+ TIMEOUT: 'graphify_timed_out',
55
+ EXIT_NONZERO: 'graphify_exit_nonzero',
56
+ });
57
+
58
+ function execGraphify(cwd, args, options = {}) {
59
+ const timeout = options.timeout ?? 30000;
60
+ const result = execTool('graphify', args, {
61
+ cwd,
62
+ timeout,
63
+ env: { ...process.env, PYTHONUNBUFFERED: '1' },
64
+ });
65
+
66
+ // ENOENT — seam normalizes to exitCode 127. Surface as typed reason.
67
+ if (result.error && result.error.code === 'ENOENT') {
68
+ return {
69
+ exitCode: 127,
70
+ stdout: '',
71
+ stderr: 'graphify not found on PATH',
72
+ reason: GRAPHIFY_REASON.ENOENT,
73
+ };
74
+ }
75
+
76
+ // Timeout — seam exposes signal; spawnSync sets SIGTERM when killed by timeout.
77
+ if (result.signal === 'SIGTERM') {
78
+ return {
79
+ exitCode: 124,
80
+ stdout: result.stdout,
81
+ stderr: 'graphify timed out after ' + timeout + 'ms',
82
+ reason: GRAPHIFY_REASON.TIMEOUT,
83
+ timeout_ms: timeout,
84
+ };
85
+ }
86
+
87
+ return {
88
+ exitCode: result.exitCode,
89
+ stdout: result.stdout,
90
+ stderr: result.stderr,
91
+ reason: result.exitCode === 0 ? GRAPHIFY_REASON.OK : GRAPHIFY_REASON.EXIT_NONZERO,
92
+ };
93
+ }
94
+
95
+ // ─── Presence & Version ──────────────────────────────────────────────────────
96
+
97
+ /**
98
+ * Check whether the graphify CLI binary is installed and accessible on PATH.
99
+ * Uses --help (NOT --version, which graphify does not support).
100
+ *
101
+ * @returns {{ installed: boolean, message?: string }}
102
+ */
103
+ function checkGraphifyInstalled() {
104
+ const result = execTool('graphify', ['--help'], { timeout: 5000 });
105
+
106
+ if (result.error) {
107
+ return {
108
+ installed: false,
109
+ message: 'graphify is not installed.\n\nInstall with:\n uv pip install graphifyy && graphify install',
110
+ };
111
+ }
112
+
113
+ return { installed: true };
114
+ }
115
+
116
+ /**
117
+ * Detect graphify version and check compatibility.
118
+ * Tested range: >=0.4.0,<1.0
119
+ *
120
+ * Detection strategy:
121
+ * 1. Try `graphify --version` (works for most CLI installations, incl. venv installs)
122
+ * 2. Fall back to python3 importlib.metadata (legacy / system Python path)
123
+ * 3. Return null version gracefully if both fail
124
+ *
125
+ * @returns {{ version: string|null, compatible: boolean|null, warning: string|null }}
126
+ */
127
+ function checkGraphifyVersion() {
128
+ // Strategy 1: try `graphify --version` directly (2s timeout -- fast path)
129
+ const versionResult = execTool('graphify', ['--version'], { timeout: 2000 });
130
+
131
+ let versionStr = null;
132
+
133
+ if (!versionResult.error && versionResult.exitCode === 0) {
134
+ // graphify --version may emit "graphify 0.4.23" or just "0.4.23"
135
+ const match = versionResult.stdout.match(/(\d+\.\d+(?:\.\d+)*)/);
136
+ if (match) {
137
+ versionStr = match[1];
138
+ }
139
+ }
140
+
141
+ // Strategy 2: fall back to python3 importlib.metadata
142
+ if (!versionStr) {
143
+ const pyResult = execTool('python3', [
144
+ '-c',
145
+ 'from importlib.metadata import version; print(version("graphifyy"))',
146
+ ], { timeout: 5000 });
147
+
148
+ if (!pyResult.error && pyResult.exitCode === 0 && pyResult.stdout) {
149
+ versionStr = pyResult.stdout;
150
+ }
151
+ }
152
+
153
+ if (!versionStr) {
154
+ return { version: null, compatible: null, warning: 'Could not determine graphify version' };
155
+ }
156
+
157
+ const parts = versionStr.split('.').map(Number);
158
+
159
+ if (parts.length < 2 || parts.some(isNaN)) {
160
+ return { version: versionStr, compatible: null, warning: 'Could not parse version: ' + versionStr };
161
+ }
162
+
163
+ const compatible = parts[0] === 0 && parts[1] >= 4;
164
+ const warning = compatible ? null : 'graphify version ' + versionStr + ' is outside tested range >=0.4.0,<1.0';
165
+
166
+ return { version: versionStr, compatible, warning };
167
+ }
168
+
169
+ // ─── Internal Helpers ────────────────────────────────────────────────────────
170
+
171
+ /**
172
+ * Safely read and parse a JSON file. Returns null on missing file or parse error.
173
+ * Prevents crashes on malformed JSON (T-02-01 mitigation).
174
+ *
175
+ * @param {string} filePath - Absolute path to JSON file
176
+ * @returns {object|null}
177
+ */
178
+ function safeReadJson(filePath) {
179
+ try {
180
+ if (!fs.existsSync(filePath)) return null;
181
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
182
+ } catch (_e) {
183
+ return null;
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Build a bidirectional adjacency map from graph nodes and edges.
189
+ * Each node ID maps to an array of { target, edge } entries.
190
+ * Bidirectional: both source->target and target->source are added (Pitfall 3).
191
+ *
192
+ * @param {{ nodes: object[], edges: object[] }} graph
193
+ * @returns {Object.<string, Array<{ target: string, edge: object }>>}
194
+ */
195
+ function buildAdjacencyMap(graph) {
196
+ const adj = {};
197
+ for (const node of (graph.nodes || [])) {
198
+ adj[node.id] = [];
199
+ }
200
+ for (const edge of (graph.edges || graph.links || [])) {
201
+ if (!adj[edge.source]) adj[edge.source] = [];
202
+ if (!adj[edge.target]) adj[edge.target] = [];
203
+ adj[edge.source].push({ target: edge.target, edge });
204
+ adj[edge.target].push({ target: edge.source, edge });
205
+ }
206
+ return adj;
207
+ }
208
+
209
+ /**
210
+ * Seed-then-expand query: find nodes matching term, then BFS-expand up to maxHops.
211
+ * Matches on node label and description (case-insensitive substring, D-01).
212
+ *
213
+ * @param {{ nodes: object[], edges: object[] }} graph
214
+ * @param {string} term - Search term
215
+ * @param {number} [maxHops=2] - Maximum BFS hops from seed nodes
216
+ * @returns {{ nodes: object[], edges: object[], seeds: Set<string> }}
217
+ */
218
+ function seedAndExpand(graph, term, maxHops = 2) {
219
+ const lowerTerm = term.toLowerCase();
220
+ const nodeMap = Object.fromEntries((graph.nodes || []).map(n => [n.id, n]));
221
+ const adj = buildAdjacencyMap(graph);
222
+
223
+ // Seed: match on label and description (case-insensitive substring)
224
+ const seeds = (graph.nodes || []).filter(n =>
225
+ (n.label || '').toLowerCase().includes(lowerTerm) ||
226
+ (n.description || '').toLowerCase().includes(lowerTerm)
227
+ );
228
+
229
+ // BFS expand from seeds
230
+ const visitedNodes = new Set(seeds.map(n => n.id));
231
+ const collectedEdges = [];
232
+ const seenEdgeKeys = new Set();
233
+ let frontier = seeds.map(n => n.id);
234
+
235
+ for (let hop = 0; hop < maxHops && frontier.length > 0; hop++) {
236
+ const nextFrontier = [];
237
+ for (const nodeId of frontier) {
238
+ for (const entry of (adj[nodeId] || [])) {
239
+ // Deduplicate edges by source::target::label key
240
+ const edgeKey = `${entry.edge.source}::${entry.edge.target}::${entry.edge.label || ''}`;
241
+ if (!seenEdgeKeys.has(edgeKey)) {
242
+ seenEdgeKeys.add(edgeKey);
243
+ collectedEdges.push(entry.edge);
244
+ }
245
+ if (!visitedNodes.has(entry.target)) {
246
+ visitedNodes.add(entry.target);
247
+ nextFrontier.push(entry.target);
248
+ }
249
+ }
250
+ }
251
+ frontier = nextFrontier;
252
+ }
253
+
254
+ const resultNodes = [...visitedNodes].map(id => nodeMap[id]).filter(Boolean);
255
+ return { nodes: resultNodes, edges: collectedEdges, seeds: new Set(seeds.map(n => n.id)) };
256
+ }
257
+
258
+ /**
259
+ * Apply token budget by dropping edges by confidence tier (D-04, D-05, D-06).
260
+ * Token estimation: Math.ceil(JSON.stringify(obj).length / 4).
261
+ * Drop order: AMBIGUOUS -> INFERRED -> EXTRACTED.
262
+ *
263
+ * @param {{ nodes: object[], edges: object[], seeds: Set<string> }} result
264
+ * @param {number|null} budgetTokens - Max tokens, or null/falsy for unlimited
265
+ * @returns {{ nodes: object[], edges: object[], trimmed: string|null, total_nodes: number, total_edges: number, term?: string }}
266
+ */
267
+ function applyBudget(result, budgetTokens) {
268
+ if (!budgetTokens) return result;
269
+
270
+ const CONFIDENCE_ORDER = ['AMBIGUOUS', 'INFERRED', 'EXTRACTED'];
271
+ let edges = [...result.edges];
272
+ let omitted = 0;
273
+
274
+ const estimateTokens = (obj) => Math.ceil(JSON.stringify(obj).length / 4);
275
+
276
+ for (const tier of CONFIDENCE_ORDER) {
277
+ if (estimateTokens({ nodes: result.nodes, edges }) <= budgetTokens) break;
278
+ const before = edges.length;
279
+ // Check both confidence and confidence_score field names (Open Question 1)
280
+ edges = edges.filter(e => (e.confidence || e.confidence_score) !== tier);
281
+ omitted += before - edges.length;
282
+ }
283
+
284
+ // Find unreachable nodes after edge removal
285
+ const reachableNodes = new Set();
286
+ for (const edge of edges) {
287
+ reachableNodes.add(edge.source);
288
+ reachableNodes.add(edge.target);
289
+ }
290
+ // Always keep seed nodes
291
+ const nodes = result.nodes.filter(n => reachableNodes.has(n.id) || (result.seeds && result.seeds.has(n.id)));
292
+ const unreachable = result.nodes.length - nodes.length;
293
+
294
+ return {
295
+ nodes,
296
+ edges,
297
+ trimmed: omitted > 0 ? `[${omitted} edges omitted, ${unreachable} nodes unreachable]` : null,
298
+ total_nodes: nodes.length,
299
+ total_edges: edges.length,
300
+ };
301
+ }
302
+
303
+ // ─── Public API ──────────────────────────────────────────────────────────────
304
+
305
+ /**
306
+ * Query the knowledge graph for nodes matching a term, with optional budget cap.
307
+ * Uses seed-then-expand BFS traversal (D-01).
308
+ *
309
+ * @param {string} cwd - Working directory
310
+ * @param {string} term - Search term
311
+ * @param {{ budget?: number|null }} [options={}]
312
+ * @returns {object}
313
+ */
314
+ function graphifyQuery(cwd, term, options = {}) {
315
+ const planningDir = path.join(cwd, '.planning');
316
+ if (!isGraphifyEnabled(planningDir)) return disabledResponse();
317
+
318
+ const graphPath = path.join(planningDir, 'graphs', 'graph.json');
319
+ if (!fs.existsSync(graphPath)) {
320
+ return { error: 'No graph built yet. Run graphify build first.' };
321
+ }
322
+
323
+ const graph = safeReadJson(graphPath);
324
+ if (!graph) {
325
+ return { error: 'Failed to parse graph.json' };
326
+ }
327
+
328
+ let result = seedAndExpand(graph, term);
329
+
330
+ if (options.budget) {
331
+ result = applyBudget(result, options.budget);
332
+ }
333
+
334
+ return {
335
+ term,
336
+ nodes: result.nodes,
337
+ edges: result.edges,
338
+ total_nodes: result.nodes.length,
339
+ total_edges: result.edges.length,
340
+ trimmed: result.trimmed || null,
341
+ };
342
+ }
343
+
344
+ /**
345
+ * Strict 4-40 hex fence for graph.built_at_commit values (#3170). Anything
346
+ * else (dashed, prose, empty) is treated as absent so a hostile graph.json
347
+ * cannot smuggle a `--upload-pack=…` option into a `git` argv.
348
+ */
349
+ const COMMIT_HASH_RE = /^[0-9a-f]{4,40}$/i;
350
+
351
+ /**
352
+ * Read git HEAD for the project at `cwd`. Returns the full commit hash on
353
+ * success, or null when cwd is not a git repo / `git` is not on PATH.
354
+ */
355
+ function readGitHead(cwd) {
356
+ const r = execGit(['rev-parse', 'HEAD'], { cwd });
357
+ if (r.exitCode !== 0) return null;
358
+ return r.stdout.trim() || null;
359
+ }
360
+
361
+ /**
362
+ * Count commits between `from` and `to` (exclusive..inclusive, like
363
+ * `git rev-list --count A..B`). Returns null when either ref is unreachable
364
+ * or the cwd is not a git repo.
365
+ */
366
+ function countCommitsBetween(cwd, from, to) {
367
+ const r = execGit(['rev-list', '--count', `${from}..${to}`], { cwd });
368
+ if (r.exitCode !== 0) return null;
369
+ const n = parseInt(r.stdout.trim(), 10);
370
+ return Number.isFinite(n) ? n : null;
371
+ }
372
+
373
+ /**
374
+ * Return status information about the knowledge graph (STAT-01, STAT-02).
375
+ *
376
+ * Surfaces the graphify v0.7+ commit-staleness signal as four optional
377
+ * fields when graph.built_at_commit is present and validly formatted
378
+ * (#3170). Tri-state on commit_stale: null means "we don't know" (pre-v0.7
379
+ * graph, no git, or unreachable commit), distinct from false ("known
380
+ * fresh").
381
+ *
382
+ * @param {string} cwd - Working directory
383
+ * @returns {object}
384
+ */
385
+ function graphifyStatus(cwd) {
386
+ const planningDir = path.join(cwd, '.planning');
387
+ if (!isGraphifyEnabled(planningDir)) return disabledResponse();
388
+
389
+ const graphPath = path.join(planningDir, 'graphs', 'graph.json');
390
+ if (!fs.existsSync(graphPath)) {
391
+ return { exists: false, message: 'No graph built yet. Run graphify build to create one.' };
392
+ }
393
+
394
+ const stat = fs.statSync(graphPath);
395
+ const graph = safeReadJson(graphPath);
396
+ if (!graph) {
397
+ return { error: 'Failed to parse graph.json' };
398
+ }
399
+
400
+ const STALE_MS = 24 * 60 * 60 * 1000; // 24 hours
401
+ const age = Date.now() - stat.mtimeMs;
402
+
403
+ // Commit-staleness signal (#3170). Validate before passing to git.
404
+ const rawBuilt = (graph.built_at_commit || '').toString().trim();
405
+ const builtAt = COMMIT_HASH_RE.test(rawBuilt) ? rawBuilt : null;
406
+ const head = readGitHead(cwd);
407
+ let commitsBehind = null;
408
+ let commitStale = null;
409
+ if (builtAt && head) {
410
+ commitsBehind = countCommitsBetween(cwd, builtAt, head);
411
+ if (commitsBehind !== null) commitStale = commitsBehind > 0;
412
+ }
413
+
414
+ // Auto-update status (#3347). Read .last-build-status.json written by the
415
+ // hooks/gsd-graphify-update.sh PostToolUse hook (opt-in via graphify.auto_update,
416
+ // default false). When the most recent auto-build is "failed" or still "running",
417
+ // fold that into the existing `stale: true` signal so consumers (gsd-planner,
418
+ // gsd-phase-researcher) surface the standard "treat semantic relationships as
419
+ // approximate" annotation without per-consumer prompt changes. The full state
420
+ // (running/failed/exit_code/duration_ms/head_at_build) is exposed under
421
+ // `last_build` for callers that want richer context.
422
+ const statusPath = path.join(planningDir, 'graphs', '.last-build-status.json');
423
+ const lastBuildAutoUpdate = fs.existsSync(statusPath) ? safeReadJson(statusPath) : null;
424
+ const autoUpdateStale =
425
+ lastBuildAutoUpdate &&
426
+ (lastBuildAutoUpdate.status === 'failed' || lastBuildAutoUpdate.status === 'running');
427
+
428
+ return {
429
+ exists: true,
430
+ last_build: stat.mtime.toISOString(),
431
+ node_count: (graph.nodes || []).length,
432
+ edge_count: (graph.edges || graph.links || []).length,
433
+ hyperedge_count: (graph.hyperedges || []).length,
434
+ stale: age > STALE_MS || Boolean(autoUpdateStale),
435
+ age_hours: Math.round(age / (60 * 60 * 1000)),
436
+ built_at_commit: builtAt ? builtAt.slice(0, 7) : null,
437
+ current_commit: head ? head.slice(0, 7) : null,
438
+ commits_behind: commitsBehind,
439
+ commit_stale: commitStale,
440
+ last_build_auto_update: lastBuildAutoUpdate || null,
441
+ };
442
+ }
443
+
444
+ /**
445
+ * Compute topology-level diff between current graph and last build snapshot (D-07, D-08, D-09).
446
+ *
447
+ * @param {string} cwd - Working directory
448
+ * @returns {object}
449
+ */
450
+ function graphifyDiff(cwd) {
451
+ const planningDir = path.join(cwd, '.planning');
452
+ if (!isGraphifyEnabled(planningDir)) return disabledResponse();
453
+
454
+ const snapshotPath = path.join(planningDir, 'graphs', '.last-build-snapshot.json');
455
+ const graphPath = path.join(planningDir, 'graphs', 'graph.json');
456
+
457
+ if (!fs.existsSync(snapshotPath)) {
458
+ return { no_baseline: true, message: 'No previous snapshot. Run graphify build first, then build again to generate a diff baseline.' };
459
+ }
460
+
461
+ if (!fs.existsSync(graphPath)) {
462
+ return { error: 'No current graph. Run graphify build first.' };
463
+ }
464
+
465
+ const current = safeReadJson(graphPath);
466
+ const snapshot = safeReadJson(snapshotPath);
467
+
468
+ if (!current || !snapshot) {
469
+ return { error: 'Failed to parse graph or snapshot file' };
470
+ }
471
+
472
+ // Diff nodes
473
+ const currentNodeMap = Object.fromEntries((current.nodes || []).map(n => [n.id, n]));
474
+ const snapshotNodeMap = Object.fromEntries((snapshot.nodes || []).map(n => [n.id, n]));
475
+
476
+ const nodesAdded = Object.keys(currentNodeMap).filter(id => !snapshotNodeMap[id]);
477
+ const nodesRemoved = Object.keys(snapshotNodeMap).filter(id => !currentNodeMap[id]);
478
+ const nodesChanged = Object.keys(currentNodeMap).filter(id =>
479
+ snapshotNodeMap[id] && JSON.stringify(currentNodeMap[id]) !== JSON.stringify(snapshotNodeMap[id])
480
+ );
481
+
482
+ // Diff edges (keyed by source+target+relation)
483
+ const edgeKey = (e) => `${e.source}::${e.target}::${e.relation || e.label || ''}`;
484
+ const currentEdgeMap = Object.fromEntries((current.edges || current.links || []).map(e => [edgeKey(e), e]));
485
+ const snapshotEdgeMap = Object.fromEntries((snapshot.edges || snapshot.links || []).map(e => [edgeKey(e), e]));
486
+
487
+ const edgesAdded = Object.keys(currentEdgeMap).filter(k => !snapshotEdgeMap[k]);
488
+ const edgesRemoved = Object.keys(snapshotEdgeMap).filter(k => !currentEdgeMap[k]);
489
+ const edgesChanged = Object.keys(currentEdgeMap).filter(k =>
490
+ snapshotEdgeMap[k] && JSON.stringify(currentEdgeMap[k]) !== JSON.stringify(snapshotEdgeMap[k])
491
+ );
492
+
493
+ return {
494
+ nodes: { added: nodesAdded.length, removed: nodesRemoved.length, changed: nodesChanged.length },
495
+ edges: { added: edgesAdded.length, removed: edgesRemoved.length, changed: edgesChanged.length },
496
+ timestamp: snapshot.timestamp || null,
497
+ };
498
+ }
499
+
500
+ // ─── Build Pipeline (Phase 3) ───────────────────────────────────────────────
501
+
502
+ /**
503
+ * Pre-flight checks for graphify build (BUILD-01, BUILD-02, D-09).
504
+ * Does NOT invoke graphify -- returns structured JSON for the builder agent.
505
+ *
506
+ * @param {string} cwd - Working directory
507
+ * @returns {object}
508
+ */
509
+ function graphifyBuild(cwd) {
510
+ const planningDir = path.join(cwd, '.planning');
511
+ if (!isGraphifyEnabled(planningDir)) return disabledResponse();
512
+
513
+ const installed = checkGraphifyInstalled();
514
+ if (!installed.installed) return { error: installed.message };
515
+
516
+ const version = checkGraphifyVersion();
517
+
518
+ // Ensure output directory exists (D-05)
519
+ const graphsDir = path.join(planningDir, 'graphs');
520
+ fs.mkdirSync(graphsDir, { recursive: true });
521
+
522
+ // Read build timeout from config -- default 300s per D-02
523
+ const config = safeReadJson(path.join(planningDir, 'config.json')) || {};
524
+ const timeoutSec = (config.graphify && config.graphify.build_timeout) || 300;
525
+
526
+ return {
527
+ action: 'spawn_agent',
528
+ graphs_dir: graphsDir,
529
+ graphify_out: path.join(cwd, 'graphify-out'),
530
+ timeout_seconds: timeoutSec,
531
+ version: version.version,
532
+ version_warning: version.warning,
533
+ artifacts: ['graph.json', 'graph.html', 'GRAPH_REPORT.md'],
534
+ };
535
+ }
536
+
537
+ /**
538
+ * Write a diff snapshot after successful build (D-06).
539
+ * Reads graph.json from .planning/graphs/ and writes .last-build-snapshot.json
540
+ * using platformWriteSync for crash safety.
541
+ *
542
+ * @param {string} cwd - Working directory
543
+ * @returns {object}
544
+ */
545
+ function writeSnapshot(cwd) {
546
+ const graphPath = path.join(cwd, '.planning', 'graphs', 'graph.json');
547
+ const graph = safeReadJson(graphPath);
548
+ if (!graph) return { error: 'Cannot write snapshot: graph.json not parseable' };
549
+
550
+ const snapshot = {
551
+ version: 1,
552
+ timestamp: new Date().toISOString(),
553
+ nodes: graph.nodes || [],
554
+ edges: graph.edges || graph.links || [],
555
+ };
556
+
557
+ const snapshotPath = path.join(cwd, '.planning', 'graphs', '.last-build-snapshot.json');
558
+ platformWriteSync(snapshotPath, JSON.stringify(snapshot, null, 2));
559
+ return {
560
+ saved: true,
561
+ timestamp: snapshot.timestamp,
562
+ node_count: snapshot.nodes.length,
563
+ edge_count: snapshot.edges.length,
564
+ };
565
+ }
566
+
567
+ // ─── Exports ─────────────────────────────────────────────────────────────────
568
+
569
+ module.exports = {
570
+ // Config gate
571
+ isGraphifyEnabled,
572
+ disabledResponse,
573
+ // Subprocess
574
+ execGraphify,
575
+ GRAPHIFY_REASON,
576
+ // Presence and version
577
+ checkGraphifyInstalled,
578
+ checkGraphifyVersion,
579
+ // Query (Phase 2)
580
+ graphifyQuery,
581
+ safeReadJson,
582
+ buildAdjacencyMap,
583
+ seedAndExpand,
584
+ applyBudget,
585
+ // Status (Phase 2)
586
+ graphifyStatus,
587
+ // Diff (Phase 2)
588
+ graphifyDiff,
589
+ // Build (Phase 3)
590
+ graphifyBuild,
591
+ writeSnapshot,
592
+ };