savepoint 1.0.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 (273) hide show
  1. package/.claude/settings.local.json +20 -0
  2. package/.prettierignore +4 -0
  3. package/.savepoint/Design.md +196 -0
  4. package/.savepoint/PRD.md +58 -0
  5. package/.savepoint/audit/E01-go-setup/proposals.md +166 -0
  6. package/.savepoint/audit/E01-go-setup/snapshot.md +71 -0
  7. package/.savepoint/audit/E01-scaffolding/proposals/AGENTS.md +66 -0
  8. package/.savepoint/audit/E01-scaffolding/proposals/Design.md +210 -0
  9. package/.savepoint/audit/E01-scaffolding/proposals/epic-Design.md +117 -0
  10. package/.savepoint/audit/E01-scaffolding/proposals/quality-review.md +101 -0
  11. package/.savepoint/audit/E01-scaffolding/snapshot.md +54 -0
  12. package/.savepoint/audit/E02-data-model/snapshot.md +128 -0
  13. package/.savepoint/audit/E02-data-readers/proposals.md +123 -0
  14. package/.savepoint/audit/E02-data-readers/snapshot.md +54 -0
  15. package/.savepoint/audit/E03-board-tui-core/proposals.md +146 -0
  16. package/.savepoint/audit/E03-board-tui-core/snapshot.md +57 -0
  17. package/.savepoint/audit/E03-cli-foundation/snapshot.md +106 -0
  18. package/.savepoint/audit/E04-board-components/proposals.md +118 -0
  19. package/.savepoint/audit/E04-board-components/snapshot.md +77 -0
  20. package/.savepoint/audit/E04-templates-and-prompts/snapshot.md +115 -0
  21. package/.savepoint/audit/E05-init-command/snapshot.md +125 -0
  22. package/.savepoint/audit/E05-phase-transitions/proposals.md +83 -0
  23. package/.savepoint/audit/E05-phase-transitions/snapshot.md +36 -0
  24. package/.savepoint/audit/E06-tui-board/snapshot.md +64 -0
  25. package/.savepoint/audit/E07-audit-pipeline/snapshot.md +165 -0
  26. package/.savepoint/audit/E08-board-workflow-cleanup/snapshot.md +65 -0
  27. package/.savepoint/config.yml +27 -0
  28. package/.savepoint/releases/v1/PRD.md +66 -0
  29. package/.savepoint/releases/v1/epics/E01-go-setup/Design.md +39 -0
  30. package/.savepoint/releases/v1/epics/E01-go-setup/tasks/T001-init-module.md +42 -0
  31. package/.savepoint/releases/v1/epics/E01-go-setup/tasks/T002-entrypoint.md +23 -0
  32. package/.savepoint/releases/v1/epics/E01-go-setup/tasks/T003-directory-structure.md +24 -0
  33. package/.savepoint/releases/v1/epics/E01-go-setup/tasks/T004-makefile.md +23 -0
  34. package/.savepoint/releases/v1/epics/E02-data-readers/Design.md +61 -0
  35. package/.savepoint/releases/v1/epics/E02-data-readers/tasks/T001-task-struct.md +29 -0
  36. package/.savepoint/releases/v1/epics/E02-data-readers/tasks/T002-frontmatter-parser.md +30 -0
  37. package/.savepoint/releases/v1/epics/E02-data-readers/tasks/T003-router-reader.md +29 -0
  38. package/.savepoint/releases/v1/epics/E02-data-readers/tasks/T004-config-reader.md +29 -0
  39. package/.savepoint/releases/v1/epics/E02-data-readers/tasks/T005-discovery.md +30 -0
  40. package/.savepoint/releases/v1/epics/E03-board-tui-core/Design.md +38 -0
  41. package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T001-model.md +29 -0
  42. package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T002-update-loop.md +30 -0
  43. package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T003-view.md +34 -0
  44. package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T004-styles.md +29 -0
  45. package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T005-layout.md +42 -0
  46. package/.savepoint/releases/v1/epics/E04-board-components/Design.md +44 -0
  47. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T001-column.md +34 -0
  48. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T002-card.md +33 -0
  49. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T003-epic-panel.md +49 -0
  50. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T004-detail-overlay.md +40 -0
  51. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T005-release-dropdown.md +33 -0
  52. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T006-help-overlay.md +34 -0
  53. package/.savepoint/releases/v1/epics/E05-phase-transitions/Design.md +38 -0
  54. package/.savepoint/releases/v1/epics/E05-phase-transitions/tasks/T001-phase-stepping.md +29 -0
  55. package/.savepoint/releases/v1/epics/E05-phase-transitions/tasks/T002-gates.md +31 -0
  56. package/.savepoint/releases/v1/epics/E05-phase-transitions/tasks/T003-write-task.md +31 -0
  57. package/.savepoint/releases/v1/epics/E05-phase-transitions/tasks/T004-write-router.md +31 -0
  58. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/Design.md +42 -0
  59. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T001-color-system.md +39 -0
  60. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T002-header-and-dividers.md +52 -0
  61. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T003-footer-status-bar.md +52 -0
  62. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T004-component-refinement.md +53 -0
  63. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T005-restore-nav-hints.md +39 -0
  64. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T007-detail-card-fixes.md +36 -0
  65. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T008-checkbox-states.md +38 -0
  66. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T009-router-priority-marker.md +41 -0
  67. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T010-auto-refresh-watcher.md +61 -0
  68. package/.savepoint/releases/v1/epics/_archived/E01-archive-and-reset/Design.md +39 -0
  69. package/.savepoint/releases/v1/epics/_archived/E01-archive-and-reset/tasks/T001-archive-epics.md +20 -0
  70. package/.savepoint/releases/v1/epics/_archived/E01-archive-and-reset/tasks/T002-rewrite-prd.md +22 -0
  71. package/.savepoint/releases/v1/epics/_archived/E01-archive-and-reset/tasks/T003-create-epic-stubs.md +24 -0
  72. package/.savepoint/releases/v1/epics/_archived/E01-archive-and-reset/tasks/T004-update-router.md +22 -0
  73. package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/Design.md +118 -0
  74. package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/handoff.md +9 -0
  75. package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/tasks/T001-package-baseline.md +45 -0
  76. package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/tasks/T002-typescript-build.md +48 -0
  77. package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/tasks/T003-vitest-smoke.md +43 -0
  78. package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/tasks/T004-lint-format-gates.md +45 -0
  79. package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/tasks/T005-scaffold-verification.md +40 -0
  80. package/.savepoint/releases/v1/epics/_archived/E02-data-model/Design.md +142 -0
  81. package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T001-domain-ids-status.md +27 -0
  82. package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T002-markdown-frontmatter-boundary.md +28 -0
  83. package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T003-task-documents.md +29 -0
  84. package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T004-release-epic-router-config-readers.md +30 -0
  85. package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T005-dependency-validation.md +29 -0
  86. package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T006-epic-task-set-reader.md +29 -0
  87. package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T007-quality-gates.md +31 -0
  88. package/.savepoint/releases/v1/epics/_archived/E02-domain-phase-model/Design.md +40 -0
  89. package/.savepoint/releases/v1/epics/_archived/E02-domain-phase-model/tasks/T001-phase-types.md +27 -0
  90. package/.savepoint/releases/v1/epics/_archived/E02-domain-phase-model/tasks/T002-phase-frontmatter.md +25 -0
  91. package/.savepoint/releases/v1/epics/_archived/E02-domain-phase-model/tasks/T003-simplify-config.md +26 -0
  92. package/.savepoint/releases/v1/epics/_archived/E02-domain-phase-model/tasks/T004-simplify-router-domain.md +24 -0
  93. package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/Design.md +122 -0
  94. package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T001-argument-parser-contract.md +28 -0
  95. package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T002-help-text-generation.md +28 -0
  96. package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T003-terminal-environment-detection.md +27 -0
  97. package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T004-command-stub-modules.md +29 -0
  98. package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T005-cli-runner-dispatch.md +34 -0
  99. package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T006-entrypoint-quality-gates.md +32 -0
  100. package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/Design.md +43 -0
  101. package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/tasks/T001-strip-args.md +26 -0
  102. package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/tasks/T002-strip-help.md +23 -0
  103. package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/tasks/T003-strip-run.md +23 -0
  104. package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/tasks/T004-delete-commands.md +24 -0
  105. package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/tasks/T005-update-cli-tests.md +22 -0
  106. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/Design.md +48 -0
  107. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T001-board-data-phases.md +26 -0
  108. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T002-phase-rendering.md +28 -0
  109. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T003-detail-pane-phases.md +27 -0
  110. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T004-phase-transitions.md +42 -0
  111. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T005-phase-gates.md +24 -0
  112. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T006-phase-write-back.md +24 -0
  113. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T007-remove-audit-flow.md +27 -0
  114. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T008-board-tests.md +25 -0
  115. package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/Design.md +85 -0
  116. package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/tasks/T001-project-template-assets.md +17 -0
  117. package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/tasks/T002-release-and-prompt-assets.md +20 -0
  118. package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/tasks/T003-template-registry-renderer.md +22 -0
  119. package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/tasks/T004-template-integrity-tests.md +17 -0
  120. package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/tasks/T005-template-closeout-quality-gates.md +16 -0
  121. package/.savepoint/releases/v1/epics/_archived/E05-init-command/Design.md +88 -0
  122. package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T001-init-cli-contract.md +22 -0
  123. package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T002-target-validation.md +23 -0
  124. package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T003-scaffold-writer.md +24 -0
  125. package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T004-magic-prompt-and-clipboard.md +23 -0
  126. package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T005-dev-deps-install-option.md +24 -0
  127. package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T006-init-command-integration.md +28 -0
  128. package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/Design.md +53 -0
  129. package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T001-delete-dead-src.md +23 -0
  130. package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T002-delete-dead-tests.md +26 -0
  131. package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T003-delete-assets.md +25 -0
  132. package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T004-clean-savepoint.md +28 -0
  133. package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T005-rewrite-agents-md.md +28 -0
  134. package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T006-clean-package-json.md +23 -0
  135. package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T007-verify.md +25 -0
  136. package/.savepoint/releases/v1/epics/_archived/E06-tui-board/Design.md +104 -0
  137. package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T001-board-command-data.md +23 -0
  138. package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T002-board-view-state.md +24 -0
  139. package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T003-transition-gates-and-writes.md +25 -0
  140. package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T004-terminal-theme.md +23 -0
  141. package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T005-ink-board-ui.md +26 -0
  142. package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T006-board-integration-audit-entry.md +24 -0
  143. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/Design.md +88 -0
  144. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T001-audit-cli-contract.md +23 -0
  145. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T002-quality-gate-runner.md +23 -0
  146. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T003-snapshot-and-prompt.md +23 -0
  147. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T004-audit-orchestration-router.md +27 -0
  148. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T005-proposal-validation-apply.md +25 -0
  149. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T006-audit-review-state.md +24 -0
  150. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T007-audit-review-ui.md +26 -0
  151. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T008-audit-pipeline-integration.md +24 -0
  152. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/Design.md +103 -0
  153. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T001-acceptance-criteria-model.md +30 -0
  154. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T002-release-task-set-reader.md +33 -0
  155. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T003-board-data-and-plain-output.md +34 -0
  156. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T004-board-selection-state.md +33 -0
  157. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T005-ink-board-layout-cleanup.md +37 -0
  158. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T006-task-detail-popup.md +36 -0
  159. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T007-templates-acceptance-criteria.md +34 -0
  160. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T008-board-workflow-integration.md +41 -0
  161. package/.savepoint/releases/v1/epics/_archived/E09-doctor-command/Design.md +70 -0
  162. package/.savepoint/releases/v1/epics/_archived/E10-docs-and-packaging/Design.md +68 -0
  163. package/.savepoint/releases/v1/epics/_archived/E11-release-validation/Design.md +68 -0
  164. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/Design.md +26 -0
  165. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T001-border-resize-fix.md +35 -0
  166. package/.savepoint/router.md +136 -0
  167. package/.savepoint/visual-identity.md +124 -0
  168. package/AGENTS.md +141 -0
  169. package/CLAUDE.md +1 -0
  170. package/GEMINI.md +1 -0
  171. package/LICENSE +21 -0
  172. package/Makefile +13 -0
  173. package/README.md +78 -0
  174. package/agent-skills/ink-tui-design/SKILL.md +309 -0
  175. package/agent-skills/ink-tui-design/references/component-patterns.md +371 -0
  176. package/agent-skills/ink-tui-design/references/hooks-guide.md +436 -0
  177. package/agent-skills/ink-tui-design/references/ink-gotchas.md +330 -0
  178. package/agent-skills/ink-tui-design/references/testing-patterns.md +384 -0
  179. package/agent-skills/savepoint-audit/SKILL.md +35 -0
  180. package/agent-skills/savepoint-build-task/SKILL.md +39 -0
  181. package/agent-skills/savepoint-create-plan/SKILL.md +28 -0
  182. package/agent-skills/savepoint-create-task/SKILL.md +31 -0
  183. package/agent-skills/savepoint-draft-prd/SKILL.md +32 -0
  184. package/agent-skills/savepoint-system-design/SKILL.md +33 -0
  185. package/agent-skills/superpowers/brainstorming/SKILL.md +165 -0
  186. package/agent-skills/superpowers/brainstorming/visual-companion.md +304 -0
  187. package/agent-skills/superpowers/dispatching-parallel-agents/SKILL.md +193 -0
  188. package/agent-skills/superpowers/executing-plans/SKILL.md +77 -0
  189. package/agent-skills/superpowers/finishing-a-development-branch/SKILL.md +213 -0
  190. package/agent-skills/superpowers/receiving-code-review/SKILL.md +226 -0
  191. package/agent-skills/superpowers/requesting-code-review/SKILL.md +115 -0
  192. package/agent-skills/superpowers/requesting-code-review/code-reviewer.md +160 -0
  193. package/agent-skills/superpowers/subagent-driven-development/SKILL.md +292 -0
  194. package/agent-skills/superpowers/subagent-driven-development/code-quality-reviewer-prompt.md +27 -0
  195. package/agent-skills/superpowers/subagent-driven-development/implementer-prompt.md +113 -0
  196. package/agent-skills/superpowers/subagent-driven-development/spec-reviewer-prompt.md +61 -0
  197. package/agent-skills/superpowers/systematic-debugging/SKILL.md +305 -0
  198. package/agent-skills/superpowers/systematic-debugging/condition-based-waiting.md +122 -0
  199. package/agent-skills/superpowers/systematic-debugging/defense-in-depth.md +130 -0
  200. package/agent-skills/superpowers/systematic-debugging/root-cause-tracing.md +183 -0
  201. package/agent-skills/superpowers/test-driven-development/SKILL.md +389 -0
  202. package/agent-skills/superpowers/test-driven-development/testing-anti-patterns.md +317 -0
  203. package/agent-skills/superpowers/verification-before-completion/SKILL.md +147 -0
  204. package/agent-skills/superpowers/writing-plans/SKILL.md +159 -0
  205. package/agent-skills/superpowers/writing-plans/plan-document-reviewer-prompt.md +49 -0
  206. package/assets/banner.png +0 -0
  207. package/assets/logo.png +0 -0
  208. package/assets/strawman.png +0 -0
  209. package/go.mod +33 -0
  210. package/go.sum +73 -0
  211. package/ink-cli-ui-design.zip +0 -0
  212. package/internal/board/board.go +121 -0
  213. package/internal/board/board_test.go +99 -0
  214. package/internal/board/card.go +72 -0
  215. package/internal/board/card_test.go +111 -0
  216. package/internal/board/column.go +61 -0
  217. package/internal/board/column_test.go +81 -0
  218. package/internal/board/detail.go +140 -0
  219. package/internal/board/detail_test.go +233 -0
  220. package/internal/board/epic_panel.go +69 -0
  221. package/internal/board/epic_panel_test.go +246 -0
  222. package/internal/board/help.go +40 -0
  223. package/internal/board/help_test.go +85 -0
  224. package/internal/board/layout.go +58 -0
  225. package/internal/board/layout_test.go +89 -0
  226. package/internal/board/model.go +151 -0
  227. package/internal/board/model_test.go +67 -0
  228. package/internal/board/release.go +42 -0
  229. package/internal/board/release_test.go +177 -0
  230. package/internal/board/transitions.go +88 -0
  231. package/internal/board/transitions_test.go +141 -0
  232. package/internal/board/update.go +155 -0
  233. package/internal/board/update_test.go +128 -0
  234. package/internal/board/view.go +190 -0
  235. package/internal/board/view_test.go +147 -0
  236. package/internal/data/config.go +87 -0
  237. package/internal/data/config_test.go +73 -0
  238. package/internal/data/discover.go +152 -0
  239. package/internal/data/discover_test.go +106 -0
  240. package/internal/data/errors.go +9 -0
  241. package/internal/data/lifecycle.go +37 -0
  242. package/internal/data/lifecycle_test.go +38 -0
  243. package/internal/data/parser.go +189 -0
  244. package/internal/data/parser_test.go +216 -0
  245. package/internal/data/router.go +52 -0
  246. package/internal/data/router_test.go +35 -0
  247. package/internal/data/task.go +46 -0
  248. package/internal/data/task_test.go +51 -0
  249. package/internal/data/write.go +144 -0
  250. package/internal/data/write_test.go +456 -0
  251. package/internal/styles/palette.go +47 -0
  252. package/internal/styles/styles.go +122 -0
  253. package/main.exe +0 -0
  254. package/main.go +11 -0
  255. package/package.json +25 -0
  256. package/savepoint +0 -0
  257. package/savepoint.exe +0 -0
  258. package/scripts/vitest-preload.cjs +95 -0
  259. package/templates/project/.savepoint/Design.md +47 -0
  260. package/templates/project/.savepoint/PRD.md +34 -0
  261. package/templates/project/.savepoint/config.yml +27 -0
  262. package/templates/project/.savepoint/router.md +152 -0
  263. package/templates/project/.savepoint/visual-identity.md +122 -0
  264. package/templates/project/AGENTS.md +130 -0
  265. package/templates/prompts/audit-reconciliation.prompt.md +67 -0
  266. package/templates/prompts/design.prompt.md +43 -0
  267. package/templates/prompts/epic-design.prompt.md +43 -0
  268. package/templates/prompts/magic-prompt.prompt.md +7 -0
  269. package/templates/prompts/prd.prompt.md +42 -0
  270. package/templates/prompts/task-breakdown.prompt.md +54 -0
  271. package/templates/prompts/task-building.prompt.md +38 -0
  272. package/templates/prompts/task-planning.prompt.md +53 -0
  273. package/templates/release/v1/PRD.md +37 -0
