gsd-pi 2.24.0 → 2.26.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 (206) hide show
  1. package/README.md +13 -3
  2. package/dist/headless.js +24 -4
  3. package/dist/models-resolver.d.ts +0 -11
  4. package/dist/models-resolver.js +0 -15
  5. package/dist/resource-loader.d.ts +0 -1
  6. package/dist/resource-loader.js +0 -9
  7. package/dist/resources/GSD-WORKFLOW.md +12 -9
  8. package/dist/resources/extensions/async-jobs/index.ts +9 -1
  9. package/dist/resources/extensions/bg-shell/index.ts +3 -2
  10. package/dist/resources/extensions/bg-shell/overlay.ts +18 -17
  11. package/dist/resources/extensions/get-secrets-from-user.ts +5 -23
  12. package/dist/resources/extensions/gsd/activity-log.ts +5 -3
  13. package/dist/resources/extensions/gsd/auto-prompts.ts +14 -0
  14. package/dist/resources/extensions/gsd/auto-recovery.ts +7 -4
  15. package/dist/resources/extensions/gsd/auto-worktree.ts +132 -3
  16. package/dist/resources/extensions/gsd/auto.ts +265 -48
  17. package/dist/resources/extensions/gsd/cache.ts +3 -1
  18. package/dist/resources/extensions/gsd/doctor-proactive.ts +7 -6
  19. package/dist/resources/extensions/gsd/doctor.ts +26 -1
  20. package/dist/resources/extensions/gsd/files.ts +13 -2
  21. package/dist/resources/extensions/gsd/git-service.ts +74 -14
  22. package/dist/resources/extensions/gsd/gsd-db.ts +78 -1
  23. package/dist/resources/extensions/gsd/guided-flow.ts +54 -22
  24. package/dist/resources/extensions/gsd/index.ts +62 -8
  25. package/dist/resources/extensions/gsd/memory-extractor.ts +352 -0
  26. package/dist/resources/extensions/gsd/memory-store.ts +441 -0
  27. package/dist/resources/extensions/gsd/migrate/command.ts +2 -2
  28. package/dist/resources/extensions/gsd/migrate/writer.ts +39 -0
  29. package/dist/resources/extensions/gsd/parallel-orchestrator.ts +122 -4
  30. package/dist/resources/extensions/gsd/preferences.ts +2 -1
  31. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  32. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +4 -4
  33. package/dist/resources/extensions/gsd/prompts/discuss.md +5 -5
  34. package/dist/resources/extensions/gsd/prompts/execute-task.md +1 -1
  35. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  36. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  37. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  38. package/dist/resources/extensions/gsd/prompts/queue.md +3 -3
  39. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  40. package/dist/resources/extensions/gsd/roadmap-slices.ts +45 -1
  41. package/dist/resources/extensions/gsd/state.ts +17 -6
  42. package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +54 -0
  43. package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +58 -0
  44. package/dist/resources/extensions/gsd/tests/derive-state.test.ts +70 -0
  45. package/dist/resources/extensions/gsd/tests/doctor-proactive.test.ts +23 -3
  46. package/dist/resources/extensions/gsd/tests/git-service.test.ts +70 -4
  47. package/dist/resources/extensions/gsd/tests/gsd-db.test.ts +2 -2
  48. package/dist/resources/extensions/gsd/tests/md-importer.test.ts +2 -3
  49. package/dist/resources/extensions/gsd/tests/memory-extractor.test.ts +180 -0
  50. package/dist/resources/extensions/gsd/tests/memory-store.test.ts +345 -0
  51. package/dist/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +13 -7
  52. package/dist/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +171 -0
  53. package/dist/resources/extensions/gsd/tests/smart-entry-draft.test.ts +1 -1
  54. package/dist/resources/extensions/gsd/tests/validate-milestone.test.ts +8 -4
  55. package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +147 -2
  56. package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +88 -10
  57. package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +314 -87
  58. package/dist/resources/extensions/gsd/triage-ui.ts +1 -1
  59. package/dist/resources/extensions/gsd/types.ts +2 -0
  60. package/dist/resources/extensions/gsd/visualizer-data.ts +291 -10
  61. package/dist/resources/extensions/gsd/visualizer-overlay.ts +237 -28
  62. package/dist/resources/extensions/gsd/visualizer-views.ts +462 -48
  63. package/dist/resources/extensions/gsd/worktree.ts +9 -2
  64. package/dist/resources/extensions/search-the-web/native-search.ts +19 -5
  65. package/dist/resources/extensions/shared/path-display.ts +19 -0
  66. package/package.json +1 -6
  67. package/packages/pi-agent-core/dist/agent-loop.js +2 -0
  68. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  69. package/packages/pi-agent-core/src/agent-loop.ts +2 -0
  70. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  71. package/packages/pi-ai/dist/providers/anthropic.js +64 -0
  72. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  73. package/packages/pi-ai/dist/providers/mistral.js +3 -0
  74. package/packages/pi-ai/dist/providers/mistral.js.map +1 -1
  75. package/packages/pi-ai/dist/types.d.ts +23 -1
  76. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  77. package/packages/pi-ai/dist/types.js.map +1 -1
  78. package/packages/pi-ai/src/providers/anthropic.ts +65 -1
  79. package/packages/pi-ai/src/providers/mistral.ts +3 -0
  80. package/packages/pi-ai/src/types.ts +19 -1
  81. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +7 -0
  82. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  83. package/packages/pi-coding-agent/dist/core/agent-session.js +32 -0
  84. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  85. package/packages/pi-coding-agent/dist/core/keybindings.js +1 -1
  86. package/packages/pi-coding-agent/dist/core/keybindings.js.map +1 -1
  87. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  88. package/packages/pi-coding-agent/dist/core/lsp/client.js +12 -1
  89. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  90. package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
  91. package/packages/pi-coding-agent/dist/core/lsp/index.js +7 -0
  92. package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
  93. package/packages/pi-coding-agent/dist/core/sdk.d.ts +2 -2
  94. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  95. package/packages/pi-coding-agent/dist/core/sdk.js +8 -3
  96. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  97. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  98. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  99. package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
  100. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  101. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  102. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  103. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  104. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  105. package/packages/pi-coding-agent/dist/core/system-prompt.js +2 -1
  106. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  107. package/packages/pi-coding-agent/dist/index.d.ts +2 -1
  108. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  109. package/packages/pi-coding-agent/dist/index.js +5 -1
  110. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  111. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts +41 -3
  112. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  113. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +301 -62
  114. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  115. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  116. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +17 -0
  117. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  118. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +5 -0
  119. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  120. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +135 -30
  121. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  122. package/packages/pi-coding-agent/dist/tests/path-display.test.d.ts +8 -0
  123. package/packages/pi-coding-agent/dist/tests/path-display.test.d.ts.map +1 -0
  124. package/packages/pi-coding-agent/dist/tests/path-display.test.js +60 -0
  125. package/packages/pi-coding-agent/dist/tests/path-display.test.js.map +1 -0
  126. package/packages/pi-coding-agent/dist/utils/clipboard-image.d.ts.map +1 -1
  127. package/packages/pi-coding-agent/dist/utils/clipboard-image.js +32 -6
  128. package/packages/pi-coding-agent/dist/utils/clipboard-image.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/utils/path-display.d.ts +34 -0
  130. package/packages/pi-coding-agent/dist/utils/path-display.d.ts.map +1 -0
  131. package/packages/pi-coding-agent/dist/utils/path-display.js +36 -0
  132. package/packages/pi-coding-agent/dist/utils/path-display.js.map +1 -0
  133. package/packages/pi-coding-agent/src/core/agent-session.ts +36 -0
  134. package/packages/pi-coding-agent/src/core/keybindings.ts +1 -1
  135. package/packages/pi-coding-agent/src/core/lsp/client.ts +11 -1
  136. package/packages/pi-coding-agent/src/core/lsp/index.ts +7 -0
  137. package/packages/pi-coding-agent/src/core/sdk.ts +17 -1
  138. package/packages/pi-coding-agent/src/core/settings-manager.ts +11 -0
  139. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  140. package/packages/pi-coding-agent/src/core/system-prompt.ts +2 -1
  141. package/packages/pi-coding-agent/src/index.ts +15 -0
  142. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +347 -62
  143. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +18 -0
  144. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +124 -4
  145. package/packages/pi-coding-agent/src/tests/path-display.test.ts +85 -0
  146. package/packages/pi-coding-agent/src/utils/clipboard-image.ts +33 -6
  147. package/packages/pi-coding-agent/src/utils/path-display.ts +36 -0
  148. package/src/resources/GSD-WORKFLOW.md +12 -9
  149. package/src/resources/extensions/async-jobs/index.ts +9 -1
  150. package/src/resources/extensions/bg-shell/index.ts +3 -2
  151. package/src/resources/extensions/bg-shell/overlay.ts +18 -17
  152. package/src/resources/extensions/get-secrets-from-user.ts +5 -23
  153. package/src/resources/extensions/gsd/activity-log.ts +5 -3
  154. package/src/resources/extensions/gsd/auto-prompts.ts +14 -0
  155. package/src/resources/extensions/gsd/auto-recovery.ts +7 -4
  156. package/src/resources/extensions/gsd/auto-worktree.ts +132 -3
  157. package/src/resources/extensions/gsd/auto.ts +265 -48
  158. package/src/resources/extensions/gsd/cache.ts +3 -1
  159. package/src/resources/extensions/gsd/doctor-proactive.ts +7 -6
  160. package/src/resources/extensions/gsd/doctor.ts +26 -1
  161. package/src/resources/extensions/gsd/files.ts +13 -2
  162. package/src/resources/extensions/gsd/git-service.ts +74 -14
  163. package/src/resources/extensions/gsd/gsd-db.ts +78 -1
  164. package/src/resources/extensions/gsd/guided-flow.ts +54 -22
  165. package/src/resources/extensions/gsd/index.ts +62 -8
  166. package/src/resources/extensions/gsd/memory-extractor.ts +352 -0
  167. package/src/resources/extensions/gsd/memory-store.ts +441 -0
  168. package/src/resources/extensions/gsd/migrate/command.ts +2 -2
  169. package/src/resources/extensions/gsd/migrate/writer.ts +39 -0
  170. package/src/resources/extensions/gsd/parallel-orchestrator.ts +122 -4
  171. package/src/resources/extensions/gsd/preferences.ts +2 -1
  172. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  173. package/src/resources/extensions/gsd/prompts/discuss-headless.md +4 -4
  174. package/src/resources/extensions/gsd/prompts/discuss.md +5 -5
  175. package/src/resources/extensions/gsd/prompts/execute-task.md +1 -1
  176. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  177. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  178. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  179. package/src/resources/extensions/gsd/prompts/queue.md +3 -3
  180. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  181. package/src/resources/extensions/gsd/roadmap-slices.ts +45 -1
  182. package/src/resources/extensions/gsd/state.ts +17 -6
  183. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +54 -0
  184. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +58 -0
  185. package/src/resources/extensions/gsd/tests/derive-state.test.ts +70 -0
  186. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +23 -3
  187. package/src/resources/extensions/gsd/tests/git-service.test.ts +70 -4
  188. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +2 -2
  189. package/src/resources/extensions/gsd/tests/md-importer.test.ts +2 -3
  190. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +180 -0
  191. package/src/resources/extensions/gsd/tests/memory-store.test.ts +345 -0
  192. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +13 -7
  193. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +171 -0
  194. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +1 -1
  195. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +8 -4
  196. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +147 -2
  197. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +88 -10
  198. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +314 -87
  199. package/src/resources/extensions/gsd/triage-ui.ts +1 -1
  200. package/src/resources/extensions/gsd/types.ts +2 -0
  201. package/src/resources/extensions/gsd/visualizer-data.ts +291 -10
  202. package/src/resources/extensions/gsd/visualizer-overlay.ts +237 -28
  203. package/src/resources/extensions/gsd/visualizer-views.ts +462 -48
  204. package/src/resources/extensions/gsd/worktree.ts +9 -2
  205. package/src/resources/extensions/search-the-web/native-search.ts +19 -5
  206. package/src/resources/extensions/shared/path-display.ts +19 -0
