pi-gsd 2.0.23 → 2.1.0

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 (214) hide show
  1. package/dist/pi-gsd-hooks.js +5 -5
  2. package/dist/pi-gsd-tools.js +38 -38
  3. package/gsd/hooks/gsd-context-monitor.js +164 -0
  4. package/gsd/hooks/gsd-prompt-guard.js +99 -0
  5. package/gsd/hooks/gsd-workflow-guard.js +98 -0
  6. package/gsd/prompts/gsd-discuss-milestone.md +7 -0
  7. package/gsd/templates/milestone-context.md +231 -0
  8. package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/complete-milestone.md +7 -1
  9. package/gsd/workflows/discuss-milestone.md +467 -0
  10. package/package.json +4 -5
  11. package/scripts/postinstall.js +129 -332
  12. package/.gsd/harnesses/pi/get-shit-done/workflows/discuss-phase.md.bak +0 -1049
  13. package/.gsd/harnesses/pi/get-shit-done/workflows/execute-phase.md.bak +0 -846
  14. package/.gsd/harnesses/pi/get-shit-done/workflows/new-milestone.md.bak +0 -486
  15. package/.gsd/harnesses/pi/get-shit-done/workflows/new-project.md.bak +0 -1250
  16. package/.gsd/harnesses/pi/get-shit-done/workflows/plan-phase.md.bak +0 -859
  17. package/.gsd/harnesses/pi/gsd-file-manifest.json +0 -219
  18. package/.gsd/harnesses/pi/hooks/gsd-context-monitor.js +0 -159
  19. package/.gsd/harnesses/pi/hooks/gsd-prompt-guard.js +0 -99
  20. package/.gsd/harnesses/pi/hooks/gsd-workflow-guard.js +0 -97
  21. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/VERSION +0 -0
  22. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-advisor-researcher.md +0 -0
  23. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-assumptions-analyzer.md +0 -0
  24. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-codebase-mapper.md +0 -0
  25. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-debugger.md +0 -0
  26. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-executor.md +0 -0
  27. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-integration-checker.md +0 -0
  28. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-nyquist-auditor.md +0 -0
  29. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-phase-researcher.md +0 -0
  30. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-plan-checker.md +0 -0
  31. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-planner.md +0 -0
  32. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-project-researcher.md +0 -0
  33. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-research-synthesizer.md +0 -0
  34. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-roadmapper.md +0 -0
  35. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-ui-auditor.md +0 -0
  36. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-ui-checker.md +0 -0
  37. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-ui-researcher.md +0 -0
  38. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-user-profiler.md +0 -0
  39. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-verifier.md +0 -0
  40. /package/{.gsd/harnesses/pi → gsd}/hooks/gsd-check-update.js +0 -0
  41. /package/{.gsd/harnesses/pi → gsd}/hooks/gsd-statusline.js +0 -0
  42. /package/{prompts → gsd/prompts}/gsd-add-backlog.md +0 -0
  43. /package/{prompts → gsd/prompts}/gsd-add-phase.md +0 -0
  44. /package/{prompts → gsd/prompts}/gsd-add-tests.md +0 -0
  45. /package/{prompts → gsd/prompts}/gsd-add-todo.md +0 -0
  46. /package/{prompts → gsd/prompts}/gsd-audit-milestone.md +0 -0
  47. /package/{prompts → gsd/prompts}/gsd-audit-uat.md +0 -0
  48. /package/{prompts → gsd/prompts}/gsd-autonomous.md +0 -0
  49. /package/{prompts → gsd/prompts}/gsd-check-todos.md +0 -0
  50. /package/{prompts → gsd/prompts}/gsd-cleanup.md +0 -0
  51. /package/{prompts → gsd/prompts}/gsd-complete-milestone.md +0 -0
  52. /package/{prompts → gsd/prompts}/gsd-debug.md +0 -0
  53. /package/{prompts → gsd/prompts}/gsd-discuss-phase.md +0 -0
  54. /package/{prompts → gsd/prompts}/gsd-do.md +0 -0
  55. /package/{prompts → gsd/prompts}/gsd-execute-milestone.md +0 -0
  56. /package/{prompts → gsd/prompts}/gsd-execute-phase.md +0 -0
  57. /package/{prompts → gsd/prompts}/gsd-fast.md +0 -0
  58. /package/{prompts → gsd/prompts}/gsd-forensics.md +0 -0
  59. /package/{prompts → gsd/prompts}/gsd-insert-phase.md +0 -0
  60. /package/{prompts → gsd/prompts}/gsd-join-discord.md +0 -0
  61. /package/{prompts → gsd/prompts}/gsd-list-phase-assumptions.md +0 -0
  62. /package/{prompts → gsd/prompts}/gsd-list-workspaces.md +0 -0
  63. /package/{prompts → gsd/prompts}/gsd-manager.md +0 -0
  64. /package/{prompts → gsd/prompts}/gsd-map-codebase.md +0 -0
  65. /package/{prompts → gsd/prompts}/gsd-milestone-summary.md +0 -0
  66. /package/{prompts → gsd/prompts}/gsd-new-milestone.md +0 -0
  67. /package/{prompts → gsd/prompts}/gsd-new-project.md +0 -0
  68. /package/{prompts → gsd/prompts}/gsd-new-workspace.md +0 -0
  69. /package/{prompts → gsd/prompts}/gsd-note.md +0 -0
  70. /package/{prompts → gsd/prompts}/gsd-pause-work.md +0 -0
  71. /package/{prompts → gsd/prompts}/gsd-plan-milestone-gaps.md +0 -0
  72. /package/{prompts → gsd/prompts}/gsd-plan-milestone.md +0 -0
  73. /package/{prompts → gsd/prompts}/gsd-plan-phase.md +0 -0
  74. /package/{prompts → gsd/prompts}/gsd-plant-seed.md +0 -0
  75. /package/{prompts → gsd/prompts}/gsd-pr-branch.md +0 -0
  76. /package/{prompts → gsd/prompts}/gsd-profile-user.md +0 -0
  77. /package/{prompts → gsd/prompts}/gsd-quick.md +0 -0
  78. /package/{prompts → gsd/prompts}/gsd-reapply-patches.md +0 -0
  79. /package/{prompts → gsd/prompts}/gsd-remove-phase.md +0 -0
  80. /package/{prompts → gsd/prompts}/gsd-remove-workspace.md +0 -0
  81. /package/{prompts → gsd/prompts}/gsd-research-phase.md +0 -0
  82. /package/{prompts → gsd/prompts}/gsd-resume-work.md +0 -0
  83. /package/{prompts → gsd/prompts}/gsd-review-backlog.md +0 -0
  84. /package/{prompts → gsd/prompts}/gsd-review.md +0 -0
  85. /package/{prompts → gsd/prompts}/gsd-session-report.md +0 -0
  86. /package/{prompts → gsd/prompts}/gsd-set-profile.md +0 -0
  87. /package/{prompts → gsd/prompts}/gsd-settings.md +0 -0
  88. /package/{prompts → gsd/prompts}/gsd-ship.md +0 -0
  89. /package/{prompts → gsd/prompts}/gsd-thread.md +0 -0
  90. /package/{prompts → gsd/prompts}/gsd-ui-phase.md +0 -0
  91. /package/{prompts → gsd/prompts}/gsd-ui-review.md +0 -0
  92. /package/{prompts → gsd/prompts}/gsd-validate-phase.md +0 -0
  93. /package/{prompts → gsd/prompts}/gsd-verify-work.md +0 -0
  94. /package/{prompts → gsd/prompts}/gsd-workstreams.md +0 -0
  95. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/checkpoints.md +0 -0
  96. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/continuation-format.md +0 -0
  97. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/decimal-phase-calculation.md +0 -0
  98. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/git-integration.md +0 -0
  99. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/git-planning-commit.md +0 -0
  100. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/model-profile-resolution.md +0 -0
  101. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/model-profiles.md +0 -0
  102. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/phase-argument-parsing.md +0 -0
  103. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/planning-config.md +0 -0
  104. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/questioning.md +0 -0
  105. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/tdd.md +0 -0
  106. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/ui-brand.md +0 -0
  107. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/user-profiling.md +0 -0
  108. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/verification-patterns.md +0 -0
  109. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/workstream-flag.md +0 -0
  110. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/DEBUG.md +0 -0
  111. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/UAT.md +0 -0
  112. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/UI-SPEC.md +0 -0
  113. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/VALIDATION.md +0 -0
  114. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/claude-md.md +0 -0
  115. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/codebase/architecture.md +0 -0
  116. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/codebase/concerns.md +0 -0
  117. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/codebase/conventions.md +0 -0
  118. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/codebase/integrations.md +0 -0
  119. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/codebase/stack.md +0 -0
  120. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/codebase/structure.md +0 -0
  121. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/codebase/testing.md +0 -0
  122. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/config.json +0 -0
  123. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/context.md +0 -0
  124. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/continue-here.md +0 -0
  125. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/copilot-instructions.md +0 -0
  126. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/debug-subagent-prompt.md +0 -0
  127. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/dev-preferences.md +0 -0
  128. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/discovery.md +0 -0
  129. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/discussion-log.md +0 -0
  130. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/milestone-archive.md +0 -0
  131. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/milestone.md +0 -0
  132. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/phase-prompt.md +0 -0
  133. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/planner-subagent-prompt.md +0 -0
  134. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/project.md +0 -0
  135. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/requirements.md +0 -0
  136. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/research-project/ARCHITECTURE.md +0 -0
  137. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/research-project/FEATURES.md +0 -0
  138. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/research-project/PITFALLS.md +0 -0
  139. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/research-project/STACK.md +0 -0
  140. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/research-project/SUMMARY.md +0 -0
  141. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/research.md +0 -0
  142. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/retrospective.md +0 -0
  143. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/roadmap.md +0 -0
  144. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/state.md +0 -0
  145. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/summary-complex.md +0 -0
  146. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/summary-minimal.md +0 -0
  147. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/summary-standard.md +0 -0
  148. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/summary.md +0 -0
  149. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/user-profile.md +0 -0
  150. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/user-setup.md +0 -0
  151. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/verification-report.md +0 -0
  152. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/add-backlog.md +0 -0
  153. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/add-phase.md +0 -0
  154. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/add-tests.md +0 -0
  155. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/add-todo.md +0 -0
  156. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/audit-milestone.md +0 -0
  157. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/audit-uat.md +0 -0
  158. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/autonomous.md +0 -0
  159. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/check-todos.md +0 -0
  160. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/cleanup.md +0 -0
  161. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/debug.md +0 -0
  162. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/diagnose-issues.md +0 -0
  163. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/discovery-phase.md +0 -0
  164. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/discuss-phase-assumptions.md +0 -0
  165. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/discuss-phase.md +0 -0
  166. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/do.md +0 -0
  167. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/execute-milestone.md +0 -0
  168. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/execute-phase.md +0 -0
  169. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/execute-plan.md +0 -0
  170. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/fast.md +0 -0
  171. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/forensics.md +0 -0
  172. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/health.md +0 -0
  173. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/help.md +0 -0
  174. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/insert-phase.md +0 -0
  175. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/list-phase-assumptions.md +0 -0
  176. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/list-workspaces.md +0 -0
  177. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/manager.md +0 -0
  178. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/map-codebase.md +0 -0
  179. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/milestone-summary.md +0 -0
  180. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/new-milestone.md +0 -0
  181. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/new-project.md +0 -0
  182. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/new-workspace.md +0 -0
  183. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/next.md +0 -0
  184. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/node-repair.md +0 -0
  185. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/note.md +0 -0
  186. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/pause-work.md +0 -0
  187. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/plan-milestone-gaps.md +0 -0
  188. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/plan-milestone.md +0 -0
  189. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/plan-phase.md +0 -0
  190. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/plant-seed.md +0 -0
  191. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/pr-branch.md +0 -0
  192. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/profile-user.md +0 -0
  193. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/progress.md +0 -0
  194. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/quick.md +0 -0
  195. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/remove-phase.md +0 -0
  196. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/remove-workspace.md +0 -0
  197. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/research-phase.md +0 -0
  198. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/resume-project.md +0 -0
  199. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/review-backlog.md +0 -0
  200. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/review.md +0 -0
  201. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/session-report.md +0 -0
  202. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/set-profile.md +0 -0
  203. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/settings.md +0 -0
  204. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/ship.md +0 -0
  205. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/stats.md +0 -0
  206. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/thread.md +0 -0
  207. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/transition.md +0 -0
  208. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/ui-phase.md +0 -0
  209. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/ui-review.md +0 -0
  210. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/update.md +0 -0
  211. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/validate-phase.md +0 -0
  212. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/verify-phase.md +0 -0
  213. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/verify-work.md +0 -0
  214. /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/workstreams.md +0 -0
