savepoint 1.0.1 → 1.0.2

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 (127) hide show
  1. package/.claude/settings.local.json +4 -1
  2. package/.savepoint/Design.md +22 -17
  3. package/.savepoint/audit/v1/E01/proposals.md +168 -0
  4. package/.savepoint/audit/v1/E01/snapshot.md +78 -0
  5. package/.savepoint/audit/{E01-go-setup → v1/E01-go-setup}/proposals.md +7 -7
  6. package/.savepoint/audit/{E01-go-setup → v1/E01-go-setup}/snapshot.md +2 -2
  7. package/.savepoint/audit/{E01-scaffolding → v1/E01-scaffolding}/proposals/AGENTS.md +5 -5
  8. package/.savepoint/audit/{E02-data-readers → v1/E02-data-readers}/proposals.md +20 -20
  9. package/.savepoint/audit/{E02-data-readers → v1/E02-data-readers}/snapshot.md +1 -1
  10. package/.savepoint/audit/{E03-board-tui-core → v1/E03-board-tui-core}/proposals.md +11 -11
  11. package/.savepoint/audit/{E03-board-tui-core → v1/E03-board-tui-core}/snapshot.md +1 -1
  12. package/.savepoint/audit/{E04-board-components → v1/E04-board-components}/proposals.md +14 -14
  13. package/.savepoint/audit/{E04-board-components → v1/E04-board-components}/snapshot.md +1 -1
  14. package/.savepoint/audit/{E05-init-command → v1/E05-init-command}/snapshot.md +1 -1
  15. package/.savepoint/audit/{E05-phase-transitions → v1/E05-phase-transitions}/proposals.md +4 -4
  16. package/.savepoint/audit/{E05-phase-transitions → v1/E05-phase-transitions}/snapshot.md +1 -1
  17. package/.savepoint/audit/{E06-atari-noir-layout → v1/E06-atari-noir-layout}/proposals.md +2 -2
  18. package/.savepoint/audit/{E07-audit-pipeline → v1/E07-audit-pipeline}/snapshot.md +6 -6
  19. package/.savepoint/audit/v1.1/E02-cross-platform-compatibility/proposals.md +114 -0
  20. package/.savepoint/audit/v1.1/E02-cross-platform-compatibility/snapshot.md +41 -0
  21. package/.savepoint/audit/v1.1/E04-epic-navigation/proposals.md +156 -0
  22. package/.savepoint/audit/v1.1/E04-epic-navigation/snapshot.md +48 -0
  23. package/.savepoint/releases/v1/epics/E01-go-setup/tasks/T001-init-module.md +1 -1
  24. package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T005-layout.md +1 -1
  25. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T002-card.md +1 -1
  26. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T006-help-overlay.md +1 -1
  27. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/{Design.md → E06-Detail.md} +5 -3
  28. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T002-header-and-dividers.md +1 -1
  29. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T003-footer-status-bar.md +1 -1
  30. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T004-component-refinement.md +1 -1
  31. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T010-auto-refresh-watcher.md +2 -0
  32. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/{Design.md → E01-Detail.md} +9 -1
  33. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/{T007-next-activity-header.md → T001-next-activity-header.md} +13 -12
  34. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T002-rename-epic-design-files.md +9 -9
  35. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T003-rename-release-prd.md +2 -2
  36. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T004-update-instruction-files.md +13 -12
  37. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T005-update-cross-references.md +14 -13
  38. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T006-column-and-detail-scrolling.md +25 -15
  39. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T007-column-focus-border-stability.md +57 -0
  40. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/{Design.md → E02-Detail.md} +12 -3
  41. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T001-fix-makefile.md +11 -8
  42. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T002-linux-build-target.md +12 -7
  43. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T003-macos-build-target.md +9 -5
  44. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T004-smoke-tests-and-artifacts.md +30 -9
  45. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/E03-Detail.md +32 -0
  46. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T001-border-resize-fix.md +40 -0
  47. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T002-next-activity-below-header.md +64 -0
  48. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T003-checkbox-rendering-fix.md +56 -0
  49. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T005-unify-status-glyphs.md +65 -0
  50. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T006-forced-256-color-profile.md +36 -0
  51. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/E04-Detail.md +51 -0
  52. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/tasks/T001-sidebar-focusable-navigation.md +65 -0
  53. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/tasks/T002-epic-detail-overlay.md +73 -0
  54. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/tasks/T003-epic-status-glyphs.md +73 -0
  55. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/E05-Detail.md +45 -0
  56. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T001-update-agents-md.md +34 -0
  57. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T002-update-router-md.md +30 -0
  58. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T003-update-design-md.md +33 -0
  59. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T004-implement-m-hotkey.md +88 -0
  60. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T005-update-help-overlay.md +30 -0
  61. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T006-tests-and-quality-gates.md +46 -0
  62. package/.savepoint/releases/v1.1/v1.1-PRD.md +79 -0
  63. package/.savepoint/router.md +33 -105
  64. package/AGENTS.md +56 -113
  65. package/Makefile +19 -3
  66. package/README.md +6 -5
  67. package/agent-skills/savepoint-audit/SKILL.md +6 -6
  68. package/agent-skills/savepoint-build-task/SKILL.md +2 -2
  69. package/agent-skills/savepoint-create-plan/SKILL.md +3 -3
  70. package/agent-skills/savepoint-create-task/SKILL.md +2 -2
  71. package/agent-skills/savepoint-draft-prd/SKILL.md +1 -1
  72. package/agent-skills/savepoint-system-design/SKILL.md +1 -1
  73. package/internal/board/board.go +43 -27
  74. package/internal/board/board_test.go +71 -0
  75. package/internal/board/card.go +34 -3
  76. package/internal/board/card_test.go +105 -12
  77. package/internal/board/column.go +40 -5
  78. package/internal/board/column_test.go +60 -13
  79. package/internal/board/detail.go +107 -25
  80. package/internal/board/detail_test.go +117 -26
  81. package/internal/board/epic_panel.go +105 -8
  82. package/internal/board/epic_panel_test.go +343 -5
  83. package/internal/board/layout.go +12 -2
  84. package/internal/board/layout_test.go +17 -0
  85. package/internal/board/model.go +141 -24
  86. package/internal/board/render_policy_test.go +77 -0
  87. package/internal/board/status.go +23 -0
  88. package/internal/board/update.go +276 -8
  89. package/internal/board/update_test.go +166 -0
  90. package/internal/board/view.go +131 -17
  91. package/internal/board/view_test.go +159 -1
  92. package/internal/board/watch.go +24 -6
  93. package/internal/buildtool/main.go +219 -0
  94. package/internal/data/parser.go +8 -0
  95. package/internal/data/parser_test.go +35 -0
  96. package/internal/data/task.go +10 -0
  97. package/internal/styles/palette.go +3 -3
  98. package/internal/styles/styles.go +39 -12
  99. package/main.go +9 -0
  100. package/package.json +1 -1
  101. package/savepoint +0 -0
  102. package/savepoint.exe +0 -0
  103. package/templates/project/.savepoint/router.md +6 -5
  104. package/templates/project/AGENTS.md +47 -101
  105. package/templates/prompts/audit-reconciliation.prompt.md +6 -6
  106. package/templates/prompts/epic-design.prompt.md +3 -3
  107. package/templates/prompts/task-breakdown.prompt.md +1 -1
  108. package/templates/prompts/task-building.prompt.md +1 -1
  109. package/templates/prompts/task-planning.prompt.md +1 -1
  110. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T001-border-resize-fix.md +0 -36
  111. package/main.exe +0 -0
  112. /package/.savepoint/audit/{E01-scaffolding → v1/E01-scaffolding}/proposals/Design.md +0 -0
  113. /package/.savepoint/audit/{E01-scaffolding → v1/E01-scaffolding}/proposals/epic-Design.md +0 -0
  114. /package/.savepoint/audit/{E01-scaffolding → v1/E01-scaffolding}/proposals/quality-review.md +0 -0
  115. /package/.savepoint/audit/{E01-scaffolding → v1/E01-scaffolding}/snapshot.md +0 -0
  116. /package/.savepoint/audit/{E02-data-model → v1/E02-data-model}/snapshot.md +0 -0
  117. /package/.savepoint/audit/{E03-cli-foundation → v1/E03-cli-foundation}/snapshot.md +0 -0
  118. /package/.savepoint/audit/{E04-templates-and-prompts → v1/E04-templates-and-prompts}/snapshot.md +0 -0
  119. /package/.savepoint/audit/{E06-atari-noir-layout → v1/E06-atari-noir-layout}/snapshot.md +0 -0
  120. /package/.savepoint/audit/{E06-tui-board → v1/E06-tui-board}/snapshot.md +0 -0
  121. /package/.savepoint/audit/{E08-board-workflow-cleanup → v1/E08-board-workflow-cleanup}/snapshot.md +0 -0
  122. /package/.savepoint/releases/v1/epics/E01-go-setup/{Design.md → E01-Detail.md} +0 -0
  123. /package/.savepoint/releases/v1/epics/E02-data-readers/{Design.md → E02-Detail.md} +0 -0
  124. /package/.savepoint/releases/v1/epics/E03-board-tui-core/{Design.md → E03-Detail.md} +0 -0
  125. /package/.savepoint/releases/v1/epics/E04-board-components/{Design.md → E04-Detail.md} +0 -0
  126. /package/.savepoint/releases/v1/epics/E05-phase-transitions/{Design.md → E05-Detail.md} +0 -0
  127. /package/.savepoint/releases/v1/{PRD.md → v1-PRD.md} +0 -0