@@ -9,6 +9,9 @@ import {
9
9
  renderAgentView,
10
10
  renderChangelogView,
11
11
  renderExportView,
12
+ renderKnowledgeView,
13
+ renderCapturesView,
14
+ renderHealthView,
12
15
  } from "../visualizer-views.js";
13
16
  import type { VisualizerData } from "../visualizer-data.js";
14
17
  import { createTestContext } from "./test-helpers.ts";
@@ -32,6 +35,8 @@ function makeVisualizerData(overrides: Partial<VisualizerData> = {}): Visualizer
32
35
  byPhase: [],
33
36
  bySlice: [],
34
37
  byModel: [],
38
+ byTier: [],
39
+ tierSavingsLine: "",
35
40
  units: [],
36
41
  criticalPath: {
37
42
  milestonePath: [],
@@ -42,6 +47,28 @@ function makeVisualizerData(overrides: Partial<VisualizerData> = {}): Visualizer
42
47
  remainingSliceCount: 0,
43
48
  agentActivity: null,
44
49
  changelog: { entries: [] },
50
+ sliceVerifications: [],
51
+ knowledge: { rules: [], patterns: [], lessons: [], exists: false },
52
+ captures: { entries: [], pendingCount: 0, totalCount: 0 },
53
+ health: {
54
+ budgetCeiling: undefined,
55
+ tokenProfile: "standard",
56
+ truncationRate: 0,
57
+ continueHereRate: 0,
58
+ tierBreakdown: [],
59
+ tierSavingsLine: "",
60
+ toolCalls: 0,
61
+ assistantMessages: 0,
62
+ userMessages: 0,
63
+ },
64
+ discussion: [],
65
+ stats: {
66
+ missingCount: 0,
67
+ missingSlices: [],
68
+ updatedCount: 0,
69
+ updatedSlices: [],
70
+ recentEntries: [],
71
+ },
45
72
  ...overrides,
46
73
  };
