@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,548 @@
1
+ #!/usr/bin/env node
2
+ // gsd-hook-version: {{GSD_VERSION}}
3
+ // Claude Code Statusline - GSD Edition
4
+ // Shows: model | current task (or GSD state) | directory | context usage
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const os = require('os');
9
+ const { isSemverNewer } = require('../get-shit-done/bin/lib/semver-compare.cjs');
10
+
11
+ // --- Config + last-command readers ------------------------------------------
12
+
13
+ /**
14
+ * Walk up from dir looking for .planning/config.json and return its parsed contents.
15
+ * Returns {} if not found or unreadable.
16
+ */
17
+ function readGsdConfig(dir) {
18
+ const home = os.homedir();
19
+ let current = dir;
20
+ for (let i = 0; i < 10; i++) {
21
+ const candidate = path.join(current, '.planning', 'config.json');
22
+ if (fs.existsSync(candidate)) {
23
+ try {
24
+ return JSON.parse(fs.readFileSync(candidate, 'utf8')) || {};
25
+ } catch (e) {
26
+ return {};
27
+ }
28
+ }
29
+ const parent = path.dirname(current);
30
+ if (parent === current || current === home) break;
31
+ current = parent;
32
+ }
33
+ return {};
34
+ }
35
+
36
+ /**
37
+ * Lookup a dotted key path (e.g. 'statusline.show_last_command') in a config
38
+ * object that may use either nested or flat keys.
39
+ */
40
+ function getConfigValue(cfg, keyPath) {
41
+ if (!cfg || typeof cfg !== 'object') return undefined;
42
+ if (keyPath in cfg) return cfg[keyPath];
43
+ const parts = keyPath.split('.');
44
+ let cur = cfg;
45
+ for (const p of parts) {
46
+ if (cur == null || typeof cur !== 'object' || !(p in cur)) return undefined;
47
+ cur = cur[p];
48
+ }
49
+ return cur;
50
+ }
51
+
52
+ /**
53
+ * Extract the most recently invoked slash command from a Claude Code JSONL
54
+ * transcript file. Returns the command name (no leading slash) or null.
55
+ *
56
+ * Claude Code embeds slash invocations in user messages as
57
+ * <command-name>/foo</command-name>
58
+ * We scan lines from the end of the file, stopping at the first match.
59
+ */
60
+ function readLastSlashCommand(transcriptPath) {
61
+ if (!transcriptPath || typeof transcriptPath !== 'string') return null;
62
+ let content;
63
+ try {
64
+ if (!fs.existsSync(transcriptPath)) return null;
65
+ // Read only the tail — typical transcripts grow large. 256 KiB comfortably
66
+ // covers dozens of recent turns while staying cheap per render.
67
+ const stat = fs.statSync(transcriptPath);
68
+ const MAX = 256 * 1024;
69
+ const start = Math.max(0, stat.size - MAX);
70
+ const fd = fs.openSync(transcriptPath, 'r');
71
+ try {
72
+ const buf = Buffer.alloc(stat.size - start);
73
+ fs.readSync(fd, buf, 0, buf.length, start);
74
+ content = buf.toString('utf8');
75
+ } finally {
76
+ fs.closeSync(fd);
77
+ }
78
+ } catch (e) {
79
+ return null;
80
+ }
81
+ // Find the LAST occurrence — scan right-to-left via lastIndexOf on the tag.
82
+ const tagClose = '</command-name>';
83
+ const idx = content.lastIndexOf(tagClose);
84
+ if (idx < 0) return null;
85
+ const openTag = '<command-name>';
86
+ const openIdx = content.lastIndexOf(openTag, idx);
87
+ if (openIdx < 0) return null;
88
+ let name = content.slice(openIdx + openTag.length, idx).trim();
89
+ // Strip a leading slash if present, and any trailing arguments-on-same-line noise.
90
+ if (name.startsWith('/')) name = name.slice(1);
91
+ // Command names in Claude Code transcripts are plain identifiers like "gsd-plan-phase"
92
+ // or namespaced like "plugin:skill". Reject anything with whitespace/newlines/control chars.
93
+ if (!name || /[\s\\"<>]/.test(name) || name.length > 80) return null;
94
+ return name;
95
+ }
96
+
97
+ // --- GSD state reader -------------------------------------------------------
98
+
99
+ /**
100
+ * Walk up from dir looking for .planning/STATE.md.
101
+ * Returns parsed state object or null.
102
+ */
103
+ function readGsdState(dir) {
104
+ const home = os.homedir();
105
+ let current = dir;
106
+ for (let i = 0; i < 10; i++) {
107
+ const candidate = path.join(current, '.planning', 'STATE.md');
108
+ if (fs.existsSync(candidate)) {
109
+ try {
110
+ return parseStateMd(fs.readFileSync(candidate, 'utf8'));
111
+ } catch (e) {
112
+ return null;
113
+ }
114
+ }
115
+ const parent = path.dirname(current);
116
+ if (parent === current || current === home) break;
117
+ current = parent;
118
+ }
119
+ return null;
120
+ }
121
+
122
+ /**
123
+ * Parse STATE.md frontmatter + Phase line from body.
124
+ *
125
+ * Returns:
126
+ * { status, milestone, milestoneName, phaseNum, phaseTotal, phaseName,
127
+ * activePhase, nextAction, nextPhases, completedPhases, totalPhases, percent }
128
+ *
129
+ * Phase-lifecycle fields (issue #2833):
130
+ * - activePhase : phase number ("4.5") when an orchestrator is mid-flight, null otherwise
131
+ * - nextAction : recommended next command ("execute-phase") when idle, null otherwise
132
+ * - nextPhases : array of phase numbers (["4.5"]) for nextAction, null otherwise
133
+ * - completedPhases / totalPhases / percent : milestone progress dimension
134
+ *
135
+ * All new fields default to undefined when absent — formatGsdState() degrades
136
+ * gracefully so existing STATE.md files (without these fields) keep working.
137
+ */
138
+ function parseStateMd(content) {
139
+ const state = {};
140
+
141
+ // YAML frontmatter between --- markers (anchored at file start)
142
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
143
+ if (fmMatch) {
144
+ const fm = fmMatch[1];
145
+ // Top-level scalar key: value
146
+ for (const line of fm.split('\n')) {
147
+ const m = line.match(/^(\w+):\s*(.+)/);
148
+ if (!m) continue;
149
+ const [, key, val] = m;
150
+ const v = val.trim().replace(/^["']|["']$/g, '');
151
+ // status / milestone-level fields (existing — preserved exactly)
152
+ if (key === 'status') state.status = v === 'null' ? null : v;
153
+ if (key === 'milestone') state.milestone = v === 'null' ? null : v;
154
+ if (key === 'milestone_name') state.milestoneName = v === 'null' ? null : v;
155
+ // Phase-lifecycle fields (new in issue #2833)
156
+ // active_phase: phase number when an orchestrator is in-flight, null when idle
157
+ if (key === 'active_phase') state.activePhase = (v === 'null' || v === '') ? null : v;
158
+ // next_action: recommended command when idle (discuss-phase / plan-phase / execute-phase / verify-phase)
159
+ if (key === 'next_action') state.nextAction = (v === 'null' || v === '') ? null : v;
160
+ }
161
+ // next_phases supports both flow array and block-list YAML forms.
162
+ const npFlowMatch = fm.match(/^next_phases:\s*\[([^\]]*)\]/m);
163
+ if (npFlowMatch) {
164
+ const items = npFlowMatch[1].split(',').map(s => s.trim().replace(/^["']|["']$/g, '')).filter(Boolean);
165
+ state.nextPhases = items.length > 0 ? items : null;
166
+ } else {
167
+ const npBlockMatch = fm.match(/^next_phases:\s*\n((?:[ \t]*-[ \t]*[^\n]+\n?)*)/m);
168
+ if (npBlockMatch) {
169
+ const items = npBlockMatch[1]
170
+ .split('\n')
171
+ .map(line => line.match(/^[ \t]*-[ \t]*(.+)$/))
172
+ .filter(Boolean)
173
+ .map(m => m[1].trim().replace(/^["']|["']$/g, ''))
174
+ .filter(Boolean);
175
+ state.nextPhases = items.length > 0 ? items : null;
176
+ }
177
+ }
178
+ // progress nested block: completed_phases / total_phases / percent (2-space indent)
179
+ const progMatch = fm.match(/^progress:\s*\n((?:[ \t]+\w+:.+\n?)+)/m);
180
+ if (progMatch) {
181
+ const cp = progMatch[1].match(/^[ \t]+completed_phases:\s*(\d+)/m);
182
+ const tp = progMatch[1].match(/^[ \t]+total_phases:\s*(\d+)/m);
183
+ const pc = progMatch[1].match(/^[ \t]+percent:\s*(\d+)/m);
184
+ if (cp) state.completedPhases = cp[1];
185
+ if (tp) state.totalPhases = tp[1];
186
+ if (pc) state.percent = pc[1];
187
+ }
188
+ }
189
+
190
+ // Phase: N of M (name) or Phase: none active (...)
191
+ const phaseMatch = content.match(/^Phase:\s*(\d+)\s+of\s+(\d+)(?:\s+\(([^)]+)\))?/m);
192
+ if (phaseMatch) {
193
+ state.phaseNum = phaseMatch[1];
194
+ state.phaseTotal = phaseMatch[2];
195
+ state.phaseName = phaseMatch[3] || null;
196
+ }
197
+
198
+ // Fallback: parse Status: from body when frontmatter is absent
199
+ if (!state.status) {
200
+ const bodyStatus = content.match(/^Status:\s*(.+)/m);
201
+ if (bodyStatus) {
202
+ const raw = bodyStatus[1].trim().toLowerCase();
203
+ if (raw.includes('ready to plan') || raw.includes('planning')) state.status = 'planning';
204
+ else if (raw.includes('execut')) state.status = 'executing';
205
+ else if (raw.includes('complet') || raw.includes('archived')) state.status = 'complete';
206
+ }
207
+ }
208
+
209
+ return state;
210
+ }
211
+
212
+ /**
213
+ * Render a 10-segment milestone progress bar (matches the context meter style).
214
+ *
215
+ * @param {number|string|null|undefined} percent — 0-100; missing/NaN returns ''
216
+ * @returns {string} '[█████░░░░░] 50%' or '' (so callers can `[bar].filter(Boolean)`)
217
+ */
218
+ function renderProgressBar(percent) {
219
+ if (percent == null || isNaN(percent)) return '';
220
+ const pct = Math.max(0, Math.min(100, parseInt(percent, 10)));
221
+ const filled = Math.floor(pct / 10);
222
+ const bar = '█'.repeat(filled) + '░'.repeat(10 - filled);
223
+ return `[${bar}] ${pct}%`;
224
+ }
225
+
226
+ /**
227
+ * Format GSD state into display string.
228
+ *
229
+ * Backward-compatible default (no new fields populated):
230
+ * "v1.9 Code Quality · executing · fix-graphiti-deployment (1/5)"
231
+ *
232
+ * Phase-lifecycle scenes (issue #2833 — activate when STATE.md frontmatter
233
+ * carries the new fields; otherwise rendering falls through to the default):
234
+ *
235
+ * active_phase set → "v2.0 [██░] X% · Phase 4.5 executing"
236
+ * active_phase null + next_action set → "v2.0 [██░] X% · next execute-phase 4.5"
237
+ * percent=100 (milestone done) → "v2.0 [██████████] 100% · milestone complete"
238
+ * none of the above → existing "<status> · <phase>" path
239
+ *
240
+ * Progress bar is opt-in: appended to the milestone segment only when
241
+ * progress.percent is present in frontmatter; absent → empty string.
242
+ */
243
+ function formatGsdState(s) {
244
+ const parts = [];
245
+
246
+ // Milestone segment: version + name + (opt-in) progress bar
247
+ if (s.milestone || s.milestoneName) {
248
+ const ver = s.milestone || '';
249
+ const name = (s.milestoneName && s.milestoneName !== 'milestone') ? s.milestoneName : '';
250
+ const bar = renderProgressBar(s.percent);
251
+ const pieces = [ver, name, bar].filter(Boolean);
252
+ if (pieces.length > 0) parts.push(pieces.join(' '));
253
+ }
254
+
255
+ // Phase-lifecycle scenes (issue #2833) — first match wins; falls through to
256
+ // the original "<status> · <phase>" path when none of the new fields apply.
257
+ const phasesStr = (s.nextPhases && s.nextPhases.length > 0) ? s.nextPhases.join('/') : null;
258
+
259
+ if (s.activePhase) {
260
+ // Scene 1: an orchestrator is mid-flight on this phase.
261
+ // stage = whichever lifecycle status was written by the orchestrator
262
+ // (discussing / planning / executing / verifying)
263
+ const stage = s.status || '';
264
+ parts.push(stage ? `Phase ${s.activePhase} ${stage}` : `Phase ${s.activePhase}`);
265
+ } else if (s.nextAction && phasesStr) {
266
+ // Scene 2: idle + a recommended next command is visible to the user.
267
+ // Surfaces "what to run next" without the user opening STATE.md.
268
+ parts.push(`next ${s.nextAction} ${phasesStr}`);
269
+ } else if (Number(s.percent) === 100 || (s.completedPhases && s.totalPhases && s.completedPhases === s.totalPhases)) {
270
+ // Scene 3: milestone complete (every phase done).
271
+ parts.push('milestone complete');
272
+ } else {
273
+ // Backward-compatible default — preserved EXACTLY for STATE.md files that
274
+ // don't carry the new lifecycle fields. Identical output to v1.38.x and
275
+ // earlier so no existing project's status-line changes shape.
276
+ if (s.status) parts.push(s.status);
277
+ if (s.phaseNum && s.phaseTotal) {
278
+ const phase = s.phaseName
279
+ ? `${s.phaseName} (${s.phaseNum}/${s.phaseTotal})`
280
+ : `ph ${s.phaseNum}/${s.phaseTotal}`;
281
+ parts.push(phase);
282
+ }
283
+ }
284
+
285
+ return parts.join(' · ');
286
+ }
287
+
288
+ // --- stdin ------------------------------------------------------------------
289
+
290
+ function runStatusline() {
291
+ let input = '';
292
+ // Timeout guard: if stdin doesn't close within 3s (e.g. pipe issues on
293
+ // Windows/Git Bash), exit silently instead of hanging. See #775.
294
+ const stdinTimeout = setTimeout(() => process.exit(0), 3000);
295
+ process.stdin.setEncoding('utf8');
296
+ process.stdin.on('data', chunk => input += chunk);
297
+ process.stdin.on('end', () => {
298
+ clearTimeout(stdinTimeout);
299
+ try {
300
+ const data = JSON.parse(input);
301
+ const model = data.model?.display_name || 'Claude';
302
+ const dir = data.workspace?.current_dir || process.cwd();
303
+ const session = data.session_id || '';
304
+ const remaining = data.context_window?.remaining_percentage;
305
+
306
+ // Context window display (shows USED percentage scaled to usable context)
307
+ // Claude Code reserves a buffer for autocompact. By default this is ~16.5%
308
+ // of the total window, but users can override it via CLAUDE_CODE_AUTO_COMPACT_WINDOW
309
+ // (a token count). When the env var is set, compute the buffer % dynamically so
310
+ // the meter correctly reflects early-compaction configurations (#2219).
311
+ const totalCtx = data.context_window?.total_tokens || 1_000_000;
312
+ const acw = parseInt(process.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW || '0', 10);
313
+ const AUTO_COMPACT_BUFFER_PCT = acw > 0
314
+ ? Math.min(100, (acw / totalCtx) * 100)
315
+ : 16.5;
316
+ let ctx = '';
317
+ if (remaining != null) {
318
+ // Normalize: subtract buffer from remaining, scale to usable range
319
+ const usableRemaining = Math.max(0, ((remaining - AUTO_COMPACT_BUFFER_PCT) / (100 - AUTO_COMPACT_BUFFER_PCT)) * 100);
320
+ const used = Math.max(0, Math.min(100, Math.round(100 - usableRemaining)));
321
+
322
+ // Write context metrics to bridge file for the context-monitor PostToolUse hook.
323
+ // The monitor reads this file to inject agent-facing warnings when context is low.
324
+ // Reject session IDs with path separators or traversal sequences to prevent
325
+ // a malicious session_id from writing files outside the temp directory.
326
+ const sessionSafe = session && !/[/\\]|\.\./.test(session);
327
+ if (sessionSafe) {
328
+ try {
329
+ const bridgePath = path.join(os.tmpdir(), `claude-ctx-${session}.json`);
330
+ // used_pct written to the bridge must match CC's native /context reporting:
331
+ // raw used = 100 - remaining_percentage (no buffer normalization applied).
332
+ // The normalized `used` value is correct for the statusline progress bar but
333
+ // inflates the context monitor warning messages by ~13 points (#2451).
334
+ const rawUsedPct = Math.round(100 - remaining);
335
+ const bridgeData = JSON.stringify({
336
+ session_id: session,
337
+ remaining_percentage: remaining,
338
+ used_pct: rawUsedPct,
339
+ timestamp: Math.floor(Date.now() / 1000)
340
+ });
341
+ fs.writeFileSync(bridgePath, bridgeData);
342
+ } catch (e) {
343
+ // Silent fail -- bridge is best-effort, don't break statusline
344
+ }
345
+ }
346
+
347
+ // Build progress bar (10 segments)
348
+ const filled = Math.floor(used / 10);
349
+ const bar = '█'.repeat(filled) + '░'.repeat(10 - filled);
350
+
351
+ // Color based on usable context thresholds
352
+ if (used < 50) {
353
+ ctx = ` \x1b[32m${bar} ${used}%\x1b[0m`;
354
+ } else if (used < 65) {
355
+ ctx = ` \x1b[33m${bar} ${used}%\x1b[0m`;
356
+ } else if (used < 80) {
357
+ ctx = ` \x1b[38;5;208m${bar} ${used}%\x1b[0m`;
358
+ } else {
359
+ ctx = ` \x1b[5;31m💀 ${bar} ${used}%\x1b[0m`;
360
+ }
361
+ }
362
+
363
+ // Current task from todos
364
+ let task = '';
365
+ const homeDir = os.homedir();
366
+ // Respect CLAUDE_CONFIG_DIR for custom config directory setups (#870)
367
+ const claudeDir = process.env.CLAUDE_CONFIG_DIR || path.join(homeDir, '.claude');
368
+ const todosDir = path.join(claudeDir, 'todos');
369
+ if (session && fs.existsSync(todosDir)) {
370
+ try {
371
+ // Single-pass max-by-mtime scan: only the newest matching todos file
372
+ // is needed, so the O(n log n) sort and the intermediate array from the
373
+ // prior `.filter().map(statSync).sort()` chain are unnecessary. Identical
374
+ // I/O (one statSync per match) and identical result. (#305)
375
+ let latest = null;
376
+ for (const entry of fs.readdirSync(todosDir)) {
377
+ if (!entry.startsWith(session) || !entry.includes('-agent-') || !entry.endsWith('.json')) continue;
378
+ const mtime = fs.statSync(path.join(todosDir, entry)).mtime;
379
+ if (!latest || mtime > latest.mtime) latest = { name: entry, mtime };
380
+ }
381
+
382
+ if (latest) {
383
+ try {
384
+ const todos = JSON.parse(fs.readFileSync(path.join(todosDir, latest.name), 'utf8'));
385
+ const inProgress = todos.find(t => t.status === 'in_progress');
386
+ if (inProgress) task = inProgress.activeForm || '';
387
+ } catch (e) {}
388
+ }
389
+ } catch (e) {
390
+ // Silently fail on file system errors - don't break statusline
391
+ }
392
+ }
393
+
394
+ // GSD state (milestone · status · phase) — shown when no todo task
395
+ const gsdStateStr = task ? '' : formatGsdState(readGsdState(dir) || {});
396
+
397
+ // GSD update available?
398
+ // Check shared cache first (#1421), fall back to runtime-specific cache for
399
+ // backward compatibility with older gsd-check-update.js versions.
400
+ let gsdUpdate = '';
401
+ const sharedCacheFile = path.join(homeDir, '.cache', 'gsd', 'gsd-update-check.json');
402
+ const legacyCacheFile = path.join(claudeDir, 'cache', 'gsd-update-check.json');
403
+ const cacheFile = fs.existsSync(sharedCacheFile) ? sharedCacheFile : legacyCacheFile;
404
+ if (fs.existsSync(cacheFile)) {
405
+ try {
406
+ const cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
407
+ if (cache.update_available) {
408
+ gsdUpdate = '\x1b[33m⬆ /gsd:update\x1b[0m │ ';
409
+ }
410
+ if (cache.stale_hooks && cache.stale_hooks.length > 0) {
411
+ // If installed version is ahead of npm latest, this is a dev install.
412
+ // Running /gsd:update would downgrade — show a contextual warning instead.
413
+ const isDevInstall = (
414
+ cache.installed &&
415
+ cache.latest &&
416
+ cache.latest !== 'unknown' &&
417
+ isInstalledAheadOfLatest(cache.installed, cache.latest)
418
+ );
419
+ if (isDevInstall) {
420
+ gsdUpdate += '\x1b[33m⚠ dev install — re-run installer to sync hooks\x1b[0m │ ';
421
+ } else {
422
+ gsdUpdate += '\x1b[31m⚠ stale hooks — run /gsd:update\x1b[0m │ ';
423
+ }
424
+ }
425
+ } catch (e) {}
426
+ }
427
+
428
+ // Last-slash-command suffix and context_position config (#2538, #2937).
429
+ // Reads the active session transcript for the most recent <command-name> tag.
430
+ // Failure here must never break the statusline — wrap the entire lookup.
431
+ let lastCmdSuffix = '';
432
+ let position = 'end';
433
+ try {
434
+ const cfg = readGsdConfig(dir);
435
+ if (getConfigValue(cfg, 'statusline.show_last_command') === true) {
436
+ const transcriptPath = data.transcript_path;
437
+ const lastCmd = readLastSlashCommand(transcriptPath);
438
+ if (lastCmd) {
439
+ lastCmdSuffix = ` │ \x1b[2mlast: /${lastCmd}\x1b[0m`;
440
+ }
441
+ }
442
+ const cfgPos = getConfigValue(cfg, 'statusline.context_position');
443
+ if (cfgPos != null) position = cfgPos;
444
+ } catch (e) {
445
+ // Never break the statusline on config/transcript errors
446
+ }
447
+
448
+ // Output
449
+ const dirname = path.basename(dir);
450
+ const middle = task
451
+ ? `\x1b[1m${task}\x1b[0m`
452
+ : gsdStateStr
453
+ ? `\x1b[2m${gsdStateStr}\x1b[0m`
454
+ : null;
455
+
456
+ process.stdout.write(composeStatusline({ gsdUpdate, model, ctx, middle, dirname, lastCmdSuffix, position }));
457
+ } catch (e) {
458
+ // Silent fail - don't break statusline on parse errors
459
+ }
460
+ });
461
+ }
462
+
463
+ // --- Layout composer --------------------------------------------------------
464
+
465
+ /**
466
+ * Compose the statusline string from pre-built segments.
467
+ *
468
+ * @param {object} opts
469
+ * @param {string} [opts.gsdUpdate=''] - leading update/stale-hooks warning (already formatted)
470
+ * @param {string} opts.model - model display name (plain text; dim styling applied here)
471
+ * @param {string} [opts.ctx=''] - context-window meter segment (empty string = absent)
472
+ * @param {string|null} [opts.middle=null] - middle segment (todo task or GSD state), null = absent
473
+ * @param {string} opts.dirname - project directory basename (dim styling applied here)
474
+ * @param {string} [opts.lastCmdSuffix=''] - last-command suffix, e.g. ' │ last: /foo'
475
+ * @param {'end'|'front'} [opts.position='end']
476
+ * - 'end' (default): ctx appended after dirname — preserved byte-for-byte
477
+ * - 'front': ctx immediately after model name so the meter stays visible in narrow terminals
478
+ *
479
+ * Invalid position values are silently coerced to 'end' — config-set schema rejects
480
+ * invalid values upfront; runtime fallback defends against stale/corrupt configs
481
+ * without breaking the statusline.
482
+ */
483
+ function composeStatusline({
484
+ gsdUpdate = '',
485
+ model,
486
+ ctx = '',
487
+ middle = null,
488
+ dirname,
489
+ lastCmdSuffix = '',
490
+ position = 'end',
491
+ } = {}) {
492
+ const modelSeg = `\x1b[2m${model}\x1b[0m`;
493
+ const dirSeg = `\x1b[2m${dirname}\x1b[0m`;
494
+ // Coerce invalid values to 'end' (belt-and-suspenders; see JSDoc above)
495
+ const pos = position === 'front' ? 'front' : 'end';
496
+
497
+ if (pos === 'front') {
498
+ if (middle) return `${gsdUpdate}${modelSeg}${ctx} │ ${middle} │ ${dirSeg}${lastCmdSuffix}`;
499
+ return `${gsdUpdate}${modelSeg}${ctx} │ ${dirSeg}${lastCmdSuffix}`;
500
+ }
501
+ // 'end' — preserved byte-for-byte relative to original inline templates
502
+ if (middle) return `${gsdUpdate}${modelSeg} │ ${middle} │ ${dirSeg}${ctx}${lastCmdSuffix}`;
503
+ return `${gsdUpdate}${modelSeg} │ ${dirSeg}${ctx}${lastCmdSuffix}`;
504
+ }
505
+
506
+ function isInstalledAheadOfLatest(installed, latest) {
507
+ return isSemverNewer(installed, latest);
508
+ }
509
+
510
+ // Export helpers for unit tests. Harmless when run as a script.
511
+ module.exports = {
512
+ readGsdState, parseStateMd, formatGsdState,
513
+ readGsdConfig, getConfigValue, readLastSlashCommand,
514
+ composeStatusline,
515
+ isInstalledAheadOfLatest,
516
+ };
517
+
518
+ /**
519
+ * Render the statusline from an already-parsed hook input object. Exported for
520
+ * testing without feeding stdin. Returns the rendered string.
521
+ */
522
+ function renderStatusline(data) {
523
+ const model = data.model?.display_name || 'Claude';
524
+ const dir = data.workspace?.current_dir || process.cwd();
525
+ const dirname = path.basename(dir);
526
+
527
+ let lastCmdSuffix = '';
528
+ let position = 'end';
529
+ try {
530
+ const cfg = readGsdConfig(dir);
531
+ if (getConfigValue(cfg, 'statusline.show_last_command') === true) {
532
+ const lastCmd = readLastSlashCommand(data.transcript_path);
533
+ if (lastCmd) {
534
+ lastCmdSuffix = ` │ \x1b[2mlast: /${lastCmd}\x1b[0m`;
535
+ }
536
+ }
537
+ const cfgPos = getConfigValue(cfg, 'statusline.context_position');
538
+ if (cfgPos != null) position = cfgPos;
539
+ } catch (e) { /* swallow */ }
540
+
541
+ const gsdStateStr = formatGsdState(readGsdState(dir) || {});
542
+ const middle = gsdStateStr ? `\x1b[2m${gsdStateStr}\x1b[0m` : null;
543
+ return composeStatusline({ model, ctx: '', middle, dirname, lastCmdSuffix, position });
544
+ }
545
+
546
+ module.exports.renderStatusline = renderStatusline;
547
+
548
+ if (require.main === module) runStatusline();
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env node
2
+ // gsd-hook-version: {{GSD_VERSION}}
3
+ // SessionStart banner that surfaces GSD update availability when GSD's
4
+ // statusline isn't installed. Reads the cache that
5
+ // gsd-check-update-worker.js writes to ~/.cache/gsd/gsd-update-check.json.
6
+ //
7
+ // Opt-in by design: bin/install.js only registers this hook when the user
8
+ // declines to install (or replace) the GSD statusline. The presence of the
9
+ // SessionStart entry IS the opt-in — there is no separate runtime flag.
10
+ //
11
+ // See issue #2795 for the rationale.
12
+
13
+ 'use strict';
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+ const os = require('os');
18
+
19
+ // Suppress repeat parse-error banners for 24 hours so a genuinely broken
20
+ // cache file doesn't nag the user every session.
21
+ const RATE_LIMIT_SECONDS = 24 * 60 * 60;
22
+
23
+ /**
24
+ * Build the SessionStart JSON envelope to emit, given parsed cache state.
25
+ * Pure function — no I/O. Returns null when the hook should print nothing.
26
+ *
27
+ * @param {object} state
28
+ * @param {object|null} state.cache Parsed cache, or null if missing/unreadable.
29
+ * @param {boolean} state.parseError True iff cache file existed but JSON.parse failed.
30
+ * @param {boolean} state.suppressFailureWarning True when a recent failure warning already fired.
31
+ * @returns {{systemMessage: string}|null} JSON envelope, or null for silent exit.
32
+ */
33
+ function buildBannerOutput(state) {
34
+ const { cache, parseError, suppressFailureWarning } = state || {};
35
+ if (parseError) {
36
+ if (suppressFailureWarning) return null;
37
+ return { systemMessage: 'GSD update check failed.' };
38
+ }
39
+ if (!cache) return null;
40
+ if (!cache.update_available) return null;
41
+ const installed = cache.installed || 'unknown';
42
+ const latest = cache.latest || 'unknown';
43
+ return {
44
+ systemMessage: `GSD update available: ${installed} → ${latest}. Run /gsd:update.`,
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Read and parse the update-check cache file.
50
+ *
51
+ * @param {string} cacheFile
52
+ * @returns {{cache: object|null, parseError: boolean}}
53
+ */
54
+ function readCache(cacheFile) {
55
+ let cache = null;
56
+ let parseError = false;
57
+ try {
58
+ if (fs.existsSync(cacheFile)) {
59
+ const raw = fs.readFileSync(cacheFile, 'utf8');
60
+ cache = JSON.parse(raw);
61
+ }
62
+ } catch (e) {
63
+ // Distinguish "file unreadable" from "JSON malformed": both fail-open to
64
+ // null cache, but a JSON parse error becomes a one-time diagnostic.
65
+ parseError = e instanceof SyntaxError;
66
+ }
67
+ return { cache, parseError };
68
+ }
69
+
70
+ /**
71
+ * Has a failure warning been emitted within the rate-limit window?
72
+ *
73
+ * @param {string} sentinelFile
74
+ * @param {number} nowSeconds
75
+ * @returns {boolean}
76
+ */
77
+ function shouldSuppressFailureWarning(sentinelFile, nowSeconds) {
78
+ try {
79
+ if (!fs.existsSync(sentinelFile)) return false;
80
+ const last = parseInt(fs.readFileSync(sentinelFile, 'utf8').trim(), 10);
81
+ if (!Number.isFinite(last)) return false;
82
+ return nowSeconds - last < RATE_LIMIT_SECONDS;
83
+ } catch (e) {
84
+ return false;
85
+ }
86
+ }
87
+
88
+ function recordFailureWarning(sentinelFile, nowSeconds) {
89
+ try {
90
+ fs.writeFileSync(sentinelFile, String(nowSeconds));
91
+ } catch (e) {
92
+ // Best-effort: a non-writable cache dir means we'll re-warn next session,
93
+ // which is no worse than the un-instrumented baseline.
94
+ }
95
+ }
96
+
97
+ function main() {
98
+ const cacheDir = path.join(os.homedir(), '.cache', 'gsd');
99
+ const cacheFile = path.join(cacheDir, 'gsd-update-check.json');
100
+ const sentinelFile = path.join(cacheDir, 'banner-failure-warned-at');
101
+ const now = Math.floor(Date.now() / 1000);
102
+
103
+ const { cache, parseError } = readCache(cacheFile);
104
+ const suppressFailureWarning = parseError
105
+ ? shouldSuppressFailureWarning(sentinelFile, now)
106
+ : false;
107
+ const output = buildBannerOutput({ cache, parseError, suppressFailureWarning });
108
+
109
+ if (parseError && !suppressFailureWarning) {
110
+ // Ensure cache dir exists before writing the sentinel — first-run case
111
+ // where ~/.cache/gsd was created by check-update but the parent dir got
112
+ // wiped between runs.
113
+ try {
114
+ fs.mkdirSync(cacheDir, { recursive: true });
115
+ } catch (e) {
116
+ // Best-effort: failure to create the dir means we'll re-warn next
117
+ // session, which is no worse than the un-instrumented baseline.
118
+ }
119
+ recordFailureWarning(sentinelFile, now);
120
+ }
121
+
122
+ if (output) {
123
+ process.stdout.write(JSON.stringify(output));
124
+ }
125
+ }
126
+
127
+ if (require.main === module) main();
128
+
129
+ module.exports = {
130
+ buildBannerOutput,
131
+ readCache,
132
+ shouldSuppressFailureWarning,
133
+ RATE_LIMIT_SECONDS,
134
+ };