@@ -0,0 +1,219 @@
1
+ package main
2
+
3
+ import (
4
+ "archive/tar"
5
+ "compress/gzip"
6
+ "errors"
7
+ "flag"
8
+ "fmt"
9
+ "io"
10
+ "os"
11
+ "os/exec"
12
+ "path/filepath"
13
+ "runtime"
14
+ )
15
+
16
+ type target struct {
17
+ os string
18
+ arch string
19
+ }
20
+
21
+ var targets = []target{
22
+ {os: "linux", arch: "amd64"},
23
+ {os: "linux", arch: "arm64"},
24
+ {os: "darwin", arch: "amd64"},
25
+ {os: "darwin", arch: "arm64"},
26
+ }
27
+
28
+ var versionOverride string
29
+
30
+ func main() {
31
+ if err := run(os.Args[1:]); err != nil {
32
+ fmt.Fprintln(os.Stderr, err)
33
+ os.Exit(1)
34
+ }
35
+ }
36
+
37
+ func run(args []string) error {
38
+ flags := flag.NewFlagSet("buildtool", flag.ContinueOnError)
39
+ flags.StringVar(&versionOverride, "version", "", "version to inject into the binary")
40
+ if err := flags.Parse(args); err != nil {
41
+ return err
42
+ }
43
+ if flags.NArg() != 1 {
44
+ return errors.New("usage: go run ./internal/buildtool [-version vX.Y.Z] <build|clean|build-linux|build-darwin|build-all|dist|smoke-test>")
45
+ }
46
+
47
+ switch flags.Arg(0) {
48
+ case "build":
49
+ return buildLocal()
50
+ case "clean":
51
+ return clean()
52
+ case "build-linux":
53
+ return buildMatching("linux")
54
+ case "build-darwin":
55
+ return buildMatching("darwin")
56
+ case "build-all":
57
+ return buildAll()
58
+ case "dist":
59
+ return dist()
60
+ case "smoke-test":
61
+ return smokeTest()
62
+ default:
63
+ return fmt.Errorf("unknown build target %q", flags.Arg(0))
64
+ }
65
+ }
66
+
67
+ func buildLocal() error {
68
+ return runGoBuild(localExecutable(), runtime.GOOS, runtime.GOARCH)
69
+ }
70
+
71
+ func clean() error {
72
+ for _, path := range []string{"savepoint", "savepoint.exe", "dist"} {
73
+ if err := os.RemoveAll(path); err != nil {
74
+ return fmt.Errorf("clean %s: %w", path, err)
75
+ }
76
+ }
77
+ return nil
78
+ }
79
+
80
+ func buildMatching(goos string) error {
81
+ for _, target := range targets {
82
+ if target.os != goos {
83
+ continue
84
+ }
85
+ if err := buildTarget(target); err != nil {
86
+ return err
87
+ }
88
+ }
89
+ return nil
90
+ }
91
+
92
+ func buildAll() error {
93
+ for _, target := range targets {
94
+ if err := buildTarget(target); err != nil {
95
+ return err
96
+ }
97
+ }
98
+ return nil
99
+ }
100
+
101
+ func buildTarget(target target) error {
102
+ output := filepath.Join("dist", target.os+"-"+target.arch, "savepoint")
103
+ return runGoBuild(output, target.os, target.arch)
104
+ }
105
+
106
+ func runGoBuild(output, goos, goarch string) error {
107
+ if err := os.MkdirAll(filepath.Dir(output), 0o755); err != nil && filepath.Dir(output) != "." {
108
+ return fmt.Errorf("create output dir: %w", err)
109
+ }
110
+
111
+ cmd := exec.Command("go", "build", "-ldflags", "-X main.version="+version(), "-o", output, "main.go")
112
+ cmd.Env = append(os.Environ(), "GOOS="+goos, "GOARCH="+goarch)
113
+ cmd.Stdout = os.Stdout
114
+ cmd.Stderr = os.Stderr
115
+ if err := cmd.Run(); err != nil {
116
+ return fmt.Errorf("build %s/%s: %w", goos, goarch, err)
117
+ }
118
+ return nil
119
+ }
120
+
121
+ func dist() error {
122
+ if err := buildAll(); err != nil {
123
+ return err
124
+ }
125
+ for _, target := range targets {
126
+ name := "savepoint-" + version() + "-" + target.os + "-" + target.arch + ".tar.gz"
127
+ source := filepath.Join("dist", target.os+"-"+target.arch, "savepoint")
128
+ archive := filepath.Join("dist", name)
129
+ if err := writeTarGz(archive, source, "savepoint"); err != nil {
130
+ return err
131
+ }
132
+ }
133
+ return nil
134
+ }
135
+
136
+ func writeTarGz(archivePath, sourcePath, archiveName string) error {
137
+ source, err := os.Open(sourcePath)
138
+ if err != nil {
139
+ return fmt.Errorf("open artifact source: %w", err)
140
+ }
141
+ defer source.Close()
142
+
143
+ info, err := source.Stat()
144
+ if err != nil {
145
+ return fmt.Errorf("stat artifact source: %w", err)
146
+ }
147
+
148
+ archive, err := os.Create(archivePath)
149
+ if err != nil {
150
+ return fmt.Errorf("create archive: %w", err)
151
+ }
152
+ defer archive.Close()
153
+
154
+ gzipWriter := gzip.NewWriter(archive)
155
+ defer gzipWriter.Close()
156
+
157
+ tarWriter := tar.NewWriter(gzipWriter)
158
+ defer tarWriter.Close()
159
+
160
+ header, err := tar.FileInfoHeader(info, "")
161
+ if err != nil {
162
+ return fmt.Errorf("create archive header: %w", err)
163
+ }
164
+ header.Name = archiveName
165
+ if err := tarWriter.WriteHeader(header); err != nil {
166
+ return fmt.Errorf("write archive header: %w", err)
167
+ }
168
+ if _, err := io.Copy(tarWriter, source); err != nil {
169
+ return fmt.Errorf("write archive content: %w", err)
170
+ }
171
+ return nil
172
+ }
173
+
174
+ func smokeTest() error {
175
+ if err := buildLocal(); err != nil {
176
+ return err
177
+ }
178
+ cmd := exec.Command("."+string(os.PathSeparator)+localExecutable(), "--version")
179
+ cmd.Stdout = os.Stdout
180
+ cmd.Stderr = os.Stderr
181
+ if err := cmd.Run(); err != nil {
182
+ return fmt.Errorf("smoke test: %w", err)
183
+ }
184
+ fmt.Println("smoke test passed")
185
+ return nil
186
+ }
187
+
188
+ func version() string {
189
+ if versionOverride != "" {
190
+ return versionOverride
191
+ }
192
+ if value := os.Getenv("VERSION"); value != "" {
193
+ return value
194
+ }
195
+
196
+ cmd := exec.Command("git", "describe", "--tags", "--abbrev=0")
197
+ output, err := cmd.Output()
198
+ if err == nil && len(output) > 0 {
199
+ return string(trimSpace(output))
200
+ }
201
+ return "v0.0.0"
202
+ }
203
+
204
+ func localExecutable() string {
205
+ if runtime.GOOS == "windows" {
206
+ return "savepoint.exe"
207
+ }
208
+ return "savepoint"
209
+ }
210
+
211
+ func trimSpace(value []byte) []byte {
212
+ for len(value) > 0 && (value[len(value)-1] == '\n' || value[len(value)-1] == '\r' || value[len(value)-1] == '\t' || value[len(value)-1] == ' ') {
213
+ value = value[:len(value)-1]
214
+ }
215
+ for len(value) > 0 && (value[0] == '\n' || value[0] == '\r' || value[0] == '\t' || value[0] == ' ') {
216
+ value = value[1:]
217
+ }
218
+ return value
219
+ }
@@ -175,18 +175,26 @@ func extractChecklistItems(content, heading string) []CheckItem {
175
175
  }
