savepoint 1.0.1 → 1.0.3

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 (278) hide show
  1. package/.claude/settings.local.json +15 -1
  2. package/.golangci.yml +11 -0
  3. package/.savepoint/Design.md +52 -46
  4. package/.savepoint/releases/v1/epics/E01-go-setup/tasks/T001-init-module.md +1 -1
  5. package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T005-layout.md +1 -1
  6. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T002-card.md +1 -1
  7. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T006-help-overlay.md +1 -1
  8. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/{Design.md → E06-Detail.md} +5 -3
  9. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T002-header-and-dividers.md +1 -1
  10. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T003-footer-status-bar.md +1 -1
  11. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T004-component-refinement.md +1 -1
  12. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T010-auto-refresh-watcher.md +2 -0
  13. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/{Design.md → E01-Detail.md} +9 -1
  14. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/{T007-next-activity-header.md → T001-next-activity-header.md} +13 -12
  15. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T002-rename-epic-design-files.md +9 -9
  16. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T003-rename-release-prd.md +2 -2
  17. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T004-update-instruction-files.md +13 -12
  18. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T005-update-cross-references.md +14 -13
  19. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T006-column-and-detail-scrolling.md +25 -15
  20. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T007-column-focus-border-stability.md +57 -0
  21. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/E02-Audit.md +124 -0
  22. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/{Design.md → E02-Detail.md} +12 -3
  23. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T001-fix-makefile.md +11 -8
  24. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T002-linux-build-target.md +12 -7
  25. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T003-macos-build-target.md +9 -5
  26. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T004-smoke-tests-and-artifacts.md +30 -9
  27. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/E03-Audit.md +195 -0
  28. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/E03-Detail.md +45 -0
  29. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T001-border-resize-fix.md +40 -0
  30. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T002-next-activity-below-header.md +64 -0
  31. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T003-checkbox-rendering-fix.md +56 -0
  32. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T005-unify-status-glyphs.md +65 -0
  33. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T006-forced-256-color-profile.md +36 -0
  34. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/E04-Audit.md +167 -0
  35. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/E04-Detail.md +51 -0
  36. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/tasks/T001-sidebar-focusable-navigation.md +65 -0
  37. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/tasks/T002-epic-detail-overlay.md +73 -0
  38. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/tasks/T003-epic-status-glyphs.md +73 -0
  39. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/E05-Audit.md +237 -0
  40. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/E05-Detail.md +54 -0
  41. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T001-update-agents-md.md +45 -0
  42. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T002-update-router-md.md +40 -0
  43. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T003-update-design-md.md +47 -0
  44. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T004-implement-m-hotkey.md +98 -0
  45. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T005-update-help-overlay.md +33 -0
  46. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T006-tests-and-quality-gates.md +62 -0
  47. package/.savepoint/releases/v1.1/epics/E06-audit-command/E06-Audit.md +56 -0
  48. package/.savepoint/releases/v1.1/epics/E06-audit-command/E06-Detail.md +63 -0
  49. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T005-proposals.md +44 -0
  50. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T007-apply-close.md +35 -0
  51. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T009-integration.md +40 -0
  52. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T010-audit-file-migration.md +45 -0
  53. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T011-model-tab-state.md +26 -0
  54. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T012-epic-audit-render.md +33 -0
  55. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T013-handle-tab-keys.md +34 -0
  56. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T014-tab-indicator.md +33 -0
  57. package/.savepoint/releases/v1.1/epics/E07-init-command/E07-Audit.md +336 -0
  58. package/.savepoint/releases/v1.1/epics/E07-init-command/E07-Detail.md +61 -0
  59. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T001-cli-entrypoint.md +37 -0
  60. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T002-target-validation.md +28 -0
  61. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T003-scaffold-writer.md +46 -0
  62. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T004-atomic-writes.md +27 -0
  63. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T005-magic-prompt.md +25 -0
  64. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T006-clipboard.md +26 -0
  65. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T007-integration-test.md +26 -0
  66. package/.savepoint/releases/v1.1/epics/E08-board-command/E08-Audit.md +333 -0
  67. package/.savepoint/releases/v1.1/epics/E08-board-command/E08-Detail.md +68 -0
  68. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T001-cli-entrypoint.md +26 -0
  69. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T002-non-tty-fallback.md +27 -0
  70. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T003-tui-app-shell.md +28 -0
  71. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T004-board-model.md +29 -0
  72. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T005-detail-pane.md +27 -0
  73. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T006-status-transitions.md +29 -0
  74. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T007-theme-fallbacks.md +29 -0
  75. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T008-integration-test.md +27 -0
  76. package/.savepoint/releases/v1.1/epics/E09-doctor-command/E09-Audit.md +207 -0
  77. package/.savepoint/releases/v1.1/epics/E09-doctor-command/E09-Detail.md +65 -0
  78. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T001-cli-entrypoint.md +24 -0
  79. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T002-config-router-validation.md +28 -0
  80. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T003-structure-checks.md +29 -0
  81. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T004-dependency-checks.md +27 -0
  82. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T005-audit-orphan-checks.md +28 -0
  83. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T006-quality-gates-report.md +31 -0
  84. package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/E11-Detail.md +36 -0
  85. package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T001-debug-logging.md +25 -0
  86. package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T002-increase-debounce.md +21 -0
  87. package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T003-error-handling.md +22 -0
  88. package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T004-test-verify.md +29 -0
  89. package/.savepoint/releases/v1.1/epics/E12-validation-fix/E12-Audit.md +444 -0
  90. package/.savepoint/releases/v1.1/epics/E12-validation-fix/E12-Detail.md +45 -0
  91. package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T001-default-phase.md +35 -0
  92. package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T002-default-status.md +19 -0
  93. package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T003-better-errors.md +29 -0
  94. package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T004-validate-on-write.md +25 -0
  95. package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T005-tests.md +37 -0
  96. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/E13-Audit.md +118 -0
  97. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/E13-Detail.md +73 -0
  98. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T001-safe-cleanup.md +66 -0
  99. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T002-bug-fixes.md +35 -0
  100. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T003-centralize-duplication.md +60 -0
  101. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T004-infrastructure.md +33 -0
  102. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T005-decompose-update.md +37 -0
  103. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T006-async-io.md +40 -0
  104. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T007-test-coverage.md +37 -0
  105. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/E14-Audit.md +267 -0
  106. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/E14-Detail.md +54 -0
  107. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T001-group-model.md +39 -0
  108. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T002-data-interfaces.md +42 -0
  109. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T003-discover-orphans.md +33 -0
  110. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T004-epic-panel-headings.md +35 -0
  111. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T005-shell-tokenization.md +27 -0
  112. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T006-unify-enums.md +29 -0
  113. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T007-testutil-package.md +28 -0
  114. package/.savepoint/releases/v1.1/epics/E15-hardening/E15-Detail.md +43 -0
  115. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T001-benchmarks.md +31 -0
  116. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T002-fuzz-targets.md +28 -0
  117. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T003-debug-flag.md +30 -0
  118. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T004-dist-checksums.md +27 -0
  119. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T005-windows-targets.md +28 -0
  120. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T006-abbreviation-splitting.md +26 -0
  121. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T007-root-test-allowlist.md +28 -0
  122. package/.savepoint/releases/v1.1/epics/_archived/T001-cli-entrypoint.md +25 -0
  123. package/.savepoint/releases/v1.1/epics/_archived/T002-quality-gates.md +27 -0
  124. package/.savepoint/releases/v1.1/epics/_archived/T003-snapshot.md +27 -0
  125. package/.savepoint/releases/v1.1/epics/_archived/T004-ai-reconcile.md +29 -0
  126. package/.savepoint/releases/v1.1/epics/_archived/T006-tui-review.md +31 -0
  127. package/.savepoint/releases/v1.1/epics/_archived/T008-skip-handling.md +34 -0
  128. package/.savepoint/releases/v1.1/v1.1-PRD.md +139 -0
  129. package/.savepoint/router.md +29 -108
  130. package/AGENTS.md +69 -111
  131. package/Makefile +19 -3
  132. package/README.md +6 -6
  133. package/agent-skills/savepoint-audit/SKILL.md +87 -35
  134. package/agent-skills/savepoint-build-task/SKILL.md +9 -4
  135. package/agent-skills/savepoint-create-plan/SKILL.md +10 -5
  136. package/agent-skills/savepoint-create-task/SKILL.md +44 -31
  137. package/agent-skills/savepoint-draft-prd/SKILL.md +8 -3
  138. package/agent-skills/savepoint-system-design/SKILL.md +8 -3
  139. package/agent_skills_test.go +91 -0
  140. package/cmd/board.go +59 -0
  141. package/cmd/board_test.go +137 -0
  142. package/cmd/doctor.go +53 -0
  143. package/cmd/doctor_test.go +146 -0
  144. package/cmd/init.go +63 -0
  145. package/cmd/init_test.go +104 -0
  146. package/internal/board/board.go +69 -49
  147. package/internal/board/board_test.go +83 -67
  148. package/internal/board/card.go +71 -20
  149. package/internal/board/card_test.go +141 -12
  150. package/internal/board/column.go +77 -11
  151. package/internal/board/column_test.go +63 -13
  152. package/internal/board/detail.go +107 -72
  153. package/internal/board/detail_test.go +117 -26
  154. package/internal/board/epic_panel.go +211 -18
  155. package/internal/board/epic_panel_test.go +637 -14
  156. package/internal/board/help.go +1 -0
  157. package/internal/board/help_test.go +1 -0
  158. package/internal/board/integration_test.go +266 -0
  159. package/internal/board/interfaces.go +65 -0
  160. package/internal/board/interfaces_test.go +114 -0
  161. package/internal/board/io.go +93 -0
  162. package/internal/board/layout.go +12 -2
  163. package/internal/board/layout_test.go +17 -0
  164. package/internal/board/model.go +130 -52
  165. package/internal/board/plain.go +88 -0
  166. package/internal/board/plain_test.go +117 -0
  167. package/internal/board/release.go +1 -9
  168. package/internal/board/release_test.go +6 -6
  169. package/internal/board/render_policy_test.go +77 -0
  170. package/internal/board/status.go +23 -0
  171. package/internal/board/theme.go +24 -0
  172. package/internal/board/theme_test.go +31 -0
  173. package/internal/board/transitions.go +113 -88
  174. package/internal/board/transitions_test.go +164 -141
  175. package/internal/board/tui.go +32 -0
  176. package/internal/board/update.go +472 -94
  177. package/internal/board/update_test.go +447 -0
  178. package/internal/board/util.go +76 -0
  179. package/internal/board/view.go +139 -22
  180. package/internal/board/view_test.go +171 -3
  181. package/internal/board/watch.go +57 -9
  182. package/internal/buildtool/main.go +211 -0
  183. package/internal/buildtool/main_test.go +46 -0
  184. package/internal/data/config.go +17 -3
  185. package/internal/data/config_test.go +49 -0
  186. package/internal/data/discover.go +26 -0
  187. package/internal/data/discover_test.go +34 -10
  188. package/internal/data/errors.go +4 -0
  189. package/internal/data/lifecycle.go +13 -6
  190. package/internal/data/lifecycle_test.go +14 -11
  191. package/internal/data/parser.go +29 -6
  192. package/internal/data/parser_test.go +66 -7
  193. package/internal/data/task.go +1 -0
  194. package/internal/data/write.go +85 -11
  195. package/internal/data/write_test.go +167 -0
  196. package/internal/doctor/checks.go +567 -0
  197. package/internal/doctor/checks_test.go +716 -0
  198. package/internal/doctor/gates.go +193 -0
  199. package/internal/doctor/gates_test.go +166 -0
  200. package/internal/doctor/interfaces.go +64 -0
  201. package/internal/doctor/interfaces_test.go +104 -0
  202. package/internal/doctor/repairs.go +80 -0
  203. package/internal/doctor/repairs_test.go +81 -0
  204. package/internal/doctor/report.go +157 -0
  205. package/internal/doctor/report_test.go +89 -0
  206. package/internal/init/clipboard.go +146 -0
  207. package/internal/init/clipboard_test.go +74 -0
  208. package/internal/init/install.go +16 -0
  209. package/internal/init/integration_test.go +197 -0
  210. package/internal/init/prompt.go +14 -0
  211. package/internal/init/prompt_test.go +77 -0
  212. package/internal/init/scaffold.go +59 -0
  213. package/internal/init/scaffold_test.go +179 -0
  214. package/internal/init/template_freshness_test.go +56 -0
  215. package/internal/init/validate.go +85 -0
  216. package/internal/init/validate_test.go +141 -0
  217. package/internal/init/write.go +73 -0
  218. package/internal/init/write_test.go +91 -0
  219. package/internal/styles/palette.go +3 -3
  220. package/internal/styles/styles.go +39 -12
  221. package/internal/styles/styles_test.go +133 -0
  222. package/internal/testutil/fixture.go +113 -0
  223. package/internal/testutil/fs.go +26 -0
  224. package/main.go +107 -1
  225. package/package.json +2 -2
  226. package/project-audit/audit_report_glm_5.1.md +411 -0
  227. package/project-audit/audit_report_opus_4.6 +406 -0
  228. package/project-audit/consolidated-audit-report.md +456 -0
  229. package/savepoint +0 -0
  230. package/templates/project/.savepoint/Design.md +2 -2
  231. package/templates/project/.savepoint/router.md +15 -14
  232. package/templates/project/AGENTS.md +56 -98
  233. package/templates/project/agent-skills/savepoint-audit/SKILL.md +87 -0
  234. package/templates/project/agent-skills/savepoint-build-task/SKILL.md +44 -0
  235. package/templates/project/agent-skills/savepoint-create-plan/SKILL.md +33 -0
  236. package/templates/project/agent-skills/savepoint-create-task/SKILL.md +44 -0
  237. package/templates/project/agent-skills/savepoint-draft-prd/SKILL.md +37 -0
  238. package/templates/project/agent-skills/savepoint-system-design/SKILL.md +38 -0
  239. package/templates/prompts/audit-reconciliation.prompt.md +35 -30
  240. package/templates/prompts/design.prompt.md +3 -1
  241. package/templates/prompts/epic-design.prompt.md +3 -3
  242. package/templates/prompts/task-breakdown.prompt.md +1 -1
  243. package/templates/prompts/task-building.prompt.md +1 -1
  244. package/templates/prompts/task-planning.prompt.md +1 -1
  245. package/.savepoint/audit/E01-go-setup/proposals.md +0 -166
  246. package/.savepoint/audit/E01-go-setup/snapshot.md +0 -71
  247. package/.savepoint/audit/E01-scaffolding/proposals/AGENTS.md +0 -66
  248. package/.savepoint/audit/E01-scaffolding/proposals/Design.md +0 -210
  249. package/.savepoint/audit/E01-scaffolding/proposals/epic-Design.md +0 -117
  250. package/.savepoint/audit/E01-scaffolding/proposals/quality-review.md +0 -101
  251. package/.savepoint/audit/E01-scaffolding/snapshot.md +0 -54
  252. package/.savepoint/audit/E02-data-model/snapshot.md +0 -128
  253. package/.savepoint/audit/E02-data-readers/proposals.md +0 -123
  254. package/.savepoint/audit/E02-data-readers/snapshot.md +0 -54
  255. package/.savepoint/audit/E03-board-tui-core/proposals.md +0 -146
  256. package/.savepoint/audit/E03-board-tui-core/snapshot.md +0 -57
  257. package/.savepoint/audit/E03-cli-foundation/snapshot.md +0 -106
  258. package/.savepoint/audit/E04-board-components/proposals.md +0 -118
  259. package/.savepoint/audit/E04-board-components/snapshot.md +0 -77
  260. package/.savepoint/audit/E04-templates-and-prompts/snapshot.md +0 -115
  261. package/.savepoint/audit/E05-init-command/snapshot.md +0 -125
  262. package/.savepoint/audit/E05-phase-transitions/proposals.md +0 -83
  263. package/.savepoint/audit/E05-phase-transitions/snapshot.md +0 -36
  264. package/.savepoint/audit/E06-atari-noir-layout/proposals.md +0 -130
  265. package/.savepoint/audit/E06-atari-noir-layout/snapshot.md +0 -84
  266. package/.savepoint/audit/E06-tui-board/snapshot.md +0 -64
  267. package/.savepoint/audit/E07-audit-pipeline/snapshot.md +0 -165
  268. package/.savepoint/audit/E08-board-workflow-cleanup/snapshot.md +0 -65
  269. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T001-border-resize-fix.md +0 -36
  270. package/ink-cli-ui-design.zip +0 -0
  271. package/main.exe +0 -0
  272. package/savepoint.exe +0 -0
  273. /package/.savepoint/releases/v1/epics/E01-go-setup/{Design.md → E01-Detail.md} +0 -0
  274. /package/.savepoint/releases/v1/epics/E02-data-readers/{Design.md → E02-Detail.md} +0 -0
  275. /package/.savepoint/releases/v1/epics/E03-board-tui-core/{Design.md → E03-Detail.md} +0 -0
  276. /package/.savepoint/releases/v1/epics/E04-board-components/{Design.md → E04-Detail.md} +0 -0
  277. /package/.savepoint/releases/v1/epics/E05-phase-transitions/{Design.md → E05-Detail.md} +0 -0
  278. /package/.savepoint/releases/v1/{PRD.md → v1-PRD.md} +0 -0