47
74
  }
@@ -76,29 +103,62 @@ console.log("\n=== renderProgressView ===");
76
103
  risk: "high",
77
104
  depends: ["S01"],
78
105
  tasks: [
79
- { id: "T01", title: "Dispatch Loop", done: false, active: true },
106
+ { id: "T01", title: "Dispatch Loop", done: false, active: true, estimate: "30m" },
80
107
  { id: "T02", title: "Session Mgmt", done: true, active: false },
81
108
  ],
82
109
  },
83
- {
84
- id: "S03",
85
- title: "Dashboard",
86
- done: false,
87
- active: false,
88
- risk: "medium",
89
- depends: ["S02"],
90
- tasks: [],
91
- },
92
- ],
93
- },
110
+ {
111
+ id: "S03",
112
+ title: "Dashboard",
113
+ done: false,
114
+ active: false,
115
+ risk: "medium",
116
+ depends: ["S02"],
117
+ tasks: [],
118
+ },
119
+ ],
120
+ },
121
+ {
122
+ id: "M002",
123
+ title: "Plugin Arch",
124
+ status: "pending",
125
+ dependsOn: ["M001"],
126
+ slices: [],
127
+ },
128
+ ],
129
+ sliceVerifications: [
94
130
  {
95
- id: "M002",
96
- title: "Plugin Arch",
97
- status: "pending",
98
- dependsOn: ["M001"],
99
- slices: [],
131
+ milestoneId: "M001",
132
+ sliceId: "S01",
133
+ verificationResult: "passed",
134
+ blockerDiscovered: false,
135
+ keyDecisions: [],
136
+ patternsEstablished: [],
137
+ provides: ["core-types"],
138
+ requires: [],
100
139
  },
101
140
  ],