176
176
 
177
177
  items := []CheckItem{}
178
+ var current *CheckItem
178
179
  for _, line := range strings.Split(section, "\n") {
179
180
  trimmed := strings.TrimSpace(line)
180
181
  if strings.HasPrefix(trimmed, "- [x] ") {
181
182
  items = append(items, CheckItem{Text: strings.TrimSpace(trimmed[6:]), Done: true})
183
+ current = &items[len(items)-1]
182
184
  continue
183
185
  }
184
186
  if strings.HasPrefix(trimmed, "- [ ] ") {
185
187
  items = append(items, CheckItem{Text: strings.TrimSpace(trimmed[6:]), Done: false})
188
+ current = &items[len(items)-1]
186
189
  continue
187
190
  }
188
191
  if strings.HasPrefix(trimmed, "- ") {
189
192
  items = append(items, CheckItem{Text: strings.TrimSpace(trimmed[2:]), Done: false})
193
+ current = &items[len(items)-1]
194
+ continue
195
+ }
196
+ if trimmed != "" && current != nil {
197
+ current.Text = strings.TrimSpace(current.Text + " " + trimmed)
190
198
  }
191
199
  }
192
200
  return items
@@ -220,3 +220,38 @@ Notes here.`
220
220
  t.Errorf("Task.Checklist[1] = %+v, want {Text:\"Second checklist item.\", Done:true}", task.Checklist[1])
221
221
  }
222
222
  }
223
+
224
+ func TestParseTaskFile_joinsHardWrappedChecklistItems(t *testing.T) {
225
+ p := NewParser()
226
+ content := `---
227
+ id: E06/T001
228
+ status: planned
229
+ objective: "Style the board"
230
+ ---
231
+
232
+ # Task
233
+
234
+ ## Implementation Plan
235
+
236
+ - [ ] First sentence spans across a hard markdown line break
237
+ before it ends. Second sentence stays in the same checklist item.
238
+ - [x] Already checked sentence wraps
239
+ without becoming another checklist item.
240
+ `
241
+
242
+ task, err := p.ParseTaskFile("test.md", content)
243
+ if err != nil {
244
+ t.Fatalf("ParseTaskFile() error = %v", err)
245
+ }
246
+ if len(task.Checklist) != 2 {
247
+ t.Fatalf("Task.Checklist len = %d, want 2", len(task.Checklist))
248
+ }
249
+ wantFirst := "First sentence spans across a hard markdown line break before it ends. Second sentence stays in the same checklist item."
250
+ if task.Checklist[0].Text != wantFirst || task.Checklist[0].Done {
251
+ t.Errorf("Task.Checklist[0] = %+v, want text %q and Done=false", task.Checklist[0], wantFirst)
252
+ }
253
+ wantSecond := "Already checked sentence wraps without becoming another checklist item."
254
+ if task.Checklist[1].Text != wantSecond || !task.Checklist[1].Done {
255
+ t.Errorf("Task.Checklist[1] = %+v, want text %q and Done=true", task.Checklist[1], wantSecond)
256
+ }
257
+ }
@@ -26,6 +26,15 @@ const (
26
26
  StageAudit ProgressStage = "audit"
27
27
  )
28
28
 
29
+ type TaskStatus string
30
+
31
+ const (
32
+ StatusPlanned TaskStatus = "planned"
33
+ StatusInProgress TaskStatus = "in_progress"
34
+ StatusDone TaskStatus = "done"
35
+ StatusAudited TaskStatus = "audited"
36
+ )
37
+
29
38
  type Progress struct {
30
39
  Stage ProgressStage `yaml:"stage"`
31
40
  Started bool `yaml:"started"`
@@ -35,6 +44,7 @@ type Task struct {
35
44
  ID string `yaml:"id"`
36
45
  Title string `yaml:"title"`
37
46
  Description string `yaml:"description,omitempty"`
47
+ Status string `yaml:"status,omitempty"`
38
48
  Epic string `yaml:"epic"`
39
49
  Release string `yaml:"release"`
40
50
  Column ColumnType `yaml:"column"`
@@ -17,9 +17,9 @@ const (
17
17
 
18
18
  // 256-color (ANSI256) fallbacks — nearest terminal approximations
19
19
  const (
20
- Background256 = "233"
21
- Surface256 = "232"
22
- Surface2256 = "232"
20
+ Background256 = "16"
21
+ Surface256 = "16"
22
+ Surface2256 = "16"
23
23
  Border256 = "234"
24
24
  BorderSubtle256 = "235"
25
25
  PrimaryText256 = "230"
@@ -17,6 +17,8 @@ var (
17
17
  clrDim = color(Dim, Dim256, Dim16)
18
18
  )
19
19
 
20
+ var boxBorder = lipgloss.NormalBorder()
21
+
20
22
  var (
21
23
  HeaderIcon = lipgloss.NewStyle().
22
24
  Foreground(clrOrange).
@@ -29,20 +31,21 @@ var (
29
31
  Foreground(clrBorder)
30
32
 
31
33
  HeaderFrame = lipgloss.NewStyle().
32
- Background(clrSurfaceDark).
33
34
  Padding(1, 1)
34
35
 
35
- BoardFrame = lipgloss.NewStyle().
36
- Background(clrSurfaceDark)
36
+ BoardFrame = lipgloss.NewStyle()
37
37
 
38
38
  Column = lipgloss.NewStyle().
39
- Background(clrSurfaceDark).
40
39
  Padding(0, 1)
41
40
 
41
+ ColumnUnfocused = lipgloss.NewStyle().
42
+ BorderStyle(boxBorder).
43
+ BorderForeground(clrBorder).
44
+ Padding(0, 1)
45
+
42
46
  ColumnFocused = lipgloss.NewStyle().
43
- BorderStyle(lipgloss.RoundedBorder()).
47
+ BorderStyle(boxBorder).
44
48
  BorderForeground(clrOrange).
45
- Background(clrSurfaceDark).
46
49
  Padding(0, 1)
47
50
 
48
51
  ColumnTitle = lipgloss.NewStyle().
@@ -63,30 +66,49 @@ var (
63
66
  Foreground(clrText)
64
67
 
65
68
  EpicPanel = lipgloss.NewStyle().
66
- Background(clrSurface).
69
+ BorderStyle(boxBorder).
70
+ BorderForeground(clrBorder).
67
71
  Padding(0, 1)
68
72
 
73
+ EpicItemFocused = lipgloss.NewStyle().
74
+ Foreground(clrPurple)
75
+
76
+ EpicTitleFocused = lipgloss.NewStyle().
77
+ Foreground(clrPurple).
78
+ Bold(true)
79
+
80
+ EpicPanelFocused = lipgloss.NewStyle().
81
+ BorderStyle(boxBorder).
82
+ BorderForeground(clrPurple).
83
+ Padding(0, 1)
84
+
69
85
  Card = lipgloss.NewStyle().
70
- Background(clrSurface).
71
86
  Padding(0, 1)
72
87
 
73
88
  CardFocused = lipgloss.NewStyle().
74
- BorderStyle(lipgloss.RoundedBorder()).
89
+ BorderStyle(boxBorder).
75
90
  BorderForeground(clrOrange).
76
- Background(clrSurface).
77
91
  Padding(0, 1)
78
92
 
79
- CardMeta = lipgloss.NewStyle().Foreground(clrDim)
93
+ CardMeta = lipgloss.NewStyle().Foreground(clrDim)
94
+ ScrollIndicator = lipgloss.NewStyle().
95
+ Foreground(clrDim).
96
+ Faint(true)
80
97
 
81
98
  GlyphBuild = lipgloss.NewStyle().Foreground(clrOrange)
82
99
  GlyphTest = lipgloss.NewStyle().Foreground(clrGreen)
83
100
  GlyphAudit = lipgloss.NewStyle().Foreground(clrPurple)
84
101
 
85
102
  DetailOverlay = lipgloss.NewStyle().
86
- BorderStyle(lipgloss.RoundedBorder()).
103
+ BorderStyle(boxBorder).
87
104
  BorderForeground(clrOrange).
88
105
  Padding(0, 1)
89
106
 
107
+ EpicDetailOverlay = lipgloss.NewStyle().
108
+ BorderStyle(boxBorder).
109
+ BorderForeground(clrPurple).
110
+ Padding(0, 1)
111
+
90
112
  // Footer phase styles
91
113
  FooterPhasePlan = lipgloss.NewStyle().
92
114
  Foreground(clrPurple).
@@ -106,6 +128,11 @@ var (
106
128
  FooterHints = lipgloss.NewStyle().
107
129
  Foreground(clrDim)
108
130
 
131
+ HeaderRight = lipgloss.NewStyle().
132
+ Foreground(clrDim)
133
+
134
+ RootLine = lipgloss.NewStyle()
135
+
109
136
  // Tag styles for semantic encoding
110
137
  TagDone = lipgloss.NewStyle().Foreground(clrGreen)
111
138
  TagAI = lipgloss.NewStyle().Foreground(clrPurple)
package/main.go CHANGED
@@ -1,10 +1,19 @@
1
1
  package main
2
2
 
3
3
  import (
4
+ "fmt"
5
+ "os"
6
+
4
7
  "github.com/opencode/savepoint/internal/board"
5
8
  )
6
9
 
10
+ var version = "dev"
11
+
7
12
  func main() {
13
+ if len(os.Args) > 1 && os.Args[1] == "--version" {
14
+ fmt.Println(version)
15
+ os.Exit(0)
16
+ }
8
17
  if err := board.Run(); err != nil {
9
18
  panic(err)
10
19
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "savepoint",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "It’s a simple, file-based state machine and cinematic Terminal UI (TUI) designed to force you—and your agent (Claude, Cursor, Aider, Gemini)—to slow down, write down what you're actually building, and check your work before moving on.",
5
5
  "keywords": [
6
6
  "board",
package/savepoint CHANGED
Binary file
package/savepoint.exe CHANGED
Binary file
@@ -9,7 +9,7 @@ This file routes the agent based on the project's current state. Read this whene
9
9
  3. The active epic Design
10
10
  4. The active task file, when a task is selected
11
11
 
12
- Read `.savepoint/PRD.md` only for project vision changes. Read `.savepoint/Design.md` only for architecture changes or audit closeout. Read `.savepoint/releases/v{{RELEASE_NUMBER}}/PRD.md` only for release planning or epic-order questions.
12
+ Read `.savepoint/PRD.md` only for project vision changes. Read `.savepoint/Design.md` only for architecture changes or audit closeout. Read `.savepoint/releases/{release}/{release}-PRD.md` only for release planning or epic-order questions.
13
13
 
14
14
  **Conditional read (token discipline):** if your active task touches **Ink/TUI implementation**, also read `agent-skills/ink-tui-design/SKILL.md` after Design.md as the execution guide. If it touches **TUI rendering, theme, or visual design**, also read `.savepoint/visual-identity.md` as the visual guardrails. Otherwise skip the extra files — they are tokens you do not need.
15
15
 
@@ -30,7 +30,7 @@ If the user explicitly asks you to audit an epic, perform the audit for that epi
30
30
  Persist the audit artifacts before replying:
31
31
 
32
32
  - Ensure `.savepoint/audit/{E##-epic}/snapshot.md` exists. Create a manual snapshot once if needed.
33
- - Write the proposal bundle to `.savepoint/audit/{E##-epic}/proposals.md`.
33
+ - Write the proposal bundle to `.savepoint/audit/{release}/{E##-epic}/proposals.md`.
34
34
  - Do not stop at chat-only findings. The filesystem artifact is part of the task output.
35
35
 
36
36
  ## State → next action
@@ -43,9 +43,9 @@ The project has its PRD and Design locked but no epics defined yet.
43
43
 
44
44
  **Next action:**
45
45
 
46
- 1. Read `.savepoint/releases/v{{RELEASE_NUMBER}}/PRD.md` — the v{{RELEASE_NUMBER}} release scope (epic list lives there).
46
+ 1. Read `.savepoint/releases/{release}/{release}-PRD.md` — the release scope (epic list lives there).
47
47
  2. Help the user define the epics list and confirm priority.
48
- 3. For each epic in order, create the directory `.savepoint/releases/v{{RELEASE_NUMBER}}/epics/E##-{epic-name}/` with a `Design.md` stub.
48
+ 3. For each epic in order, create the directory `.savepoint/releases/{release}/epics/E##-{epic-name}/` with a `Design.md` stub.
49
49
  4. When epic E01 (scaffolding) is created, transition to `state: epic-design` for that epic.
50
50
 
51
51
  **Do not** start writing code. We are still in planning.
@@ -70,7 +70,7 @@ Epic Design exists but tasks are missing or not fully planned.
70
70
 
71
71
  1. Re-read the epic Design.
72
72
  2. Create or update the full epic task list — each task **independently buildable**, **objective-led**, with declared `depends_on`.
73
- 3. Each task file lives at `.savepoint/releases/v{{RELEASE_NUMBER}}/epics/{E##-epic}/tasks/TNNN-slug.md` with frontmatter:
73
+ 3. Each task file lives at `.savepoint/releases/{release}/epics/{E##-epic}/tasks/TNNN-slug.md` with frontmatter:
74
74
  ```yaml
75
75
  ---
76
76
  id: {E##-epic}/TNNN-slug
@@ -150,3 +150,4 @@ If you are not Claude Opus / Gemini 2.5 Pro / GPT-5.5 / equivalent, surface a wa
150
150
  > _"Heads up — I'm running on a lighter model. Savepoint's planning steps work best with top-tier models because the embedded prompts are detailed. I'll do my best, but consider switching the model for PRD/Design/Task-breakdown steps."_
151
151
 
152
152
  Then proceed.
153
+ proceed.