@@ -0,0 +1,157 @@
1
+ package doctor
2
+
3
+ import (
4
+ "fmt"
5
+ "strings"
6
+ )
7
+
8
+ // QualityGateReport wraps quality gate results.
9
+ type QualityGateReport struct {
10
+ Results []GateResult
11
+ }
12
+
13
+ // DiagnosticReport is the complete output of all doctor checks.
14
+ type DiagnosticReport struct {
15
+ ConfigCheck error
16
+ RouterCheck error
17
+ Structure []Problem
18
+ Dependencies []Problem
19
+ AuditState []Problem
20
+ Orphans []Problem
21
+ Gates QualityGateReport
22
+ EpicFilter string
23
+ }
24
+
25
+ // RunAllChecks runs every doctor check and returns a full report.
26
+ func RunAllChecks(root string, epicFilter string) *DiagnosticReport {
27
+ report := &DiagnosticReport{
28
+ EpicFilter: epicFilter,
29
+ }
30
+
31
+ report.ConfigCheck = CheckConfig(root)
32
+ report.RouterCheck = CheckRouter(root, epicFilter)
33
+ report.Structure = CheckStructure(root, epicFilter)
34
+ report.Dependencies = CheckDependencies(root, epicFilter)
35
+ report.AuditState = CheckAuditState(root)
36
+ report.Orphans = CheckOrphans(root)
37
+ report.Gates.Results = RunQualityGates(root)
38
+
39
+ return report
40
+ }
41
+
42
+ // HasProblems returns true if any check found issues.
43
+ func (r *DiagnosticReport) HasProblems() bool {
44
+ if r.ConfigCheck != nil {
45
+ return true
46
+ }
47
+ if r.RouterCheck != nil {
48
+ return true
49
+ }
50
+ if len(r.Structure) > 0 {
51
+ return true
52
+ }
53
+ if len(r.Dependencies) > 0 {
54
+ return true
55
+ }
56
+ if len(r.AuditState) > 0 {
57
+ return true
58
+ }
59
+ if len(r.Orphans) > 0 {
60
+ return true
61
+ }
62
+ for _, g := range r.Gates.Results {
63
+ if !g.Passed {
64
+ return true
65
+ }
66
+ }
67
+ return false
68
+ }
69
+
70
+ // Format produces the full human-readable diagnostic report.
71
+ func (r *DiagnosticReport) Format() string {
72
+ var b strings.Builder
73
+
74
+ b.WriteString("savepoint doctor report\n")
75
+ if r.EpicFilter != "" {
76
+ fmt.Fprintf(&b, " filtering to epic: %s\n", r.EpicFilter)
77
+ }
78
+ b.WriteString("────────────────────────────────\n\n")
79
+
80
+ sectionHeader(&b, "Config Check")
81
+ printSingleCheck(&b, "config", r.ConfigCheck)
82
+
83
+ sectionHeader(&b, "Router Check")
84
+ printSingleCheck(&b, "router", r.RouterCheck)
85
+
86
+ sectionHeader(&b, "Structure Check")
87
+ printProblems(&b, "structure", r.Structure)
88
+
89
+ sectionHeader(&b, "Dependency Check")
90
+ printProblems(&b, "dependency", r.Dependencies)
91
+
92
+ sectionHeader(&b, "Audit State Check")
93
+ printProblems(&b, "audit", r.AuditState)
94
+
95
+ sectionHeader(&b, "Orphan Check")
96
+ printProblems(&b, "orphan", r.Orphans)
97
+
98
+ sectionHeader(&b, "Quality Gates")
99
+ for _, g := range r.Gates.Results {
100
+ status := "PASS"
101
+ if !g.Passed {
102
+ status = "FAIL"
103
+ }
104
+ fmt.Fprintf(&b, " [%s] %s", status, g.Name)
105
+ if g.Command != "" {
106
+ fmt.Fprintf(&b, " (%s)", g.Command)
107
+ }
108
+ b.WriteString("\n")
109
+ if !g.Passed {
110
+ hint := GateSuggestion(g.Name)
111
+ fmt.Fprintf(&b, " repair: %s\n", hint)
112
+ if g.Output != "" {
113
+ for _, line := range strings.Split(g.Output, "\n") {
114
+ b.WriteString(" ")
115
+ b.WriteString(line)
116
+ b.WriteString("\n")
117
+ }
118
+ }
119
+ }
120
+ }
121
+
122
+ b.WriteString("\n")
123
+ if r.HasProblems() {
124
+ b.WriteString("result: PROBLEMS FOUND (exit code 1)\n")
125
+ } else {
126
+ b.WriteString("result: ALL CLEAN (exit code 0)\n")
127
+ }
128
+
129
+ return b.String()
130
+ }
131
+
132
+ func sectionHeader(b *strings.Builder, title string) {
133
+ fmt.Fprintf(b, "◆ %s\n", title)
134
+ b.WriteString(strings.Repeat("─", len(title)+2))
135
+ b.WriteString("\n")
136
+ }
137
+
138
+ func printSingleCheck(b *strings.Builder, name string, err error) {
139
+ if err == nil {
140
+ fmt.Fprintf(b, " ✓ %s\n\n", name)
141
+ } else {
142
+ fmt.Fprintf(b, " ✗ %s: %s\n", name, err.Error())
143
+ fmt.Fprintf(b, " repair: %s\n\n", SuggestRepair(err))
144
+ }
145
+ }
146
+
147
+ func printProblems(b *strings.Builder, category string, problems []Problem) {
148
+ if len(problems) == 0 {
149
+ fmt.Fprintf(b, " ✓ no problems\n\n")
150
+ return
151
+ }
152
+ for _, p := range problems {
153
+ fmt.Fprintf(b, " ✗ %s: %s\n", category, p.Error())
154
+ fmt.Fprintf(b, " repair: %s\n", SuggestRepair(p))
155
+ }
156
+ b.WriteString("\n")
157
+ }
@@ -0,0 +1,89 @@
1
+ package doctor
2
+
3
+ import (
4
+ "strings"
5
+ "testing"
6
+
7
+ "github.com/opencode/savepoint/internal/testutil"
8
+ )
9
+
10
+ func TestDiagnosticReport_HasProblems(t *testing.T) {
11
+ root := t.TempDir()
12
+ report := RunAllChecks(root, "")
13
+ if !report.HasProblems() {
14
+ t.Fatal("RunAllChecks() on empty dir should have problems")
15
+ }
16
+ }
17
+
18
+ func TestDiagnosticReport_CleanProject(t *testing.T) {
19
+ root := t.TempDir()
20
+ writeReportProject(t, root)
21
+ report := RunAllChecks(root, "")
22
+ if report.HasProblems() {
23
+ t.Fatalf("RunAllChecks() on valid project should have no problems, got: config=%v router=%v structure=%v deps=%v audit=%v orphans=%v gates=%v",
24
+ report.ConfigCheck, report.RouterCheck, report.Structure, report.Dependencies, report.AuditState, report.Orphans, report.Gates.Results)
25
+ }
26
+ }
27
+
28
+ func TestDiagnosticReport_FormatContainsSections(t *testing.T) {
29
+ root := t.TempDir()
30
+ report := RunAllChecks(root, "")
31
+ output := report.Format()
32
+
33
+ sections := []string{
34
+ "Config Check",
35
+ "Router Check",
36
+ "Structure Check",
37
+ "Dependency Check",
38
+ "Audit State Check",
39
+ "Orphan Check",
40
+ "Quality Gates",
41
+ "PROBLEMS FOUND",
42
+ }
43
+ for _, s := range sections {
44
+ if !strings.Contains(output, s) {
45
+ t.Errorf("report.Format() missing section %q", s)
46
+ }
47
+ }
48
+ }
49
+
50
+ func TestDiagnosticReport_FormatWithEpicFilter(t *testing.T) {
51
+ root := t.TempDir()
52
+ report := RunAllChecks(root, "E03")
53
+ output := report.Format()
54
+ if !strings.Contains(output, "filtering to epic: E03") {
55
+ t.Errorf("report.Format() missing epic filter: %s", output)
56
+ }
57
+ }
58
+
59
+ func TestDiagnosticReport_FormatAllClean(t *testing.T) {
60
+ root := t.TempDir()
61
+ writeReportProject(t, root)
62
+ report := RunAllChecks(root, "")
63
+ output := report.Format()
64
+ if !strings.Contains(output, "ALL CLEAN") {
65
+ t.Errorf("report.Format() on clean project should say ALL CLEAN, got: %s", output)
66
+ }
67
+ if strings.Contains(output, "PROBLEMS FOUND") {
68
+ t.Errorf("report.Format() on clean project should not say PROBLEMS FOUND, got: %s", output)
69
+ }
70
+ }
71
+
72
+ func TestDiagnosticReport_FormatShowsRepairs(t *testing.T) {
73
+ root := t.TempDir()
74
+ report := RunAllChecks(root, "")
75
+ output := report.Format()
76
+ if !strings.Contains(output, "repair:") {
77
+ t.Errorf("report.Format() should include repair suggestions, got: %s", output)
78
+ }
79
+ }
80
+
81
+ func writeReportProject(t *testing.T, root string) {
82
+ t.Helper()
83
+ testutil.SetupMinimalProject(t, root, "v1", "E01-foo")
84
+ testutil.WriteTask(t, root, "v1", "E01-foo", testutil.TaskFixture{
85
+ Slug: "T001-task",
86
+ Status: "planned",
87
+ Objective: "Task",
88
+ })
89
+ }
@@ -0,0 +1,146 @@
1
+ package init
2
+
3
+ import (
4
+ "bytes"
5
+ "fmt"
6
+ "os/exec"
7
+ "runtime"
8
+ )
9
+
10
+ type ClipboardStatus int
11
+
12
+ const (
13
+ ClipboardCopied ClipboardStatus = iota
14
+ ClipboardSkipped
15
+ ClipboardFailed
16
+ )
17
+
18
+ func (s ClipboardStatus) String() string {
19
+ switch s {
20
+ case ClipboardCopied:
21
+ return "copied"
22
+ case ClipboardSkipped:
23
+ return "skipped"
24
+ case ClipboardFailed:
25
+ return "failed"
26
+ default:
27
+ return "unknown"
28
+ }
29
+ }
30
+
31
+ type ClipboardResult struct {
32
+ Status ClipboardStatus
33
+ Tool string
34
+ Message string
35
+ }
36
+
37
+ func CopyToClipboard(text string) ClipboardResult {
38
+ switch runtime.GOOS {
39
+ case "windows":
40
+ return copyWindows(text)
41
+ case "darwin":
42
+ return copyDarwin(text)
43
+ case "linux":
44
+ return copyLinux(text)
45
+ default:
46
+ return ClipboardResult{
47
+ Status: ClipboardSkipped,
48
+ Tool: "",
49
+ Message: fmt.Sprintf("unsupported platform: %s", runtime.GOOS),
50
+ }
51
+ }
52
+ }
53
+
54
+ func copyWindows(text string) ClipboardResult {
55
+ if _, err := exec.LookPath("clip"); err != nil {
56
+ return ClipboardResult{
57
+ Status: ClipboardSkipped,
58
+ Tool: "clip.exe",
59
+ Message: "clip.exe not found in PATH",
60
+ }
61
+ }
62
+
63
+ cmd := exec.Command("clip")
64
+ cmd.Stdin = bytes.NewReader([]byte(text))
65
+ if err := cmd.Run(); err != nil {
66
+ return ClipboardResult{
67
+ Status: ClipboardFailed,
68
+ Tool: "clip.exe",
69
+ Message: fmt.Sprintf("clip.exe failed: %v", err),
70
+ }
71
+ }
72
+
73
+ return ClipboardResult{
74
+ Status: ClipboardCopied,
75
+ Tool: "clip.exe",
76
+ Message: "",
77
+ }
78
+ }
79
+
80
+ func copyDarwin(text string) ClipboardResult {
81
+ if _, err := exec.LookPath("pbcopy"); err != nil {
82
+ return ClipboardResult{
83
+ Status: ClipboardSkipped,
84
+ Tool: "pbcopy",
85
+ Message: "pbcopy not found in PATH",
86
+ }
87
+ }
88
+
89
+ cmd := exec.Command("pbcopy")
90
+ cmd.Stdin = bytes.NewReader([]byte(text))
91
+ if err := cmd.Run(); err != nil {
92
+ return ClipboardResult{
93
+ Status: ClipboardFailed,
94
+ Tool: "pbcopy",
95
+ Message: fmt.Sprintf("pbcopy failed: %v", err),
96
+ }
97
+ }
98
+
99
+ return ClipboardResult{
100
+ Status: ClipboardCopied,
101
+ Tool: "pbcopy",
102
+ Message: "",
103
+ }
104
+ }
105
+
106
+ func copyLinux(text string) ClipboardResult {
107
+ if path, err := exec.LookPath("xclip"); err == nil {
108
+ cmd := exec.Command(path, "-selection", "clipboard")
109
+ cmd.Stdin = bytes.NewReader([]byte(text))
110
+ if err := cmd.Run(); err != nil {
111
+ return ClipboardResult{
112
+ Status: ClipboardFailed,
113
+ Tool: "xclip",
114
+ Message: fmt.Sprintf("xclip failed: %v", err),
115
+ }
116
+ }
117
+ return ClipboardResult{
118
+ Status: ClipboardCopied,
119
+ Tool: "xclip",
120
+ Message: "",
121
+ }
122
+ }
123
+
124
+ if path, err := exec.LookPath("xsel"); err == nil {
125
+ cmd := exec.Command(path, "--input", "--clipboard")
126
+ cmd.Stdin = bytes.NewReader([]byte(text))
127
+ if err := cmd.Run(); err != nil {
128
+ return ClipboardResult{
129
+ Status: ClipboardFailed,
130
+ Tool: "xsel",
131
+ Message: fmt.Sprintf("xsel failed: %v", err),
132
+ }
133
+ }
134
+ return ClipboardResult{
135
+ Status: ClipboardCopied,
136
+ Tool: "xsel",
137
+ Message: "",
138
+ }
139
+ }
140
+
141
+ return ClipboardResult{
142
+ Status: ClipboardSkipped,
143
+ Tool: "",
144
+ Message: "no clipboard tool found (tried xclip, xsel)",
145
+ }
146
+ }
@@ -0,0 +1,74 @@
1
+ package init
2
+
3
+ import (
4
+ "runtime"
5
+ "testing"
6
+ )
7
+
8
+ func TestCopyToClipboard_returnsWithoutPanic(t *testing.T) {
9
+ result := CopyToClipboard("test content")
10
+ if result.Status != ClipboardCopied &&
11
+ result.Status != ClipboardSkipped &&
12
+ result.Status != ClipboardFailed {
13
+ t.Fatalf("CopyToClipboard() returned unexpected status %v", result.Status)
14
+ }
15
+ }
16
+
17
+ func TestCopyToClipboard_resultHasNonEmptyStatusString(t *testing.T) {
18
+ result := CopyToClipboard("test")
19
+ if result.Status.String() == "" {
20
+ t.Fatal("Status.String() returned empty")
21
+ }
22
+ }
23
+
24
+ func TestCopyToClipboard_skippedOnUnsupportedPlatform(t *testing.T) {
25
+ if runtime.GOOS == "windows" || runtime.GOOS == "darwin" || runtime.GOOS == "linux" {
26
+ return
27
+ }
28
+ result := CopyToClipboard("test")
29
+ if result.Status != ClipboardSkipped {
30
+ t.Fatalf("on unsupported platform, expected ClipboardSkipped, got %v", result.Status)
31
+ }
32
+ }
33
+
34
+ func TestCopyToClipboard_emptyString(t *testing.T) {
35
+ result := CopyToClipboard("")
36
+ if result.Status != ClipboardCopied &&
37
+ result.Status != ClipboardSkipped &&
38
+ result.Status != ClipboardFailed {
39
+ t.Fatalf("CopyToClipboard('') returned unexpected status %v", result.Status)
40
+ }
41
+ }
42
+
43
+ func TestCopyToClipboard_toolName(t *testing.T) {
44
+ result := CopyToClipboard("test")
45
+ if result.Status == ClipboardCopied && result.Tool == "" {
46
+ t.Fatal("ClipboardCopied but Tool is empty")
47
+ }
48
+ }
49
+
50
+ func TestClipboardStatus_string(t *testing.T) {
51
+ tests := []struct {
52
+ status ClipboardStatus
53
+ want string
54
+ }{
55
+ {ClipboardCopied, "copied"},
56
+ {ClipboardSkipped, "skipped"},
57
+ {ClipboardFailed, "failed"},
58
+ }
59
+ for _, tt := range tests {
60
+ if got := tt.status.String(); got != tt.want {
61
+ t.Errorf("ClipboardStatus(%d).String() = %q, want %q", tt.status, got, tt.want)
62
+ }
63
+ }
64
+ }
65
+
66
+ func TestCopyToClipboard_multipleCalls(t *testing.T) {
67
+ for i := 0; i < 3; i++ {
68
+ result := CopyToClipboard("test content")
69
+ if result.Status != ClipboardCopied &&
70
+ result.Status != ClipboardSkipped {
71
+ t.Fatalf("call %d: unexpected status %v", i, result.Status)
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,16 @@
1
+ package init
2
+
3
+ import (
4
+ "fmt"
5
+ "os/exec"
6
+ )
7
+
8
+ func InstallDependencies(dir string) error {
9
+ cmd := exec.Command("npm", "install")
10
+ cmd.Dir = dir
11
+ out, err := cmd.CombinedOutput()
12
+ if err != nil {
13
+ return fmt.Errorf("npm install failed in %s: %w\noutput: %s", dir, err, string(out))
14
+ }
15
+ return nil
16
+ }
@@ -0,0 +1,197 @@
1
+ package init
2
+
3
+ import (
4
+ "io/fs"
5
+ "os"
6
+ "os/exec"
7
+ "path/filepath"
8
+ "strings"
9
+ "testing"
10
+ "testing/fstest"
11
+
12
+ "github.com/opencode/savepoint/internal/testutil"
13
+ )
14
+
15
+ func runInitPipeline(t *testing.T, dir string, force bool) string {
16
+ t.Helper()
17
+
18
+ if err := ValidateTarget(dir, force); err != nil {
19
+ t.Fatal(err)
20
+ }
21
+
22
+ templates := fstest.MapFS{
23
+ ".savepoint": &fstest.MapFile{Mode: fs.ModeDir | 0755},
24
+ ".savepoint/config.yml": &fstest.MapFile{Data: []byte("key: value")},
25
+ ".savepoint/Design.md": &fstest.MapFile{Data: []byte("# {{PROJECT_NAME}} Design")},
26
+ ".savepoint/PRD.md": &fstest.MapFile{Data: []byte("PRD: {{PROJECT_NAME}}")},
27
+ ".savepoint/router.md": &fstest.MapFile{Data: []byte("# Router")},
28
+ ".savepoint/visual-identity.md": &fstest.MapFile{Data: []byte("# Visual Identity")},
29
+ "AGENTS.md": &fstest.MapFile{Data: []byte("# Agents Guide\n\nBuild: npm run build")},
30
+ "agent-skills/savepoint-audit/SKILL.md": &fstest.MapFile{Data: []byte("# Audit Skill")},
31
+ }
32
+
33
+ projectName := ProjectNameFromDir(dir)
34
+ if err := Scaffold(templates, dir, projectName, force); err != nil {
35
+ t.Fatal(err)
36
+ }
37
+
38
+ promptTemplates := fstest.MapFS{
39
+ "magic-prompt.prompt.md": &fstest.MapFile{
40
+ Data: []byte("<!-- AGENT: Read AGENTS.md -->\n\nProject: {{PROJECT_NAME}}\n\nStart by reading AGENTS.md"),
41
+ },
42
+ }
43
+
44
+ prompt, err := RenderMagicPrompt(promptTemplates, projectName)
45
+ if err != nil {
46
+ t.Fatal(err)
47
+ }
48
+
49
+ return prompt
50
+ }
51
+
52
+ func TestIntegration_EmptyDirectory(t *testing.T) {
53
+ dir := t.TempDir()
54
+ prompt := runInitPipeline(t, dir, false)
55
+ projectName := filepath.Base(dir)
56
+
57
+ entries := []string{
58
+ ".savepoint/config.yml",
59
+ ".savepoint/Design.md",
60
+ ".savepoint/PRD.md",
61
+ ".savepoint/router.md",
62
+ ".savepoint/visual-identity.md",
63
+ "AGENTS.md",
64
+ "agent-skills/savepoint-audit/SKILL.md",
65
+ }
66
+ for _, e := range entries {
67
+ if _, err := os.Stat(filepath.Join(dir, e)); err != nil {
68
+ t.Errorf("missing %s: %v", e, err)
69
+ }
70
+ }
71
+
72
+ data, err := os.ReadFile(filepath.Join(dir, ".savepoint", "Design.md"))
73
+ if err != nil {
74
+ t.Fatal(err)
75
+ }
76
+ if !strings.Contains(string(data), projectName) {
77
+ t.Errorf("Design.md content = %q, want to contain %q", string(data), projectName)
78
+ }
79
+
80
+ if !strings.Contains(prompt, projectName) {
81
+ t.Errorf("prompt = %q, want to contain %q", prompt, projectName)
82
+ }
83
+ if !strings.Contains(prompt, "AGENT") {
84
+ t.Errorf("prompt = %q, want AGENT marker", prompt)
85
+ }
86
+
87
+ result := CopyToClipboard(prompt)
88
+ if result.Status != ClipboardCopied &&
89
+ result.Status != ClipboardSkipped &&
90
+ result.Status != ClipboardFailed {
91
+ t.Errorf("unexpected clipboard status: %v", result.Status)
92
+ }
93
+ }
94
+
95
+ func TestIntegration_CompatibleProject(t *testing.T) {
96
+ dir := t.TempDir()
97
+
98
+ for _, name := range []string{"package.json", ".git", "README.md"} {
99
+ testutil.WriteFile(t, filepath.Join(dir, name), "")
100
+ }
101
+
102
+ prompt := runInitPipeline(t, dir, false)
103
+
104
+ if _, err := os.Stat(filepath.Join(dir, "package.json")); err != nil {
105
+ t.Errorf("package.json missing: %v", err)
106
+ }
107
+
108
+ if _, err := os.Stat(filepath.Join(dir, ".savepoint", "config.yml")); err != nil {
109
+ t.Errorf(".savepoint/config.yml missing: %v", err)
110
+ }
111
+
112
+ if !strings.Contains(prompt, "AGENT") {
113
+ t.Errorf("prompt should contain AGENT marker")
114
+ }
115
+ }
116
+
117
+ func TestIntegration_NoForceOnExistingSavepoint(t *testing.T) {
118
+ dir := t.TempDir()
119
+
120
+ savepointDir := filepath.Join(dir, ".savepoint")
121
+ if err := os.Mkdir(savepointDir, 0755); err != nil {
122
+ t.Fatal(err)
123
+ }
124
+
125
+ err := ValidateTarget(dir, false)
126
+ if err == nil {
127
+ t.Fatal("expected error for existing .savepoint without --force")
128
+ }
129
+ if !strings.Contains(err.Error(), "already contains") {
130
+ t.Errorf("error = %q, want 'already contains'", err.Error())
131
+ }
132
+ }
133
+
134
+ func TestIntegration_ForceOverwritesExistingSavepoint(t *testing.T) {
135
+ dir := t.TempDir()
136
+
137
+ savepointDir := filepath.Join(dir, ".savepoint")
138
+ if err := os.Mkdir(savepointDir, 0755); err != nil {
139
+ t.Fatal(err)
140
+ }
141
+
142
+ prompt := runInitPipeline(t, dir, true)
143
+
144
+ for _, path := range []string{
145
+ ".savepoint/config.yml", ".savepoint/Design.md",
146
+ ".savepoint/PRD.md", ".savepoint/router.md",
147
+ } {
148
+ if _, err := os.Stat(filepath.Join(dir, path)); err != nil {
149
+ t.Errorf("expected %s to exist after --force: %v", path, err)
150
+ }
151
+ }
152
+
153
+ if !strings.Contains(prompt, "AGENT") {
154
+ t.Errorf("prompt should contain AGENT marker")
155
+ }
156
+ }
157
+
158
+ func TestIntegration_InstallDependencies(t *testing.T) {
159
+ if _, err := exec.LookPath("npm"); err != nil {
160
+ t.Skip("npm not found in PATH, skipping install test")
161
+ }
162
+
163
+ dir := t.TempDir()
164
+
165
+ packageJSON := filepath.Join(dir, "package.json")
166
+ testutil.WriteFile(t, packageJSON, `{"name":"test","version":"0.0.0"}`)
167
+
168
+ if err := ValidateTarget(dir, false); err != nil {
169
+ t.Fatal(err)
170
+ }
171
+
172
+ if err := InstallDependencies(dir); err != nil {
173
+ t.Fatalf("InstallDependencies() error = %v", err)
174
+ }
175
+
176
+ if _, err := os.Stat(filepath.Join(dir, "package-lock.json")); err != nil {
177
+ t.Errorf("package-lock.json not created after npm install: %v", err)
178
+ }
179
+ }
180
+
181
+ func TestIntegration_MagicPromptInOutput(t *testing.T) {
182
+ dir := t.TempDir()
183
+ prompt := runInitPipeline(t, dir, false)
184
+ projectName := filepath.Base(dir)
185
+
186
+ expectedParts := []string{
187
+ projectName,
188
+ "AGENT",
189
+ "AGENTS.md",
190
+ "Project",
191
+ }
192
+ for _, part := range expectedParts {
193
+ if !strings.Contains(prompt, part) {
194
+ t.Errorf("prompt missing %q", part)
195
+ }
196
+ }
197
+ }
@@ -0,0 +1,14 @@
1
+ package init
2
+
3
+ import (
4
+ "fmt"
5
+ "io/fs"
6
+ )
7
+
8
+ func RenderMagicPrompt(templates fs.FS, projectName string) (string, error) {
9
+ content, err := fs.ReadFile(templates, "magic-prompt.prompt.md")
10
+ if err != nil {
11
+ return "", fmt.Errorf("read magic prompt template: %w", err)
12
+ }
13
+ return interpolate(string(content), projectName), nil
14
+ }