141
+ stats: {
142
+ missingCount: 2,
143
+ missingSlices: [
144
+ { milestoneId: "M001", sliceId: "S02", title: "State Engine" },
145
+ { milestoneId: "M001", sliceId: "S03", title: "Dashboard" },
146
+ ],
147
+ updatedCount: 1,
148
+ updatedSlices: [
149
+ { milestoneId: "M001", sliceId: "S01", title: "Core Types", completedAt: "2026-03-15T14:30:00Z" },
150
+ ],
151
+ recentEntries: [
152
+ {
153
+ milestoneId: "M001",
154
+ sliceId: "S01",
155
+ title: "Core Types Infrastructure",
156
+ oneLiner: "Core structures assembled",
157
+ filesModified: [],
158
+ completedAt: "2026-03-15T14:30:00Z",
159
+ },
160
+ ],
161
+ },
102
162
  });
103
163
 
104
164
  const lines = renderProgressView(data, mockTheme, 80);
@@ -108,12 +168,82 @@ console.log("\n=== renderProgressView ===");
108
168
  assertTrue(lines.some(l => l.includes("T01")), "shows task T01 for active slice");
109
169
  assertTrue(lines.some(l => l.includes("M002")), "shows milestone M002");
110
170
  assertTrue(lines.some(l => l.includes("depends on M001")), "shows dependency note");
171
+ assertTrue(lines.some(l => l.includes("30m")), "shows task estimate");
172
+ assertTrue(lines.some(l => l.includes("Feature Snapshot")), "shows stats header");
173
+ assertTrue(lines.some(l => l.includes("Missing slices")), "shows missing slices count");
174
+ assertTrue(lines.some(l => l.includes("State Engine")), "shows missing slice preview");
175
+ assertTrue(lines.some(l => l.includes("Updated (last 7 days)")), "shows updated count");
176
+ assertTrue(lines.some(l => l.includes("Recent completions")), "shows recent completions section");
177
+ assertTrue(lines.some(l => l.includes("Core structures assembled")), "shows recent one-liner entry");
178
+ }
179
+
180
+ {
181
+ const data = makeVisualizerData({
182
+ discussion: [
183
+ {
184
+ milestoneId: "M001",
185
+ title: "First Milestone",
186
+ state: "discussed",
187
+ hasContext: true,
188
+ hasDraft: false,
189
+ lastUpdated: "2026-03-15T14:30:00Z",
190
+ },
191
+ {
192
+ milestoneId: "M002",
193
+ title: "Plugin Arch",
194
+ state: "draft",
195
+ hasContext: false,
196
+ hasDraft: true,
197
+ lastUpdated: "2026-03-16T09:00:00Z",
198
+ },
199
+ {
200
+ milestoneId: "M003",
201
+ title: "Next Batch",
202
+ state: "undiscussed",
203
+ hasContext: false,
204
+ hasDraft: false,
205
+ lastUpdated: null,
206
+ },
207
+ ],
208
+ });
209
+
210
+ const lines = renderProgressView(data, mockTheme, 80);
211
+ assertTrue(lines.some(l => l.includes("Discussion Status")), "shows discussion section");
212
+ assertTrue(lines.some(l => l.includes("Discussed: 1")), "counts discussed milestones");
213
+ assertTrue(lines.some(l => l.includes("Draft")), "shows draft badge");
214
+ assertTrue(lines.some(l => l.includes("Pending")), "shows pending badge");
215
+ }
216
+
217
+ // Verification badges
218
+ {
219
+ const data = makeVisualizerData({
220
+ milestones: [
221
+ {
222
+ id: "M001", title: "Test", status: "active", dependsOn: [],
223
+ slices: [
224
+ { id: "S01", title: "Done Slice", done: true, active: false, risk: "low", depends: [], tasks: [] },
225
+ ],
226
+ },
227
+ ],
228
+ sliceVerifications: [
229
+ {
230
+ milestoneId: "M001", sliceId: "S01",
231
+ verificationResult: "passed", blockerDiscovered: true,
232
+ keyDecisions: [], patternsEstablished: [], provides: [], requires: [],
233
+ },
234
+ ],
235
+ });
236
+
237
+ const lines = renderProgressView(data, mockTheme, 80);
238
+ // The verification badge should show check mark and warning
239
+ assertTrue(lines.some(l => l.includes("S01")), "shows slice with verification");
111
240
  }
112
241
 
113
242
  {
114
243
  const data = makeVisualizerData({ milestones: [] });
115
244
  const lines = renderProgressView(data, mockTheme, 80);
116
- assertEq(lines.length, 0, "empty milestones produce no lines");
245
+ assertTrue(lines.some(l => l.includes("Feature Snapshot")), "shows stats snapshot even when no milestones");
246
+ assertTrue(lines.some(l => l.includes("Missing slices")), "reports missing slices count");
117
247
  }