@@ -2,15 +2,24 @@
2
2
  /**
3
3
  * postinstall.js - GSD harness installer
4
4
  *
5
- * Runs automatically after `npm install pi-gsd`.
6
- * Copies the pi harness from this package's
7
- * \`.gsd/harnesses/pi/\` into the consumer project's \`.pi/gsd/\`
8
- * and installs the \`pi-gsd-hooks.ts\` extension into \`.pi/extensions/\`.
5
+ * Runs automatically after `npm install pi-gsd` (or `pi install npm:pi-gsd`).
6
+ * Copies runtime harness content from this package's `gsd/` into the consumer
7
+ * project's `.pi/gsd/`, and hook scripts into `.pi/gsd/hooks/`.
9
8
  *
10
- * Safe to re-run - files are skipped if already present (unless GSD_FORCE=1).
9
+ * Version detection: each installed .md file carries a `<gsd-version v="X.Y.Z" />`
10
+ * tag stamped with the npm package version that wrote it. On reinstall we read
11
+ * that tag from a sample file; if it doesn't match the current package version we
12
+ * overwrite everything so workflow/template fixes always reach installed projects.
13
+ * Files tagged `do-not-update` are never overwritten (user customisations).
14
+ *
15
+ * Excluded from the `.pi/gsd/` copy:
16
+ * gsd/prompts/ → served from npm package via pi.prompts (not installed locally)
17
+ * gsd/hooks/ → copied to .pi/gsd/hooks/ instead
11
18
  */