@@ -0,0 +1,144 @@
1
+ package data
2
+
3
+ import (
4
+ "fmt"
5
+ "os"
6
+ "path/filepath"
7
+ "strings"
8
+ "time"
9
+
10
+ "gopkg.in/yaml.v3"
11
+ )
12
+
13
+ var ErrMtimeConflict = fmt.Errorf("file modified since last read")
14
+
15
+ func WriteTaskStatus(path string, task *Task, expectedMtime time.Time) error {
16
+ if err := ValidateTaskLifecycle(*task); err != nil {
17
+ return err
18
+ }
19
+
20
+ fi, err := os.Stat(path)
21
+ if err != nil {
22
+ return fmt.Errorf("stat %s: %w", path, err)
23
+ }
24
+
25
+ if !fi.ModTime().Equal(expectedMtime) {
26
+ return ErrMtimeConflict
27
+ }
28
+
29
+ content, err := os.ReadFile(path)
30
+ if err != nil {
31
+ return fmt.Errorf("read %s: %w", path, err)
32
+ }
33
+
34
+ normalized := strings.ReplaceAll(string(content), "\r\n", "\n")
35
+
36
+ raw, err := extractFrontmatter(normalized)
37
+ if err != nil {
38
+ return fmt.Errorf("extract frontmatter: %w", err)
39
+ }
40
+
41
+ var doc yaml.Node
42
+ if err := yaml.Unmarshal([]byte(raw), &doc); err != nil {
43
+ return fmt.Errorf("parse yaml: %w", err)
44
+ }
45
+
46
+ if doc.Kind != yaml.DocumentNode || len(doc.Content) == 0 {
47
+ return fmt.Errorf("unexpected yaml structure")
48
+ }
49
+
50
+ mapping := doc.Content[0]
51
+ if mapping.Kind != yaml.MappingNode {
52
+ return fmt.Errorf("frontmatter is not a mapping")
53
+ }
54
+
55
+ setMappingField(mapping, "status", string(task.Column))
56
+
57
+ if task.Stage == "" {
58
+ removeMappingField(mapping, "phase")
59
+ } else {
60
+ setMappingField(mapping, "phase", string(task.Stage))
61
+ }
62
+
63
+ out, err := yaml.Marshal(&doc)
64
+ if err != nil {
65
+ return fmt.Errorf("marshal yaml: %w", err)
66
+ }
67
+
68
+ delimLen := 4
69
+ bodyStart := delimLen + len(raw) + delimLen
70
+ body := ""
71
+ if bodyStart < len(normalized) {
72
+ body = normalized[bodyStart:]
73
+ }
74
+
75
+ newContent := "---\n" + strings.TrimSpace(string(out)) + "\n---" + body
76
+
77
+ return os.WriteFile(path, []byte(newContent), 0644)
78
+ }
79
+
80
+ func setMappingField(mapping *yaml.Node, key, value string) {
81
+ for i := 0; i < len(mapping.Content)-1; i += 2 {
82
+ if mapping.Content[i].Value == key {
83
+ mapping.Content[i+1].Value = value
84
+ mapping.Content[i+1].Tag = "!!str"
85
+ return
86
+ }
87
+ }
88
+ keyNode := &yaml.Node{Kind: yaml.ScalarNode, Value: key, Tag: "!!str"}
89
+ valNode := &yaml.Node{Kind: yaml.ScalarNode, Value: value, Tag: "!!str"}
90
+ mapping.Content = append(mapping.Content, keyNode, valNode)
91
+ }
92
+
93
+ func removeMappingField(mapping *yaml.Node, key string) {
94
+ for i := 0; i < len(mapping.Content)-1; i += 2 {
95
+ if mapping.Content[i].Value == key {
96
+ mapping.Content = append(mapping.Content[:i], mapping.Content[i+2:]...)
97
+ return
98
+ }
99
+ }
100
+ }
101
+
102
+ func WriteRouterState(root string, state *RouterState, expectedMtime time.Time) error {
103
+ path := filepath.Join(root, "router.md")
104
+ fi, err := os.Stat(path)
105
+ if err != nil {
106
+ return fmt.Errorf("stat %s: %w", path, err)
107
+ }
108
+
109
+ if !fi.ModTime().Equal(expectedMtime) {
110
+ return ErrMtimeConflict
111
+ }
112
+
113
+ content, err := os.ReadFile(path)
114
+ if err != nil {
115
+ return fmt.Errorf("read %s: %w", path, err)
116
+ }
117
+
118
+ normalized := strings.ReplaceAll(string(content), "\r\n", "\n")
119
+
120
+ startIdx := strings.Index(normalized, stateBlockStart)
121
+ if startIdx == -1 {
122
+ return fmt.Errorf("no Current state block found")
123
+ }
124
+
125
+ yamlStart := strings.Index(normalized[startIdx:], "```yaml")
126
+ if yamlStart == -1 {
127
+ return fmt.Errorf("no yaml code block found")
128
+ }
129
+
130
+ yamlStart += startIdx + len("```yaml")
131
+ yamlEnd := strings.Index(normalized[yamlStart:], "```")
132
+ if yamlEnd == -1 {
133
+ return fmt.Errorf("no closing code block found")
134
+ }
135
+
136
+ out, err := yaml.Marshal(state)
137
+ if err != nil {
138
+ return fmt.Errorf("marshal yaml: %w", err)
139
+ }
140
+
141
+ newContent := normalized[:yamlStart] + "\n" + strings.TrimSpace(string(out)) + "\n" + normalized[yamlStart+yamlEnd:]
142
+
143
+ return os.WriteFile(path, []byte(newContent), 0644)
144
+ }
@@ -0,0 +1,456 @@
1
+ package data
2
+
3
+ import (
4
+ "os"
5
+ "path/filepath"
6
+ "strings"
7
+ "testing"
8
+ "time"
9
+ )
10
+
11
+ func TestWriteTaskStatus_updatesStatusAndPhase(t *testing.T) {
12
+ dir := t.TempDir()
13
+ path := filepath.Join(dir, "task.md")
14
+ content := `---
15
+ id: E01/T001
16
+ status: planned
17
+ phase: build
18
+ objective: "Test"
19
+ depends_on: []
20
+ ---
21
+
22
+ # Body text`
23
+
24
+ if err := os.WriteFile(path, []byte(content), 0644); err != nil {
25
+ t.Fatal(err)
26
+ }
27
+
28
+ fi, err := os.Stat(path)
29
+ if err != nil {
30
+ t.Fatal(err)
31
+ }
32
+
33
+ task := &Task{
34
+ ID: "E01/T001",
35
+ Column: ColumnInProgress,
36
+ Stage: StageTest,
37
+ }
38
+
39
+ if err := WriteTaskStatus(path, task, fi.ModTime()); err != nil {
40
+ t.Fatalf("WriteTaskStatus() error = %v", err)
41
+ }
42
+
43
+ result, err := os.ReadFile(path)
44
+ if err != nil {
45
+ t.Fatal(err)
46
+ }
47
+
48
+ p := NewParser()
49
+ parsed, err := p.ParseTaskFile(path, string(result))
50
+ if err != nil {
51
+ t.Fatalf("ParseTaskFile() error = %v", err)
52
+ }
53
+
54
+ if parsed.Column != ColumnInProgress {
55
+ t.Errorf("Column = %v, want in_progress", parsed.Column)
56
+ }
57
+ if parsed.Stage != StageTest {
58
+ t.Errorf("Stage = %v, want test", parsed.Stage)
59
+ }
60
+
61
+ if !strings.Contains(string(result), "# Body text") {
62
+ t.Error("body content not preserved")
63
+ }
64
+ }
65
+
66
+ func TestWriteTaskStatus_removesPhaseWhenStageEmpty(t *testing.T) {
67
+ dir := t.TempDir()
68
+ path := filepath.Join(dir, "task.md")
69
+ content := `---
70
+ id: E01/T002
71
+ status: in_progress
72
+ phase: audit
73
+ objective: "Test"
74
+ ---
75
+
76
+ # Body`
77
+
78
+ if err := os.WriteFile(path, []byte(content), 0644); err != nil {
79
+ t.Fatal(err)
80
+ }
81
+
82
+ fi, _ := os.Stat(path)
83
+
84
+ task := &Task{
85
+ ID: "E01/T002",
86
+ Column: ColumnDone,
87
+ Stage: "",
88
+ }
89
+
90
+ if err := WriteTaskStatus(path, task, fi.ModTime()); err != nil {
91
+ t.Fatalf("WriteTaskStatus() error = %v", err)
92
+ }
93
+
94
+ result, _ := os.ReadFile(path)
95
+
96
+ if strings.Contains(string(result), "phase:") {
97
+ t.Error("phase field should be removed when stage is empty")
98
+ }
99
+
100
+ p := NewParser()
101
+ parsed, err := p.ParseTaskFile(path, string(result))
102
+ if err != nil {
103
+ t.Fatalf("ParseTaskFile() error = %v", err)
104
+ }
105
+
106
+ if parsed.Column != ColumnDone {
107
+ t.Errorf("Column = %v, want done", parsed.Column)
108
+ }
109
+ if parsed.Stage != "" {
110
+ t.Errorf("Stage = %v, want empty", parsed.Stage)
111
+ }
112
+ }
113
+
114
+ func TestWriteTaskStatus_removesPhaseWhenStatusPlanned(t *testing.T) {
115
+ dir := t.TempDir()
116
+ path := filepath.Join(dir, "task.md")
117
+ content := `---
118
+ id: E01/T003
119
+ status: in_progress
120
+ phase: build
121
+ ---
122
+
123
+ # Body`
124
+
125
+ if err := os.WriteFile(path, []byte(content), 0644); err != nil {
126
+ t.Fatal(err)
127
+ }
128
+
129
+ fi, _ := os.Stat(path)
130
+
131
+ task := &Task{
132
+ ID: "E01/T003",
133
+ Column: ColumnPlanned,
134
+ Stage: "",
135
+ }
136
+
137
+ if err := WriteTaskStatus(path, task, fi.ModTime()); err != nil {
138
+ t.Fatalf("WriteTaskStatus() error = %v", err)
139
+ }
140
+
141
+ result, _ := os.ReadFile(path)
142
+
143
+ if strings.Contains(string(result), "phase:") {
144
+ t.Error("phase field should be removed when status is planned")
145
+ }
146
+ }
147
+
148
+ func TestWriteTaskStatus_mtimeConflict(t *testing.T) {
149
+ dir := t.TempDir()
150
+ path := filepath.Join(dir, "task.md")
151
+ content := `---
152
+ id: E01/T004
153
+ status: planned
154
+ ---`
155
+
156
+ if err := os.WriteFile(path, []byte(content), 0644); err != nil {
157
+ t.Fatal(err)
158
+ }
159
+
160
+ oldMtime := time.Now().Add(-time.Hour)
161
+
162
+ task := &Task{
163
+ ID: "E01/T004",
164
+ Column: ColumnInProgress,
165
+ Stage: StageBuild,
166
+ }
167
+
168
+ err := WriteTaskStatus(path, task, oldMtime)
169
+ if err == nil {
170
+ t.Fatal("WriteTaskStatus() expected mtime conflict error")
171
+ }
172
+ if err != ErrMtimeConflict {
173
+ t.Fatalf("WriteTaskStatus() error = %v, want ErrMtimeConflict", err)
174
+ }
175
+ }
176
+
177
+ func TestWriteTaskStatus_addsPhaseWhenStagePresent(t *testing.T) {
178
+ dir := t.TempDir()
179
+ path := filepath.Join(dir, "task.md")
180
+ content := `---
181
+ id: E01/T005
182
+ status: in_progress
183
+ objective: "No phase yet"
184
+ ---`
185
+
186
+ if err := os.WriteFile(path, []byte(content), 0644); err != nil {
187
+ t.Fatal(err)
188
+ }
189
+
190
+ fi, _ := os.Stat(path)
191
+
192
+ task := &Task{
193
+ ID: "E01/T005",
194
+ Column: ColumnInProgress,
195
+ Stage: StageAudit,
196
+ }
197
+
198
+ if err := WriteTaskStatus(path, task, fi.ModTime()); err != nil {
199
+ t.Fatalf("WriteTaskStatus() error = %v", err)
200
+ }
201
+
202
+ result, _ := os.ReadFile(path)
203
+
204
+ if !strings.Contains(string(result), "phase: audit") {
205
+ t.Error("phase field should be added when stage is set")
206
+ }
207
+ }
208
+
209
+ func TestWriteTaskStatus_preservesBodyWithMultipleLines(t *testing.T) {
210
+ dir := t.TempDir()
211
+ path := filepath.Join(dir, "task.md")
212
+ content := `---
213
+ id: E01/T006
214
+ status: planned
215
+ ---
216
+
217
+ # Title
218
+
219
+ Some description here.
220
+
221
+ More content.`
222
+
223
+ if err := os.WriteFile(path, []byte(content), 0644); err != nil {
224
+ t.Fatal(err)
225
+ }
226
+
227
+ fi, _ := os.Stat(path)
228
+
229
+ task := &Task{
230
+ ID: "E01/T006",
231
+ Column: ColumnInProgress,
232
+ Stage: StageBuild,
233
+ }
234
+
235
+ if err := WriteTaskStatus(path, task, fi.ModTime()); err != nil {
236
+ t.Fatalf("WriteTaskStatus() error = %v", err)
237
+ }
238
+
239
+ result, _ := os.ReadFile(path)
240
+
241
+ if !strings.Contains(string(result), "# Title") {
242
+ t.Error("# Title not preserved")
243
+ }
244
+ if !strings.Contains(string(result), "Some description here.") {
245
+ t.Error("description not preserved")
246
+ }
247
+ if !strings.Contains(string(result), "More content.") {
248
+ t.Error("More content not preserved")
249
+ }
250
+ }
251
+
252
+ func TestWriteRouterState_updatesRouterFields(t *testing.T) {
253
+ dir := t.TempDir()
254
+ root := dir
255
+ content := `# Agent State Machine
256
+
257
+ ## Current state
258
+
259
+ ` + "```" + `yaml
260
+ state: task-building
261
+ release: v1
262
+ epic: E03-board-tui-core
263
+ task: E03-board-tui-core/T004-render
264
+ next_action: "Render the board"
265
+ ` + "```" + `
266
+
267
+ ## State definitions`
268
+
269
+ path := filepath.Join(root, "router.md")
270
+ if err := os.WriteFile(path, []byte(content), 0644); err != nil {
271
+ t.Fatal(err)
272
+ }
273
+
274
+ fi, err := os.Stat(path)
275
+ if err != nil {
276
+ t.Fatal(err)
277
+ }
278
+
279
+ state := &RouterState{
280
+ State: "task-building",
281
+ Release: "v1",
282
+ Epic: "E05-phase-transitions",
283
+ Task: "E05-phase-transitions/T004-write-router",
284
+ NextAction: "Write router state",
285
+ }
286
+
287
+ if err := WriteRouterState(root, state, fi.ModTime()); err != nil {
288
+ t.Fatalf("WriteRouterState() error = %v", err)
289
+ }
290
+
291
+ r := NewRouterReader()
292
+ result, err := os.ReadFile(path)
293
+ if err != nil {
294
+ t.Fatal(err)
295
+ }
296
+
297
+ parsed, err := r.ReadState(string(result))
298
+ if err != nil {
299
+ t.Fatalf("ReadState() error = %v", err)
300
+ }
301
+
302
+ if parsed.State != "task-building" {
303
+ t.Errorf("State = %q, want task-building", parsed.State)
304
+ }
305
+ if parsed.Epic != "E05-phase-transitions" {
306
+ t.Errorf("Epic = %q, want E05-phase-transitions", parsed.Epic)
307
+ }
308
+ if parsed.Release != "v1" {
309
+ t.Errorf("Release = %q, want v1", parsed.Release)
310
+ }
311
+ if parsed.Task != "E05-phase-transitions/T004-write-router" {
312
+ t.Errorf("Task = %q, want E05-phase-transitions/T004-write-router", parsed.Task)
313
+ }
314
+ if parsed.NextAction != "Write router state" {
315
+ t.Errorf("NextAction = %q, want Write router state", parsed.NextAction)
316
+ }
317
+
318
+ if !strings.Contains(string(result), "## State definitions") {
319
+ t.Error("body content after state block not preserved")
320
+ }
321
+ }
322
+
323
+ func TestWriteRouterState_mtimeConflict(t *testing.T) {
324
+ dir := t.TempDir()
325
+ root := dir
326
+ content := `## Current state
327
+
328
+ ` + "```" + `yaml
329
+ state: task-building
330
+ ` + "```" + `
331
+ `
332
+
333
+ path := filepath.Join(root, "router.md")
334
+ if err := os.WriteFile(path, []byte(content), 0644); err != nil {
335
+ t.Fatal(err)
336
+ }
337
+
338
+ oldMtime := time.Now().Add(-time.Hour)
339
+ state := &RouterState{State: "audit-pending"}
340
+
341
+ err := WriteRouterState(root, state, oldMtime)
342
+ if err == nil {
343
+ t.Fatal("WriteRouterState() expected mtime conflict error")
344
+ }
345
+ if err != ErrMtimeConflict {
346
+ t.Fatalf("WriteRouterState() error = %v, want ErrMtimeConflict", err)
347
+ }
348
+ }
349
+
350
+ func TestWriteRouterState_missingStateBlock(t *testing.T) {
351
+ dir := t.TempDir()
352
+ root := dir
353
+ content := `# No state block`
354
+
355
+ path := filepath.Join(root, "router.md")
356
+ if err := os.WriteFile(path, []byte(content), 0644); err != nil {
357
+ t.Fatal(err)
358
+ }
359
+
360
+ fi, _ := os.Stat(path)
361
+ state := &RouterState{State: "task-building"}
362
+
363
+ err := WriteRouterState(root, state, fi.ModTime())
364
+ if err == nil {
365
+ t.Fatal("WriteRouterState() expected error for missing state block")
366
+ }
367
+ }
368
+
369
+ func TestWriteRouterState_preservesNextAction(t *testing.T) {
370
+ dir := t.TempDir()
371
+ root := dir
372
+ content := `## Current state
373
+
374
+ ` + "```" + `yaml
375
+ state: task-building
376
+ release: v1
377
+ epic: E03-board-tui-core
378
+ task: ""
379
+ next_action: "Do the thing"
380
+ ` + "```" + `
381
+ `
382
+
383
+ path := filepath.Join(root, "router.md")
384
+ if err := os.WriteFile(path, []byte(content), 0644); err != nil {
385
+ t.Fatal(err)
386
+ }
387
+
388
+ fi, _ := os.Stat(path)
389
+
390
+ state := &RouterState{
391
+ State: "task-building",
392
+ Release: "v1",
393
+ Epic: "E05-phase-transitions",
394
+ Task: "",
395
+ NextAction: "Do the thing",
396
+ }
397
+
398
+ if err := WriteRouterState(root, state, fi.ModTime()); err != nil {
399
+ t.Fatalf("WriteRouterState() error = %v", err)
400
+ }
401
+
402
+ r := NewRouterReader()
403
+ result, _ := os.ReadFile(path)
404
+ parsed, _ := r.ReadState(string(result))
405
+
406
+ if parsed.NextAction != "Do the thing" {
407
+ t.Errorf("NextAction = %q, want %q", parsed.NextAction, "Do the thing")
408
+ }
409
+ }
410
+
411
+ func TestWriteTaskStatus_noFrontmatter(t *testing.T) {
412
+ dir := t.TempDir()
413
+ path := filepath.Join(dir, "task.md")
414
+ content := `# No frontmatter here`
415
+
416
+ if err := os.WriteFile(path, []byte(content), 0644); err != nil {
417
+ t.Fatal(err)
418
+ }
419
+
420
+ fi, _ := os.Stat(path)
421
+
422
+ task := &Task{
423
+ ID: "E01/T007",
424
+ Column: ColumnPlanned,
425
+ }
426
+
427
+ err := WriteTaskStatus(path, task, fi.ModTime())
428
+ if err == nil {
429
+ t.Fatal("WriteTaskStatus() expected error for missing frontmatter")
430
+ }
431
+ }
432
+
433
+ func TestWriteTaskStatus_rejectsInvalidLifecycle(t *testing.T) {
434
+ dir := t.TempDir()
435
+ path := filepath.Join(dir, "task.md")
436
+ content := `---
437
+ id: E01/T008
438
+ status: planned
439
+ ---`
440
+
441
+ if err := os.WriteFile(path, []byte(content), 0644); err != nil {
442
+ t.Fatal(err)
443
+ }
444
+
445
+ fi, _ := os.Stat(path)
446
+ task := &Task{
447
+ ID: "E01/T008",
448
+ Column: ColumnDone,
449
+ Stage: StageAudit,
450
+ }
451
+
452
+ err := WriteTaskStatus(path, task, fi.ModTime())
453
+ if err == nil {
454
+ t.Fatal("WriteTaskStatus() expected invalid lifecycle error")
455
+ }
456
+ }
@@ -0,0 +1,47 @@
1
+ package styles
2
+
3
+ // Truecolor hex constants (Atari-Noir palette)
4
+ const (
5
+ Background = "#121212"
6
+ Surface = "#0D0D0D"
7
+ Surface2 = "#0F0F0F"
8
+ Border = "#1A1A1A"
9
+ BorderSubtle = "#222222"
10
+ PrimaryText = "#F0E6DA"
11
+ AtariOrange = "#FC6323"
12
+ NPPGreen = "#A4C639"
13
+ VibePurple = "#B1A1DF"
14
+ )
15
+
16
+ // 256-color (ANSI256) fallbacks — nearest terminal approximations
17
+ const (
18
+ Background256 = "233"
19
+ Surface256 = "232"
20
+ Surface2256 = "232"
21
+ Border256 = "234"
22
+ BorderSubtle256 = "235"
23
+ PrimaryText256 = "230"
24
+ AtariOrange256 = "208"
25
+ NPPGreen256 = "148"
26
+ VibePurple256 = "147"
27
+ )
28
+
29
+ // 16-color (basic ANSI) fallbacks
30
+ const (
31
+ Background16 = "0" // black
32
+ Surface16 = "0" // black
33
+ Surface216 = "0" // black
34
+ Border16 = "0" // black
35
+ BorderSubtle16 = "8" // dark gray
36
+ PrimaryText16 = "15" // bright white
37
+ AtariOrange16 = "9" // bright red (closest to orange)
38
+ NPPGreen16 = "2" // green
39
+ VibePurple16 = "5" // magenta
40
+ Dim16 = "8" // dark gray
41
+ )
42
+
43
+ // Dim: muted foreground for metadata
44
+ const (
45
+ Dim = "#777777"
46
+ Dim256 = "243"
47
+ )