118
248
 
119
249
  // ─── Risk Heatmap ───────────────────────────────────────────────────────────
@@ -140,8 +270,6 @@ console.log("\n=== Risk Heatmap ===");
140
270
 
141
271
  const lines = renderProgressView(data, mockTheme, 80);
142
272
  assertTrue(lines.some(l => l.includes("Risk Heatmap")), "heatmap header present");
143
- assertTrue(lines.some(l => l.includes("██")), "heatmap has colored blocks");
144
- assertTrue(lines.some(l => l.includes("low") && l.includes("med") && l.includes("high")), "heatmap legend present");
145
273
  assertTrue(lines.some(l => l.includes("1 low, 1 med, 2 high")), "risk summary counts");
146
274
  assertTrue(lines.some(l => l.includes("1 high-risk not started")), "high-risk not started warning");
147
275
  }
@@ -173,12 +301,10 @@ console.log("\n=== Search/Filter ===");
173
301
  ],
174
302
  });
175
303
 
176
- // Filter by keyword "auth"
177
304
  const filtered = renderProgressView(data, mockTheme, 80, { text: "auth", field: "all" });
178
305
  assertTrue(filtered.some(l => l.includes("M001")), "filter shows matching milestone");
179
306
  assertTrue(filtered.some(l => l.includes("Filter (all): auth")), "filter indicator present");
180
307
 
181
- // Filter by risk "high"
182
308
  const riskFiltered = renderProgressView(data, mockTheme, 80, { text: "high", field: "risk" });
183
309
  assertTrue(riskFiltered.some(l => l.includes("M001")), "risk filter shows milestone with high-risk slice");
184
310
  }
@@ -214,6 +340,14 @@ console.log("\n=== renderDepsView ===");
214
340
  milestoneSlack: new Map([["M001", 0], ["M002", 0]]),
215
341
  sliceSlack: new Map([["S01", 0], ["S02", 0]]),
216
342
  },
343
+ sliceVerifications: [
344
+ {
345
+ milestoneId: "M001", sliceId: "S01",
346
+ verificationResult: "passed", blockerDiscovered: false,
347
+ keyDecisions: [], patternsEstablished: [],
348
+ provides: ["api-types"], requires: [],
349
+ },
350
+ ],
217
351
  });
218
352
 
219
353
  const lines = renderDepsView(data, mockTheme, 80);
@@ -222,6 +356,8 @@ console.log("\n=== renderDepsView ===");
222
356
  assertTrue(lines.some(l => l.includes("S01") && l.includes("S02")), "shows slice dep edge");
223
357
  assertTrue(lines.some(l => l.includes("Critical Path")), "shows critical path section");
224
358
  assertTrue(lines.some(l => l.includes("[CRITICAL]")), "shows CRITICAL badge");
359
+ assertTrue(lines.some(l => l.includes("Data Flow")), "shows data flow section");
360
+ assertTrue(lines.some(l => l.includes("api-types")), "shows provides artifact");
225
361
  }
226
362
 
227
363
  {
@@ -260,13 +396,6 @@ console.log("\n=== renderMetricsView ===");
260
396
  cost: 1.50,
261
397
  duration: 40000,
262
398
  },
263
- {
264
- phase: "planning",
265
- units: 2,
266
- tokens: { input: 400, output: 200, cacheRead: 100, cacheWrite: 50, total: 750 },
267
- cost: 1.00,
268
- duration: 20000,
269
- },
270
399
  ],
271
400
  byModel: [
272
401
  {
@@ -276,6 +405,11 @@ console.log("\n=== renderMetricsView ===");
276
405
  cost: 2.50,
277
406
  },
278
407
  ],
408
+ byTier: [
409
+ { tier: "standard", units: 3, tokens: { input: 600, output: 300, cacheRead: 100, cacheWrite: 50, total: 1050 }, cost: 1.50, downgraded: 0 },
410
+ { tier: "light", units: 2, tokens: { input: 400, output: 200, cacheRead: 100, cacheWrite: 50, total: 750 }, cost: 1.00, downgraded: 1 },
411
+ ],
412
+ tierSavingsLine: "Dynamic routing: 1/5 units downgraded (20%), cost: $1.00",
279
413
  bySlice: [
280
414
  { sliceId: "M001/S01", units: 3, tokens: { input: 600, output: 300, cacheRead: 100, cacheWrite: 50, total: 1050 }, cost: 1.50, duration: 40000 },
281
415
  { sliceId: "M001/S02", units: 2, tokens: { input: 400, output: 200, cacheRead: 100, cacheWrite: 50, total: 750 }, cost: 1.00, duration: 20000 },
@@ -288,11 +422,11 @@ console.log("\n=== renderMetricsView ===");
288
422
  assertTrue(lines.some(l => l.includes("$2.50")), "shows total cost");
289
423
  assertTrue(lines.some(l => l.includes("execution")), "shows phase name");
290
424
  assertTrue(lines.some(l => l.includes("claude-opus-4-6")), "shows model name");
291
- assertTrue(lines.some(l => l.includes("Projections")), "shows projections section");
292
- assertTrue(lines.some(l => l.includes("Avg cost/slice")), "shows avg cost per slice");
293
- assertTrue(lines.some(l => l.includes("Projected remaining")), "shows projected remaining");
294
- assertTrue(lines.some(l => l.includes("Burn rate")), "shows burn rate");
295
- assertTrue(lines.some(l => l.includes("Cost trend")), "shows sparkline");
425
+ assertTrue(lines.some(l => l.includes("By Tier")), "shows tier breakdown section");
426
+ assertTrue(lines.some(l => l.includes("standard")), "shows tier name");
427
+ assertTrue(lines.some(l => l.includes("Dynamic routing")), "shows tier savings line");
428
+ assertTrue(lines.some(l => l.includes("Tools: 15")), "shows tool call count");
429
+ assertTrue(lines.some(l => l.includes("10") && l.includes("sent")), "shows message counts");
296
430
  }