12
19
 
13
- const fs = require("fs");
20
+ "use strict";
21
+
22
+ const fs = require("fs");
14
23
  const path = require("path");
15
24
 
16
25
  // ─── Constants ────────────────────────────────────────────────────────────────
@@ -19,274 +28,190 @@ const FORCE =
19
28
  process.env.GSD_FORCE_REINSTALL === "1" ||
20
29
  process.argv.includes("--force-reinstall");
21
30
 
22
- /**
23
- * Directory that contains this package's files.
24
- * When executed via npm postinstall, __dirname is the package root.
25
- */
31
+ /** Directory that contains this package's files. */
26
32
  const PKG_DIR = path.resolve(__dirname, "..");
27
33
 
28
34
  /**
29
35
  * The consuming project's root.
30
36
  * npm sets INIT_CWD to the directory where `npm install` was run.
31
- * Fall back to process.cwd() for programmatic / npx usage.
32
37
  */
33
38
  const PROJECT_ROOT = process.env.INIT_CWD || process.cwd();
34
39
 
35
- /**
36
- * Harness definitions.
37
- *
38
- * Each entry maps:
39
- * src - subdirectory under <package>/.gsd/harnesses/
40
- * dest - directory in the consumer project root
41
- * hooks - whether this platform supports GSD hooks (copied from .gsd/hooks/)
42
- */
43
- const HARNESSES = [{ src: "pi", dest: ".pi", hooks: true, subdir: "gsd" }];
40
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
41
+
42
+ function getPackageVersion() {
43
+ try {
44
+ return JSON.parse(fs.readFileSync(path.join(PKG_DIR, "package.json"), "utf8")).version || "0.0.0";
45
+ } catch { return "0.0.0"; }
46
+ }
44
47
 