297
431
 
298
432
  {
@@ -320,32 +454,16 @@ console.log("\n=== renderTimelineView ===");
320
454
  toolCalls: 5,
321
455
  assistantMessages: 3,
322
456
  userMessages: 1,
323
- },
324
- {
325
- type: "plan-slice",
326
- id: "M001/S02",
327
- model: "claude-opus-4-6",
328
- startedAt: now - 60000,
329
- finishedAt: now - 30000,
330
- tokens: { input: 300, output: 150, cacheRead: 50, cacheWrite: 25, total: 525 },
331
- cost: 0.18,
332
- toolCalls: 2,
333
- assistantMessages: 2,
334
- userMessages: 1,
457
+ tier: "standard",
335
458
  },
336
459
  ],
337
460
  });
338
461
 
339
- // Wide terminal — Gantt view
340
- const ganttLines = renderTimelineView(data, mockTheme, 120);
341
- assertTrue(ganttLines.length >= 2, "gantt view produces lines for each unit");
342
-
343
- // Narrow terminal — list view
344
462
  const listLines = renderTimelineView(data, mockTheme, 80);
345
- assertTrue(listLines.length >= 2, "list view produces lines for each unit");
463
+ assertTrue(listLines.length >= 1, "list view produces lines");
346
464
  assertTrue(listLines.some(l => l.includes("execute-task")), "shows unit type");
347
- assertTrue(listLines.some(l => l.includes("M001/S01/T01")), "shows unit id");
348
- assertTrue(listLines.some(l => l.includes("$0.42")), "shows unit cost");
465
+ assertTrue(listLines.some(l => l.includes("[standard]")), "shows tier in timeline");
466
+ assertTrue(listLines.some(l => l.includes("opus-4-6")), "shows shortened model");
349
467
  }
350
468
 
351
469
  {
@@ -379,15 +497,21 @@ console.log("\n=== renderAgentView ===");
379
497
  cost: 0.12, toolCalls: 5, assistantMessages: 3, userMessages: 1,
380
498
  },
381
499
  ],
500
+ health: {
501
+ budgetCeiling: 10, tokenProfile: "standard",
502
+ truncationRate: 15.5, continueHereRate: 5.0,
503
+ tierBreakdown: [], tierSavingsLine: "",
504
+ toolCalls: 20, assistantMessages: 15, userMessages: 8,
505
+ },
506
+ captures: { entries: [], pendingCount: 3, totalCount: 5 },
382
507
  });
383
508
 
384
509
  const lines = renderAgentView(data, mockTheme, 80);
385
510
  assertTrue(lines.length > 0, "agent view produces output");
386
511
  assertTrue(lines.some(l => l.includes("ACTIVE")), "shows active status");
387
- assertTrue(lines.some(l => l.includes("M001/S02/T03")), "shows current unit");
388
- assertTrue(lines.some(l => l.includes("8/15")), "shows progress fraction");
389
- assertTrue(lines.some(l => l.includes("2.4 units/hr")), "shows completion rate");
390
- assertTrue(lines.some(l => l.includes("$1.23")), "shows session cost");
512
+ assertTrue(lines.some(l => l.includes("Pressure")), "shows pressure section");
513
+ assertTrue(lines.some(l => l.includes("15.5%")), "shows truncation rate");
514
+ assertTrue(lines.some(l => l.includes("Pending captures: 3")), "shows pending captures");
391
515
  }
392
516
 
393
517
  {
@@ -396,25 +520,6 @@ console.log("\n=== renderAgentView ===");
396
520
  assertTrue(lines.some(l => l.includes("No agent activity")), "shows no-activity message");
397
521
  }
398
522
 
399
- {
400
- const data = makeVisualizerData({
401
- agentActivity: {
402
- currentUnit: null,
403
- elapsed: 0,
404
- completedUnits: 5,
405
- totalSlices: 10,
406
- completionRate: 1.5,
407
- active: false,
408
- sessionCost: 0.50,
409
- sessionTokens: 20000,
410
- },
411
- });
412
-
413
- const lines = renderAgentView(data, mockTheme, 80);
414
- assertTrue(lines.some(l => l.includes("IDLE")), "shows idle status");
415
- assertTrue(lines.some(l => l.includes("Not in auto mode")), "shows not-in-auto message");
416
- }
417
-
418
523
  // ─── renderChangelogView ────────────────────────────────────────────────────
419
524
 
420
525
  console.log("\n=== renderChangelogView ===");
@@ -430,21 +535,28 @@ console.log("\n=== renderChangelogView ===");
430
535
  oneLiner: "Added JWT-based auth with refresh token rotation",
431
536
  filesModified: [
432
537
  { path: "src/auth/jwt.ts", description: "JWT token generation and validation" },
433
- { path: "src/auth/middleware.ts", description: "Express middleware for auth checks" },
434
538
  ],
435
539
  completedAt: "2026-03-15T14:30:00Z",
436
540
  },
437
541
  ],
438
542
  },
543
+ sliceVerifications: [
544
+ {
545
+ milestoneId: "M001", sliceId: "S01",
546
+ verificationResult: "passed", blockerDiscovered: false,
547
+ keyDecisions: ["Use RS256 for JWT signing"],
548
+ patternsEstablished: ["Repository pattern for data access"],
549
+ provides: [], requires: [],
550
+ },
551
+ ],
439
552
  });
440
553
 
441
554
  const lines = renderChangelogView(data, mockTheme, 80);
442
- assertTrue(lines.length > 0, "changelog view produces output");
443
555
  assertTrue(lines.some(l => l.includes("M001/S01")), "shows slice reference");
444
- assertTrue(lines.some(l => l.includes("Core Authentication Setup")), "shows entry title");
445
- assertTrue(lines.some(l => l.includes("JWT-based auth")), "shows one-liner");
446
- assertTrue(lines.some(l => l.includes("src/auth/jwt.ts")), "shows modified file");
447
- assertTrue(lines.some(l => l.includes("2026-03-15")), "shows completed date");
556
+ assertTrue(lines.some(l => l.includes("Decisions:")), "shows decisions section");
557
+ assertTrue(lines.some(l => l.includes("RS256")), "shows decision content");
558
+ assertTrue(lines.some(l => l.includes("Patterns:")), "shows patterns section");
559
+ assertTrue(lines.some(l => l.includes("Repository pattern")), "shows pattern content");
448
560
  }
449
561
 
450
562
  {
@@ -466,11 +578,126 @@ console.log("\n=== renderExportView ===");
466
578
  assertTrue(lines.some(l => l.includes("[s]")), "shows snapshot option");
467
579
  }
468
580
 