45
48
  /**
46
- * Subdirectory name used inside each harness's dest folder for
47
- * GSD-specific content (workflows, bin, references, templates …).
49
+ * Read the `<gsd-version v="..." />` tag from a file.
50
+ * Returns { version, doNotUpdate } or null.
48
51
  */
49
- // subdir is now per-harness (see harness config above)
50
-
51
- // ─── Helpers ──────────────────────────────────────────────────────────────────
52
+ function readGsdVersion(filePath) {
53
+ try {
54
+ const head = fs.readFileSync(filePath, "utf8").slice(0, 256);
55
+ const m = /<gsd-version\s+v="([^"]+)"(\s+do-not-update)?\s*\/>/.exec(head);
56
+ return m ? { version: m[1], doNotUpdate: Boolean(m[2]) } : null;
57
+ } catch { return null; }
58
+ }
52
59
 
53
60
  /**
54
- * Recursively copy a directory tree from `src` to `dest`.
55
- * If `overwrite` is false (default), existing files are left untouched.
56
- *
57
- * @param {string} src Absolute source path
58
- * @param {string} dest Absolute destination path
59
- * @param {boolean} overwrite Replace existing files when true
60
- * @returns {{ copied: number, skipped: number }}
61
+ * Recursively copy src dest, stamping each .md file with `pkgVersion`
62
+ * in its `<gsd-version>` tag (unless `do-not-update` is set).
63
+ * Skips top-level subdirs in `exclude`. Skips existing files when !overwrite
64
+ * (except: always updates if installed version tag differs from pkgVersion).
61
65
  */
62
- function copyDir(src, dest, overwrite) {
63
- let copied = 0;
64
- let skipped = 0;
65
-
66
+ function copyDir(src, dest, overwrite, exclude, pkgVersion) {
67
+ let copied = 0, skipped = 0;
66
68
  if (!fs.existsSync(src)) return { copied, skipped };
67
-
68
69
  fs.mkdirSync(dest, { recursive: true });
69
70
 
70
71
  for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
71
- const srcEntry = path.join(src, entry.name);
72
+ if (exclude && exclude.includes(entry.name)) continue;
73
+ const srcEntry = path.join(src, entry.name);
72
74
  const destEntry = path.join(dest, entry.name);
73
75
 
74
76
  if (entry.isDirectory()) {
75
- const sub = copyDir(srcEntry, destEntry, overwrite);
76
- copied += sub.copied;
77
+ const sub = copyDir(srcEntry, destEntry, overwrite, null, pkgVersion);
78
+ copied += sub.copied;
77
79
  skipped += sub.skipped;
78
80
  } else if (entry.isFile()) {
81
+ const isMd = entry.name.endsWith(".md");
82
+
79
83
  if (!overwrite && fs.existsSync(destEntry)) {
80
- skipped++;
84
+ // Even in no-overwrite mode, update .md files whose version tag
85
+ // doesn't match the current package (the whole point of this system).
86
+ if (isMd) {
87
+ const installed = readGsdVersion(destEntry);
88
+ if (!installed || installed.doNotUpdate || installed.version === pkgVersion) {
89
+ skipped++;
90
+ continue;
91
+ }
92
+ // Fall through — version mismatch, needs update
93
+ } else {
94
+ skipped++;
95
+ continue;
96
+ }
97
+ }
98
+
99
+ if (isMd) {
100
+ // Stamp with current package version
101
+ let content = fs.readFileSync(srcEntry, "utf8");
102
+ content = content.replace(
103
+ /<gsd-version\s+v="[^"]*"(\s+do-not-update)?\s*\/>/,
104
+ (_, dnu) => dnu ? `<gsd-version v="${pkgVersion}" do-not-update />` : `<gsd-version v="${pkgVersion}" />`,
105
+ );
106
+ fs.writeFileSync(destEntry, content, "utf8");
81
107
  } else {
82
108
  fs.copyFileSync(srcEntry, destEntry);
83
- copied++;
84
109
  }
110
+ copied++;
85
111
  }
86
112
  }
87
-
88
113
  return { copied, skipped };
89
114
  }
90
115
 
91
- /**
92
- * Emit a coloured status line to stdout.
93
- * Colours are stripped when stdout is not a TTY (CI / pipe).
94
- *
95
- * @param {'ok'|'skip'|'warn'|'err'} level
96
- * @param {string} msg
97
- */
98
116
  function log(level, msg) {
99
117
  const isTTY = process.stdout.isTTY;
100
- const colours = {
101
- ok: isTTY ? "\x1b[32m✓\x1b[0m" : "✓",
102
- skip: isTTY ? "\x1b[33m–\x1b[0m" : "–",
103
- warn: isTTY ? "\x1b[33m⚠\x1b[0m" : "⚠",
104
- err: isTTY ? "\x1b[31m✗\x1b[0m" : "✗",
105
- };
106
- console.log(` ${colours[level] || " "} ${msg}`);
118
+ const sym = { ok: isTTY ? "\x1b[32m✓\x1b[0m" : "✓", skip: isTTY ? "\x1b[33m–\x1b[0m" : "–", warn: isTTY ? "\x1b[33m⚠\x1b[0m" : "⚠" };
119
+ console.log(` ${sym[level] || " "} ${msg}`);
107
120
  }
108
121
 
109
122
  // ─── Main ─────────────────────────────────────────────────────────────────────
110
123
 