581
+ // ─── renderKnowledgeView ────────────────────────────────────────────────────
582
+
583
+ console.log("\n=== renderKnowledgeView ===");
584
+
469
585
  {
470
- const data = makeVisualizerData();
471
- const lines = renderExportView(data, mockTheme, 80, "/tmp/export-2026.md");
472
- assertTrue(lines.some(l => l.includes("Last export:")), "shows last export path");
473
- assertTrue(lines.some(l => l.includes("/tmp/export-2026.md")), "shows specific export path");
586
+ const data = makeVisualizerData({
587
+ knowledge: {
588
+ exists: true,
589
+ rules: [{ id: "K001", scope: "global", content: "Always use transactions" }],
590
+ patterns: [{ id: "P001", content: "Repository pattern for DB access" }],
591
+ lessons: [{ id: "L001", content: "Cache invalidation needs TTL" }],
592
+ },
593
+ });
594
+
595
+ const lines = renderKnowledgeView(data, mockTheme, 80);
596
+ assertTrue(lines.some(l => l.includes("Rules")), "shows rules section");
597
+ assertTrue(lines.some(l => l.includes("K001")), "shows rule ID");
598
+ assertTrue(lines.some(l => l.includes("Always use transactions")), "shows rule content");
599
+ assertTrue(lines.some(l => l.includes("Patterns")), "shows patterns section");
600
+ assertTrue(lines.some(l => l.includes("P001")), "shows pattern ID");
601
+ assertTrue(lines.some(l => l.includes("Lessons Learned")), "shows lessons section");
602
+ assertTrue(lines.some(l => l.includes("L001")), "shows lesson ID");
603
+ }
604
+
605
+ {
606
+ const data = makeVisualizerData({
607
+ knowledge: { exists: false, rules: [], patterns: [], lessons: [] },
608
+ });
609
+ const lines = renderKnowledgeView(data, mockTheme, 80);
610
+ assertTrue(lines.some(l => l.includes("No KNOWLEDGE.md found")), "shows no-knowledge message");
611
+ }
612
+
613
+ // ─── renderCapturesView ─────────────────────────────────────────────────────
614
+
615
+ console.log("\n=== renderCapturesView ===");
616
+
617
+ {
618
+ const data = makeVisualizerData({
619
+ captures: {
620
+ entries: [
621
+ { id: "CAP-abc123", text: "Need to add error handling", timestamp: "2026-03-15T10:00:00Z", status: "pending", classification: "inject" },
622
+ { id: "CAP-def456", text: "Consider caching layer", timestamp: "2026-03-15T11:00:00Z", status: "triaged", classification: "defer" },
623
+ { id: "CAP-ghi789", text: "Fixed typo in config", timestamp: "2026-03-15T12:00:00Z", status: "resolved", classification: "quick-task" },
624
+ ],
625
+ pendingCount: 1,
626
+ totalCount: 3,
627
+ },
628
+ });
629
+
630
+ const lines = renderCapturesView(data, mockTheme, 80);
631
+ assertTrue(lines.some(l => l.includes("3") && l.includes("total")), "shows total count");
632
+ assertTrue(lines.some(l => l.includes("1") && l.includes("pending")), "shows pending count");
633
+ assertTrue(lines.some(l => l.includes("CAP-abc123")), "shows capture ID");
634
+ assertTrue(lines.some(l => l.includes("(inject)")), "shows classification badge");
635
+ assertTrue(lines.some(l => l.includes("[pending]")), "shows status badge");
636
+ }
637
+
638
+ {
639
+ const data = makeVisualizerData({
640
+ captures: { entries: [], pendingCount: 0, totalCount: 0 },
641
+ });
642
+ const lines = renderCapturesView(data, mockTheme, 80);
643
+ assertTrue(lines.some(l => l.includes("No captures recorded")), "shows empty state");
644
+ }
645
+
646
+ // ─── renderHealthView ───────────────────────────────────────────────────────
647
+
648
+ console.log("\n=== renderHealthView ===");
649
+
650
+ {
651
+ const data = makeVisualizerData({
652
+ totals: {
653
+ units: 10, tokens: { input: 5000, output: 2000, cacheRead: 1000, cacheWrite: 500, total: 8500 },
654
+ cost: 5.00, duration: 120000, toolCalls: 50,
655
+ assistantMessages: 30, userMessages: 15,
656
+ totalTruncationSections: 3, continueHereFiredCount: 1,
657
+ },
658
+ health: {
659
+ budgetCeiling: 20.00,
660
+ tokenProfile: "standard",
661
+ truncationRate: 30.0,
662
+ continueHereRate: 10.0,
663
+ tierBreakdown: [
664
+ { tier: "standard", units: 7, tokens: { input: 3500, output: 1400, cacheRead: 700, cacheWrite: 350, total: 5950 }, cost: 3.50, downgraded: 0 },
665
+ { tier: "light", units: 3, tokens: { input: 1500, output: 600, cacheRead: 300, cacheWrite: 150, total: 2550 }, cost: 1.50, downgraded: 2 },
666
+ ],
667
+ tierSavingsLine: "Dynamic routing: 2/10 units downgraded (20%), cost: $1.50",
668
+ toolCalls: 50,
669
+ assistantMessages: 30,
670
+ userMessages: 15,
671
+ },
672
+ });
673
+
674
+ const lines = renderHealthView(data, mockTheme, 80);
675
+ assertTrue(lines.some(l => l.includes("Budget")), "shows budget section");
676
+ assertTrue(lines.some(l => l.includes("Ceiling")), "shows budget ceiling");
677
+ assertTrue(lines.some(l => l.includes("$20.00")), "shows ceiling amount");
678
+ assertTrue(lines.some(l => l.includes("Pressure")), "shows pressure section");
679
+ assertTrue(lines.some(l => l.includes("30.0%")), "shows truncation rate");
680
+ assertTrue(lines.some(l => l.includes("Routing")), "shows routing section");
681
+ assertTrue(lines.some(l => l.includes("standard")), "shows tier name");
682
+ assertTrue(lines.some(l => l.includes("2 downgraded")), "shows downgraded count");
683
+ assertTrue(lines.some(l => l.includes("Dynamic routing")), "shows savings line");
684
+ assertTrue(lines.some(l => l.includes("Session")), "shows session section");
685
+ assertTrue(lines.some(l => l.includes("Tool calls: 50")), "shows tool calls");
686
+ }
687
+
688
+ {
689
+ const data = makeVisualizerData({
690
+ health: {
691
+ budgetCeiling: undefined, tokenProfile: "compact",
692
+ truncationRate: 0, continueHereRate: 0,
693
+ tierBreakdown: [], tierSavingsLine: "",
694
+ toolCalls: 0, assistantMessages: 0, userMessages: 0,
695
+ },
696
+ });
697
+
698
+ const lines = renderHealthView(data, mockTheme, 80);
699
+ assertTrue(lines.some(l => l.includes("No budget ceiling set")), "shows no-ceiling message");
700
+ assertTrue(lines.some(l => l.includes("compact")), "shows token profile");
474
701
  }
475
702
 
476
703
  // ─── Report ─────────────────────────────────────────────────────────────────
@@ -135,7 +135,7 @@ export async function showTriageConfirmation(
135
135
  recommended: cls === proposed,
136
136
  }));
137
137
 
138
- const choice = await showNextAction(ctx as any, {
138
+ const choice = await showNextAction(ctx, {
139
139
  title: `Triage: ${result.captureId}`,
140
140
  summary,
141
141
  actions,
@@ -265,6 +265,8 @@ export interface PhaseSkipPreferences {
265
265
  skip_reassess?: boolean;
266
266
  skip_slice_research?: boolean;
267
267
  skip_milestone_validation?: boolean;
268
+ /** When true, auto-mode pauses before each slice for discussion (#789). */
269
+ require_slice_discussion?: boolean;
268
270
  }
269
271
 
270
272
  export interface NotificationPreferences {