111
124
  function main() {
112
- // Skip when running inside the package's own development tree
113
- // (i.e. when INIT_CWD === the package directory itself).
114
- if (path.resolve(PROJECT_ROOT) === path.resolve(PKG_DIR)) {
115
- log(
116
- "skip",
117
- "Running inside package source tree - skipping harness install.",
118
- );
119
- return;
120
- }
121
-
122
- // Skip when explicitly opted out
123
125
  if (process.env.GSD_SKIP_INSTALL === "1") {
124
126
  log("skip", "GSD_SKIP_INSTALL=1 - skipping harness install.");
125
127
  return;
126
128
  }
127
129
 
128
- const harnessesRoot = path.join(PKG_DIR, ".gsd", "harnesses");
129
- const hooksRoot = path.join(PKG_DIR, ".gsd", "hooks");
130
+ const pkgVersion = getPackageVersion();
131
+ const gsdSrc = path.join(PKG_DIR, "gsd");
132
+ const gsdDest = path.join(PROJECT_ROOT, ".pi", "gsd");
133
+ const hooksSrc = path.join(gsdSrc, "hooks");
134
+ const hooksDest = path.join(PROJECT_ROOT, ".pi", "gsd", "hooks");
130
135
 
131
- console.log("");
132
- console.log(" GSD - installing harness files into your project…");
133
- if (FORCE)
134
- console.log(" (force-reinstall mode: existing files will be overwritten)");
135
- console.log("");
136
-
137
- let totalCopied = 0;
138
- let totalSkipped = 0;
139
- let installed = 0;
140
-
141
- for (const harness of HARNESSES) {
142
- const srcHarness = path.join(harnessesRoot, harness.src);
143
- const destHarness = path.join(PROJECT_ROOT, harness.dest);
136
+ // Detect installed version from a sample file's <gsd-version> tag.
137
+ const sample = path.join(gsdDest, "workflows", "plan-phase.md");
138
+ const installedTag = readGsdVersion(sample);
139
+ const installedVersion = installedTag?.version ?? null;
140
+ const versionChanged = installedVersion !== pkgVersion;
144
141
 
145
- // ── get-shit-done/ content ──────────────────────────────────────────────
146
- const srcGsd = path.join(srcHarness, harness.subdir);
147
- const destGsd = path.join(destHarness, harness.subdir);
142
+ // In no-force mode, copyDir still updates individual files whose version
143
+ // tag doesn't match — so we only need to force-overwrite non-.md files
144
+ // (config.json, VERSION, hooks) when the version changes.
145
+ const overwriteAll = FORCE || versionChanged;
148
146
 
149
- if (!fs.existsSync(srcHarness)) {
150
- log(
151
- "skip",
152
- `${harness.dest}/${harness.subdir} (source absent - skipped)`,
153
- );
154
- continue;
155
- }
156
-
157
- const { copied, skipped } = copyDir(srcGsd, destGsd, FORCE);
158
- totalCopied += copied;
159
- totalSkipped += skipped;
147
+ console.log("");
148
+ console.log(` GSD v${pkgVersion} - installing into project…`);
149
+ if (versionChanged && installedVersion) {
150
+ console.log(` (updating from v${installedVersion} → v${pkgVersion})`);
151
+ } else if (FORCE) {
152
+ console.log(" (force-reinstall: existing files will be overwritten)");
153
+ }
154
+ console.log("");
160
155
 
161
- if (copied > 0 || skipped === 0) {
162
- log(
163
- "ok",
164
- `${harness.dest}/${harness.subdir} (${copied} file${copied === 1 ? "" : "s"} installed)`,
165
- );
166
- } else {
167
- log(
168
- "skip",
169
- `${harness.dest}/${harness.subdir} (already up-to-date, ${skipped} file${skipped === 1 ? "" : "s"} skipped)`,
170
- );
171
- }
156
+ // ── Runtime harness .pi/gsd/ ────────────────────────────────────────────
157
+ const { copied: gsdCopied, skipped: gsdSkipped } = copyDir(
158
+ gsdSrc, gsdDest, overwriteAll, ["prompts", "hooks"], pkgVersion,
159
+ );
172
160
 
173
- // ── gsd-file-manifest.json ──────────────────────────────────────────────
174
- const manifestSrc = path.join(srcHarness, "gsd-file-manifest.json");
175
- const manifestDest = path.join(destHarness, "gsd-file-manifest.json");
161
+ if (gsdCopied > 0) {
162
+ log("ok", `.pi/gsd (${gsdCopied} file${gsdCopied === 1 ? "" : "s"} updated to v${pkgVersion})`);
163
+ } else {
164
+ log("skip", `.pi/gsd (v${pkgVersion} already installed, ${gsdSkipped} files skipped)`);
165
+ }
176
166
 
177
- if (fs.existsSync(manifestSrc)) {
178
- if (!FORCE && fs.existsSync(manifestDest)) {
179
- totalSkipped++;
180
- } else {
181
- fs.mkdirSync(destHarness, { recursive: true });
182
- fs.copyFileSync(manifestSrc, manifestDest);
183
- totalCopied++;
184
- }
167
+ // ── Hook scripts → .pi/gsd/hooks/ ──────────────────────────────────────────
168
+ if (fs.existsSync(hooksSrc)) {
169
+ const { copied: hCopied } = copyDir(hooksSrc, hooksDest, overwriteAll, null, pkgVersion);
170
+ if (hCopied > 0) {
171
+ log("ok", `.pi/gsd/hooks (${hCopied} hook${hCopied === 1 ? "" : "s"} installed)`);
185
172
  }
173
+ }
186
174
 
187
- // ── hooks/ (platform-selective) ─────────────────────────────────────────
188
- if (harness.hooks && fs.existsSync(hooksRoot)) {
189
- const destHooks = path.join(destHarness, "hooks");
190
- const h = copyDir(hooksRoot, destHooks, FORCE);
191
- totalCopied += h.copied;
192
- totalSkipped += h.skipped;
193
-
194
- if (h.copied > 0) {
195
- log(
196
- "ok",
197
- `${harness.dest}/hooks (${h.copied} hook${h.copied === 1 ? "" : "s"} installed)`,
198
- );
199
- }
200
- }
201
-
202
- // ── skills/ (opencode only - present in .gsd/harnesses/opencode/skills) ─
203
- const srcSkills = path.join(srcHarness, "skills");
204
- const destSkills = path.join(destHarness, "skills");
205
-
206
- if (fs.existsSync(srcSkills)) {
207
- const s = copyDir(srcSkills, destSkills, FORCE);
208
- totalCopied += s.copied;
209
- totalSkipped += s.skipped;
210
-
211
- if (s.copied > 0) {
212
- log(
213
- "ok",
214
- `${harness.dest}/skills (${s.copied} skill file${s.copied === 1 ? "" : "s"} installed)`,
215
- );
216
- }
175
+ // ── Cleanup: stale .pi/hooks/ from old layout (pi ≥0.35 renames this dir) ──
176
+ const oldHooksDir = path.join(PROJECT_ROOT, ".pi", "hooks");
177
+ if (fs.existsSync(oldHooksDir)) {
178
+ const oldEntries = fs.readdirSync(oldHooksDir);
179
+ const allGsd = oldEntries.every((f) => f.startsWith("gsd-") && f.endsWith(".js"));
180
+ if (allGsd) {
181
+ for (const f of oldEntries) fs.rmSync(path.join(oldHooksDir, f));
182
+ fs.rmdirSync(oldHooksDir);
183
+ log("ok", ".pi/hooks (removed - hooks moved to .pi/gsd/hooks/)");
217
184
  }
218
-
219
- installed++;
220
185
  }
221
186
 
222
- // ── Pi extension - served from npm package via pi.extensions in package.json ─────
223
- // No local copying needed. Cleanup stale files from old install approach.
187
+ // ── Cleanup: stale files from old install layouts ──────────────────────────
224
188
  const extDir = path.join(PROJECT_ROOT, ".pi", "extensions");
225
- const staleExts = ["gsd-hooks.ts", "pi-gsd-hooks.ts"];
226
- for (const name of staleExts) {
189
+ for (const name of ["gsd-hooks.ts", "pi-gsd-hooks.ts"]) {
227
190
  const stale = path.join(extDir, name);
228
191
  if (fs.existsSync(stale)) {
229
192
  fs.rmSync(stale);
230
193
  log("ok", `.pi/extensions/${name} (removed - extension now served from package)`);
231
194
  }
232
195
  }
233
- const settingsFile2 = path.join(PROJECT_ROOT, ".pi", "settings.json");
234
- if (fs.existsSync(settingsFile2)) {
196
+ const settingsFile = path.join(PROJECT_ROOT, ".pi", "settings.json");
197
+ if (fs.existsSync(settingsFile)) {
235
198
  try {
236
- const settings2 = JSON.parse(fs.readFileSync(settingsFile2, "utf8"));
237
- if (Array.isArray(settings2.extensions)) {
238
- const cleaned2 = settings2.extensions.filter((e) => !staleExts.some((n) => e.endsWith(n)));
239
- if (cleaned2.length !== settings2.extensions.length) {
240
- settings2.extensions = cleaned2;
241
- fs.writeFileSync(settingsFile2, JSON.stringify(settings2, null, "\t"), "utf8");
199
+ const settings = JSON.parse(fs.readFileSync(settingsFile, "utf8"));
200
+ if (Array.isArray(settings.extensions)) {
201
+ const staleNames = ["gsd-hooks.ts", "pi-gsd-hooks.ts"];
202
+ const cleaned = settings.extensions.filter((e) => !staleNames.some((n) => e.endsWith(n)));
203
+ if (cleaned.length !== settings.extensions.length) {
204
+ settings.extensions = cleaned;
205
+ fs.writeFileSync(settingsFile, JSON.stringify(settings, null, "\t"), "utf8");
242
206
  log("ok", ".pi/settings.json (removed stale extension entries)");
243
207
  }
244
208
  }
245
209
  } catch { /* ignore */ }
246
210
  }
247
- // ── Pi prompt templates - cleanup stale local copies ───────────────────────
248
- // Prompts are served directly from the npm package (user scope).
249
- // Local copies in .pi/prompts/ cause collision warnings on every pi update.
250
- // Remove any gsd-*.md files previously installed there.
251
- // Also remove the old gsd-hooks.ts if present (renamed to pi-gsd-hooks.ts).
252
- const promptsDest = path.join(PROJECT_ROOT, ".pi", "prompts");
253
- if (fs.existsSync(promptsDest)) {
254
- const stale = fs
255
- .readdirSync(promptsDest)
256
- .filter((f) => f.startsWith("gsd-") && f.endsWith(".md"));
257
- if (stale.length > 0) {
258
- for (const f of stale) fs.rmSync(path.join(promptsDest, f));
259
- log(
260
- "ok",
261
- `.pi/prompts (removed ${stale.length} stale local gsd-*.md - served from package instead)`,
262
- );
263
- }
264
- }
265
- const oldExt = path.join(PROJECT_ROOT, ".pi", "extensions", "gsd-hooks.ts");
266
- if (fs.existsSync(oldExt)) {
267
- fs.rmSync(oldExt);
268
- log(
269
- "ok",
270
- ".pi/extensions/gsd-hooks.ts (removed - renamed to pi-gsd-hooks.ts)",
271
- );
272
- }
273
211
 
274
212
  console.log("");
275
-
276
- if (installed === 0) {
277
- log("warn", "No harness source directories found inside the package.");
278
- log(
279
- "warn",
280
- "The package may be incomplete. Try: npm install --force get-shit-done-cc",
281
- );
282
- console.log("");
283
- return;
284
- }
285
-
286
- console.log(` GSD v${getPackageVersion()} installed successfully.`);
287
- console.log(
288
- ` ${totalCopied} file${totalCopied === 1 ? "" : "s"} copied, ${totalSkipped} skipped.`,
289
- );
213
+ console.log(` GSD v${pkgVersion} installed successfully.`);
214
+ console.log(` ${gsdCopied} file${gsdCopied === 1 ? "" : "s"} written.`);
290
215
  console.log("");
291
216
  console.log(" Next steps:");
292
217
  console.log(" Run /gsd-new-project to initialise a project.");
@@ -295,132 +220,4 @@ function main() {
295
220
  console.log("");
296
221
  }
297
222
 
298
- /**
299
- * Read the version from this package's own package.json.
300
- * Gracefully returns 'unknown' if the file is unreadable.
301
- *
302
- * @returns {string}
303
- */
304
- function getPackageVersion() {
305
- try {
306
- const pkg = JSON.parse(
307
- fs.readFileSync(path.join(PKG_DIR, "package.json"), "utf8"),
308
- );
309
- return pkg.version || "unknown";
310
- } catch {
311
- return "unknown";
312
- }
313
- }
314
-
315
- /**
316
- * Install the GSD pi extension into the consumer project's .pi/extensions/ directory.
317
- * Also updates .pi/settings.json to include the extension in the extensions array.
318
- *
319
- * The extension registers three non-blocking pi lifecycle hooks:
320
- * session_start → background GSD update check
321
- * tool_call → workflow guard advisory (write/edit outside GSD context)
322
- * tool_result → context usage monitor
323
- *
324
- * @param {string} projectRoot Consumer project root
325
- * @param {string} pkgDir This package's root directory
326
- * @param {boolean} force Overwrite existing files
327
- * @param {function} callback Called with (copied: boolean)
328
- */
329
- function installPiExtension(projectRoot, pkgDir, force, callback) {
330
- const piDir = path.join(projectRoot, ".pi");
331
- const extDir = path.join(piDir, "extensions");
332
- const extDest = path.join(extDir, "pi-gsd-hooks.ts");
333
- const extSrc = path.join(pkgDir, ".gsd", "extensions", "pi-gsd-hooks.ts");
334
-
335
- if (!fs.existsSync(extSrc)) {
336
- log("warn", ".pi/extensions/pi-gsd-hooks.ts (source absent - skipped)");
337
- callback(false);
338
- return;
339
- }
340
-
341
- // Always update the extension - it is owned by pi-gsd, not the user.
342
- // Compare pi-gsd-extension-version comments to detect staleness.
343
- const extractVersion = (file) => {
344
- try {
345
- const match = fs
346
- .readFileSync(file, "utf8")
347
- .match(/pi-gsd-extension-version:\s*([\d.]+)/);
348
- return match ? match[1] : null;
349
- } catch {
350
- return null;
351
- }
352
- };
353
- const srcVersion = extractVersion(extSrc);
354
- const destVersion = fs.existsSync(extDest) ? extractVersion(extDest) : null;
355
- const needsUpdate = force || !destVersion || destVersion !== srcVersion;
356
-
357
- if (!needsUpdate) {
358
- log("skip", `.pi/extensions/pi-gsd-hooks.ts (up-to-date v${destVersion})`);
359
- callback(false);
360
- } else {
361
- try {
362
- fs.mkdirSync(extDir, { recursive: true });
363
- fs.copyFileSync(extSrc, extDest);
364
- log(
365
- "ok",
366
- ".pi/extensions/pi-gsd-hooks.ts (GSD lifecycle extension installed)",
367
- );
368
- callback(true);
369
- } catch (e) {
370
- log(
371
- "warn",
372
- ".pi/extensions/pi-gsd-hooks.ts (install failed: " + e.message + ")",
373
- );
374
- callback(false);
375
- return;
376
- }
377
- }
378
-
379
- // Update .pi/settings.json to include the extension path in the extensions array.
380
- // The file is already auto-discovered from .pi/extensions/, but explicit registration
381
- // is added as a belt-and-suspenders measure.
382
- const settingsFile = path.join(piDir, "settings.json");
383
- try {
384
- let settings = {};
385
- if (fs.existsSync(settingsFile)) {
386
- try {
387
- settings = JSON.parse(fs.readFileSync(settingsFile, "utf8"));
388
- } catch {
389
- // Unreadable settings - start fresh object
390
- }
391
- }
392
-
393
- const extensions = Array.isArray(settings.extensions)
394
- ? settings.extensions
395
- : [];
396
-
397
- // Remove stale gsd-hooks.ts entry if present (renamed to pi-gsd-hooks.ts)
398
- const oldExtPath = path.join(extDir, "gsd-hooks.ts");
399
- const cleaned = extensions.filter((e) => e !== oldExtPath);
400
-
401
- // Avoid duplicate entries
402
- if (!cleaned.includes(extDest)) {
403
- settings.extensions = [...cleaned, extDest];
404
- fs.mkdirSync(piDir, { recursive: true });
405
- fs.writeFileSync(
406
- settingsFile,
407
- JSON.stringify(settings, null, "\t"),
408
- "utf8",
409
- );
410
- log("ok", ".pi/settings.json (extensions array updated)");
411
- } else if (cleaned.length !== extensions.length) {
412
- // Removed stale entry but extDest already present
413
- settings.extensions = cleaned;
414
- fs.writeFileSync(
415
- settingsFile,
416
- JSON.stringify(settings, null, "\t"),
417
- "utf8",
418
- );
419
- log("ok", ".pi/settings.json (removed stale gsd-hooks.ts entry)");
420
- }
421
- } catch (e) {
422
- log("warn", ".pi/settings.json (could not update: " + e.message + ")");
423
- }
424
- }
425
-
426
223
  main();