wogiflow 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 (221) hide show
  1. package/.workflow/agents/reviewer.md +81 -0
  2. package/.workflow/agents/security.md +94 -0
  3. package/.workflow/agents/story-writer.md +58 -0
  4. package/.workflow/bridges/base-bridge.js +395 -0
  5. package/.workflow/bridges/claude-bridge.js +434 -0
  6. package/.workflow/bridges/index.js +130 -0
  7. package/.workflow/lib/assumption-detector.js +481 -0
  8. package/.workflow/lib/config-substitution.js +371 -0
  9. package/.workflow/lib/failure-categories.js +478 -0
  10. package/.workflow/state/app-map.md.template +15 -0
  11. package/.workflow/state/architecture.md.template +24 -0
  12. package/.workflow/state/component-index.json.template +5 -0
  13. package/.workflow/state/decisions.md.template +15 -0
  14. package/.workflow/state/feedback-patterns.md.template +9 -0
  15. package/.workflow/state/knowledge-sync.json.template +6 -0
  16. package/.workflow/state/progress.md.template +14 -0
  17. package/.workflow/state/ready.json.template +7 -0
  18. package/.workflow/state/request-log.md.template +14 -0
  19. package/.workflow/state/session-state.json.template +11 -0
  20. package/.workflow/state/stack.md.template +33 -0
  21. package/.workflow/state/testing.md.template +36 -0
  22. package/.workflow/templates/claude-md.hbs +257 -0
  23. package/.workflow/templates/correction-report.md +67 -0
  24. package/.workflow/templates/gemini-md.hbs +52 -0
  25. package/README.md +1802 -0
  26. package/bin/flow +205 -0
  27. package/lib/index.js +33 -0
  28. package/lib/installer.js +467 -0
  29. package/lib/release-channel.js +269 -0
  30. package/lib/skill-registry.js +526 -0
  31. package/lib/upgrader.js +401 -0
  32. package/lib/utils.js +305 -0
  33. package/package.json +64 -0
  34. package/scripts/flow +985 -0
  35. package/scripts/flow-adaptive-learning.js +1259 -0
  36. package/scripts/flow-aggregate.js +488 -0
  37. package/scripts/flow-archive +133 -0
  38. package/scripts/flow-auto-context.js +1015 -0
  39. package/scripts/flow-auto-learn.js +615 -0
  40. package/scripts/flow-bridge.js +223 -0
  41. package/scripts/flow-browser-suggest.js +316 -0
  42. package/scripts/flow-bug.js +247 -0
  43. package/scripts/flow-cascade.js +711 -0
  44. package/scripts/flow-changelog +85 -0
  45. package/scripts/flow-checkpoint.js +483 -0
  46. package/scripts/flow-cli.js +403 -0
  47. package/scripts/flow-code-intelligence.js +760 -0
  48. package/scripts/flow-complexity.js +502 -0
  49. package/scripts/flow-config-set.js +152 -0
  50. package/scripts/flow-constants.js +157 -0
  51. package/scripts/flow-context +152 -0
  52. package/scripts/flow-context-init.js +482 -0
  53. package/scripts/flow-context-monitor.js +384 -0
  54. package/scripts/flow-context-scoring.js +886 -0
  55. package/scripts/flow-correct.js +458 -0
  56. package/scripts/flow-damage-control.js +985 -0
  57. package/scripts/flow-deps +101 -0
  58. package/scripts/flow-diff.js +700 -0
  59. package/scripts/flow-done +151 -0
  60. package/scripts/flow-done.js +489 -0
  61. package/scripts/flow-durable-session.js +1541 -0
  62. package/scripts/flow-entropy-monitor.js +345 -0
  63. package/scripts/flow-export-profile +349 -0
  64. package/scripts/flow-export-scanner.js +1046 -0
  65. package/scripts/flow-figma-confirm.js +400 -0
  66. package/scripts/flow-figma-extract.js +496 -0
  67. package/scripts/flow-figma-generate.js +683 -0
  68. package/scripts/flow-figma-index.js +909 -0
  69. package/scripts/flow-figma-match.js +617 -0
  70. package/scripts/flow-figma-mcp-server.js +518 -0
  71. package/scripts/flow-figma-pipeline.js +414 -0
  72. package/scripts/flow-file-ops.js +301 -0
  73. package/scripts/flow-gate-confidence.js +825 -0
  74. package/scripts/flow-guided-edit.js +659 -0
  75. package/scripts/flow-health +185 -0
  76. package/scripts/flow-health.js +413 -0
  77. package/scripts/flow-hooks.js +556 -0
  78. package/scripts/flow-http-client.js +249 -0
  79. package/scripts/flow-hybrid-detect.js +167 -0
  80. package/scripts/flow-hybrid-interactive.js +591 -0
  81. package/scripts/flow-hybrid-test.js +152 -0
  82. package/scripts/flow-import-profile +439 -0
  83. package/scripts/flow-init +253 -0
  84. package/scripts/flow-instruction-richness.js +827 -0
  85. package/scripts/flow-jira-integration.js +579 -0
  86. package/scripts/flow-knowledge-router.js +522 -0
  87. package/scripts/flow-knowledge-sync.js +589 -0
  88. package/scripts/flow-linear-integration.js +631 -0
  89. package/scripts/flow-links.js +774 -0
  90. package/scripts/flow-log-manager.js +559 -0
  91. package/scripts/flow-loop-enforcer.js +1246 -0
  92. package/scripts/flow-loop-retry-learning.js +630 -0
  93. package/scripts/flow-lsp.js +923 -0
  94. package/scripts/flow-map-index +348 -0
  95. package/scripts/flow-map-sync +201 -0
  96. package/scripts/flow-memory-blocks.js +668 -0
  97. package/scripts/flow-memory-compactor.js +350 -0
  98. package/scripts/flow-memory-db.js +1110 -0
  99. package/scripts/flow-memory-sync.js +484 -0
  100. package/scripts/flow-metrics.js +353 -0
  101. package/scripts/flow-migrate-ids.js +370 -0
  102. package/scripts/flow-model-adapter.js +802 -0
  103. package/scripts/flow-model-router.js +884 -0
  104. package/scripts/flow-models.js +1231 -0
  105. package/scripts/flow-morning.js +517 -0
  106. package/scripts/flow-multi-approach.js +660 -0
  107. package/scripts/flow-new-feature +86 -0
  108. package/scripts/flow-onboard +1042 -0
  109. package/scripts/flow-orchestrate-llm.js +459 -0
  110. package/scripts/flow-orchestrate.js +3592 -0
  111. package/scripts/flow-output.js +123 -0
  112. package/scripts/flow-parallel-detector.js +399 -0
  113. package/scripts/flow-parallel-dispatch.js +987 -0
  114. package/scripts/flow-parallel.js +428 -0
  115. package/scripts/flow-pattern-enforcer.js +600 -0
  116. package/scripts/flow-prd-manager.js +282 -0
  117. package/scripts/flow-progress.js +323 -0
  118. package/scripts/flow-project-analyzer.js +975 -0
  119. package/scripts/flow-prompt-composer.js +487 -0
  120. package/scripts/flow-providers.js +1381 -0
  121. package/scripts/flow-queue.js +308 -0
  122. package/scripts/flow-ready +82 -0
  123. package/scripts/flow-ready.js +189 -0
  124. package/scripts/flow-regression.js +396 -0
  125. package/scripts/flow-response-parser.js +450 -0
  126. package/scripts/flow-resume.js +284 -0
  127. package/scripts/flow-rules-sync.js +439 -0
  128. package/scripts/flow-run-trace.js +718 -0
  129. package/scripts/flow-safety.js +587 -0
  130. package/scripts/flow-search +104 -0
  131. package/scripts/flow-security.js +481 -0
  132. package/scripts/flow-session-end +106 -0
  133. package/scripts/flow-session-end.js +437 -0
  134. package/scripts/flow-session-state.js +671 -0
  135. package/scripts/flow-setup-hooks +216 -0
  136. package/scripts/flow-setup-hooks.js +377 -0
  137. package/scripts/flow-skill-create.js +329 -0
  138. package/scripts/flow-skill-creator.js +572 -0
  139. package/scripts/flow-skill-generator.js +1046 -0
  140. package/scripts/flow-skill-learn.js +880 -0
  141. package/scripts/flow-skill-matcher.js +578 -0
  142. package/scripts/flow-spec-generator.js +820 -0
  143. package/scripts/flow-stack-wizard.js +895 -0
  144. package/scripts/flow-standup +162 -0
  145. package/scripts/flow-start +74 -0
  146. package/scripts/flow-start.js +235 -0
  147. package/scripts/flow-status +110 -0
  148. package/scripts/flow-status.js +301 -0
  149. package/scripts/flow-step-browser.js +83 -0
  150. package/scripts/flow-step-changelog.js +217 -0
  151. package/scripts/flow-step-comments.js +306 -0
  152. package/scripts/flow-step-complexity.js +234 -0
  153. package/scripts/flow-step-coverage.js +218 -0
  154. package/scripts/flow-step-knowledge.js +193 -0
  155. package/scripts/flow-step-pr-tests.js +364 -0
  156. package/scripts/flow-step-regression.js +89 -0
  157. package/scripts/flow-step-review.js +516 -0
  158. package/scripts/flow-step-security.js +162 -0
  159. package/scripts/flow-step-silent-failures.js +290 -0
  160. package/scripts/flow-step-simplifier.js +346 -0
  161. package/scripts/flow-story +105 -0
  162. package/scripts/flow-story.js +500 -0
  163. package/scripts/flow-suspend.js +252 -0
  164. package/scripts/flow-sync-daemon.js +654 -0
  165. package/scripts/flow-task-analyzer.js +606 -0
  166. package/scripts/flow-team-dashboard.js +748 -0
  167. package/scripts/flow-team-sync.js +752 -0
  168. package/scripts/flow-team.js +977 -0
  169. package/scripts/flow-tech-options.js +528 -0
  170. package/scripts/flow-templates.js +812 -0
  171. package/scripts/flow-tiered-learning.js +728 -0
  172. package/scripts/flow-trace +204 -0
  173. package/scripts/flow-transcript-chunking.js +1106 -0
  174. package/scripts/flow-transcript-digest.js +7918 -0
  175. package/scripts/flow-transcript-language.js +465 -0
  176. package/scripts/flow-transcript-parsing.js +1085 -0
  177. package/scripts/flow-transcript-stories.js +2194 -0
  178. package/scripts/flow-update-map +224 -0
  179. package/scripts/flow-utils.js +2242 -0
  180. package/scripts/flow-verification.js +644 -0
  181. package/scripts/flow-verify.js +1177 -0
  182. package/scripts/flow-voice-input.js +638 -0
  183. package/scripts/flow-watch +168 -0
  184. package/scripts/flow-workflow-steps.js +521 -0
  185. package/scripts/flow-workflow.js +1029 -0
  186. package/scripts/flow-worktree.js +489 -0
  187. package/scripts/hooks/adapters/base-adapter.js +102 -0
  188. package/scripts/hooks/adapters/claude-code.js +359 -0
  189. package/scripts/hooks/adapters/index.js +79 -0
  190. package/scripts/hooks/core/component-check.js +341 -0
  191. package/scripts/hooks/core/index.js +35 -0
  192. package/scripts/hooks/core/loop-check.js +241 -0
  193. package/scripts/hooks/core/session-context.js +294 -0
  194. package/scripts/hooks/core/task-gate.js +177 -0
  195. package/scripts/hooks/core/validation.js +230 -0
  196. package/scripts/hooks/entry/claude-code/post-tool-use.js +65 -0
  197. package/scripts/hooks/entry/claude-code/pre-tool-use.js +89 -0
  198. package/scripts/hooks/entry/claude-code/session-end.js +87 -0
  199. package/scripts/hooks/entry/claude-code/session-start.js +46 -0
  200. package/scripts/hooks/entry/claude-code/stop.js +43 -0
  201. package/scripts/postinstall.js +139 -0
  202. package/templates/browser-test-flow.json +56 -0
  203. package/templates/bug-report.md +43 -0
  204. package/templates/component-detail.md +42 -0
  205. package/templates/component.stories.tsx +49 -0
  206. package/templates/context/constraints.md +83 -0
  207. package/templates/context/conventions.md +177 -0
  208. package/templates/context/stack.md +60 -0
  209. package/templates/correction-report.md +90 -0
  210. package/templates/feature-proposal.md +35 -0
  211. package/templates/hybrid/_base.md +254 -0
  212. package/templates/hybrid/_patterns.md +45 -0
  213. package/templates/hybrid/create-component.md +127 -0
  214. package/templates/hybrid/create-file.md +56 -0
  215. package/templates/hybrid/create-hook.md +145 -0
  216. package/templates/hybrid/create-service.md +70 -0
  217. package/templates/hybrid/fix-bug.md +33 -0
  218. package/templates/hybrid/modify-file.md +55 -0
  219. package/templates/story.md +68 -0
  220. package/templates/task.json +56 -0
  221. package/templates/trace.md +69 -0
@@ -0,0 +1,671 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Session State Manager
5
+ *
6
+ * Persists and restores session context across Claude sessions.
7
+ * Enables automatic continuity when resuming work.
8
+ *
9
+ * Key features:
10
+ * - Tracks current task across sessions
11
+ * - Remembers recently modified files
12
+ * - Stores session decisions
13
+ * - Provides resume context for fast pickup
14
+ *
15
+ * Part of v1.7.0 Context Memory Management
16
+ */
17
+
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+ const {
21
+ getConfig,
22
+ PATHS,
23
+ STATE_DIR,
24
+ colors,
25
+ color,
26
+ warn,
27
+ success,
28
+ error,
29
+ readJson,
30
+ writeJson,
31
+ fileExists,
32
+ printHeader,
33
+ withLock
34
+ } = require('./flow-utils');
35
+
36
+ // ============================================================
37
+ // Constants
38
+ // ============================================================
39
+
40
+ const SESSION_PATH = path.join(STATE_DIR, 'session-state.json');
41
+
42
+ // Default configuration
43
+ const DEFAULTS = {
44
+ enabled: true,
45
+ autoRestore: true,
46
+ maxGapHours: 24, // Consider session "resumed" if within this gap
47
+ trackFiles: true,
48
+ trackDecisions: true,
49
+ maxRecentFiles: 20,
50
+ maxRecentDecisions: 10
51
+ };
52
+
53
+ // ============================================================
54
+ // Configuration
55
+ // ============================================================
56
+
57
+ /**
58
+ * Get session state configuration
59
+ */
60
+ function getSessionStateConfig() {
61
+ const config = getConfig();
62
+ return {
63
+ ...DEFAULTS,
64
+ ...(config.sessionState || {})
65
+ };
66
+ }
67
+
68
+ // ============================================================
69
+ // Default State
70
+ // ============================================================
71
+
72
+ /**
73
+ * Default session state structure
74
+ */
75
+ function getDefaultState() {
76
+ return {
77
+ lastActive: null,
78
+ currentTask: null,
79
+ recentFiles: [],
80
+ recentDecisions: [],
81
+ contextSnapshot: {
82
+ keyFacts: [],
83
+ inProgress: null,
84
+ blockers: []
85
+ },
86
+ metrics: {
87
+ tasksCompleted: 0,
88
+ filesModified: 0,
89
+ errorsEncountered: 0,
90
+ sessionCount: 0
91
+ },
92
+ lastSessionSummary: null
93
+ };
94
+ }
95
+
96
+ // ============================================================
97
+ // Core Operations
98
+ // ============================================================
99
+
100
+ /**
101
+ * Load session state from file
102
+ * Returns default state if file doesn't exist or is invalid
103
+ */
104
+ function loadSessionState() {
105
+ if (!fileExists(SESSION_PATH)) {
106
+ return getDefaultState();
107
+ }
108
+
109
+ try {
110
+ const state = readJson(SESSION_PATH, null);
111
+ if (!state) return getDefaultState();
112
+
113
+ // Merge with defaults to handle schema evolution
114
+ return {
115
+ ...getDefaultState(),
116
+ ...state
117
+ };
118
+ } catch (parseError) {
119
+ // Log in debug mode to help diagnose issues
120
+ if (process.env.DEBUG) {
121
+ console.warn(`[DEBUG] Could not parse session state: ${parseError.message}`);
122
+ }
123
+ return getDefaultState();
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Save session state to file (with locking for concurrent access safety)
129
+ */
130
+ async function saveSessionStateAsync(updates = {}) {
131
+ return withLock(SESSION_PATH, async () => {
132
+ const current = loadSessionState();
133
+ const newState = {
134
+ ...current,
135
+ ...updates,
136
+ lastActive: new Date().toISOString()
137
+ };
138
+ writeJson(SESSION_PATH, newState);
139
+ return newState;
140
+ });
141
+ }
142
+
143
+ /**
144
+ * Save session state to file (sync version for backward compatibility)
145
+ * Note: Use saveSessionStateAsync when possible for concurrent safety
146
+ */
147
+ function saveSessionState(updates = {}) {
148
+ const current = loadSessionState();
149
+ const newState = {
150
+ ...current,
151
+ ...updates,
152
+ lastActive: new Date().toISOString()
153
+ };
154
+
155
+ writeJson(SESSION_PATH, newState);
156
+ return newState;
157
+ }
158
+
159
+ /**
160
+ * Clear session state (for fresh start)
161
+ */
162
+ function clearSession() {
163
+ if (fileExists(SESSION_PATH)) {
164
+ fs.unlinkSync(SESSION_PATH);
165
+ }
166
+ return getDefaultState();
167
+ }
168
+
169
+ // ============================================================
170
+ // Session Detection
171
+ // ============================================================
172
+
173
+ /**
174
+ * Check if this is a resumed session (within maxGapHours)
175
+ */
176
+ function isResumingSession() {
177
+ const config = getSessionStateConfig();
178
+ if (!config.enabled) return false;
179
+
180
+ const state = loadSessionState();
181
+ if (!state.lastActive) return false;
182
+
183
+ const lastActive = new Date(state.lastActive);
184
+ const hoursSince = (Date.now() - lastActive.getTime()) / (1000 * 60 * 60);
185
+
186
+ return hoursSince < config.maxGapHours;
187
+ }
188
+
189
+ /**
190
+ * Get time since last activity
191
+ */
192
+ function getTimeSinceLastActive() {
193
+ const state = loadSessionState();
194
+ if (!state.lastActive) return null;
195
+
196
+ const lastActive = new Date(state.lastActive);
197
+ const ms = Date.now() - lastActive.getTime();
198
+
199
+ const hours = Math.floor(ms / (1000 * 60 * 60));
200
+ const minutes = Math.floor((ms % (1000 * 60 * 60)) / (1000 * 60));
201
+
202
+ if (hours > 0) {
203
+ return `${hours}h ${minutes}m ago`;
204
+ }
205
+ return `${minutes}m ago`;
206
+ }
207
+
208
+ // ============================================================
209
+ // Task Tracking
210
+ // ============================================================
211
+
212
+ /**
213
+ * Track task start
214
+ */
215
+ function trackTaskStart(taskId, taskTitle, metadata = {}) {
216
+ const state = loadSessionState();
217
+ return saveSessionState({
218
+ currentTask: {
219
+ id: taskId,
220
+ title: taskTitle,
221
+ startedAt: new Date().toISOString(),
222
+ ...metadata
223
+ }
224
+ });
225
+ }
226
+
227
+ /**
228
+ * Track task completion
229
+ */
230
+ function trackTaskComplete(taskId) {
231
+ const state = loadSessionState();
232
+ const newMetrics = {
233
+ ...state.metrics,
234
+ tasksCompleted: (state.metrics?.tasksCompleted || 0) + 1
235
+ };
236
+
237
+ return saveSessionState({
238
+ currentTask: null,
239
+ metrics: newMetrics
240
+ });
241
+ }
242
+
243
+ /**
244
+ * Get current task
245
+ */
246
+ function getCurrentTask() {
247
+ const state = loadSessionState();
248
+ return state.currentTask;
249
+ }
250
+
251
+ // ============================================================
252
+ // File Tracking
253
+ // ============================================================
254
+
255
+ /**
256
+ * Track file modification
257
+ */
258
+ function trackFileModified(filePath) {
259
+ const config = getSessionStateConfig();
260
+ if (!config.trackFiles) return null;
261
+
262
+ const state = loadSessionState();
263
+ const relPath = path.relative(process.cwd(), filePath);
264
+
265
+ // Remove if already in list, add to front
266
+ const recentFiles = [
267
+ relPath,
268
+ ...state.recentFiles.filter(f => f !== relPath)
269
+ ].slice(0, config.maxRecentFiles);
270
+
271
+ const newMetrics = {
272
+ ...state.metrics,
273
+ filesModified: (state.metrics?.filesModified || 0) + 1
274
+ };
275
+
276
+ return saveSessionState({
277
+ recentFiles,
278
+ metrics: newMetrics
279
+ });
280
+ }
281
+
282
+ /**
283
+ * Get recent files
284
+ */
285
+ function getRecentFiles(limit = 10) {
286
+ const state = loadSessionState();
287
+ return state.recentFiles.slice(0, limit);
288
+ }
289
+
290
+ // ============================================================
291
+ // Decision Tracking
292
+ // ============================================================
293
+
294
+ /**
295
+ * Track a decision made during session
296
+ */
297
+ function trackDecision(decision, context = null) {
298
+ const config = getSessionStateConfig();
299
+ if (!config.trackDecisions) return null;
300
+
301
+ const state = loadSessionState();
302
+
303
+ const recentDecisions = [
304
+ {
305
+ decision,
306
+ context,
307
+ timestamp: new Date().toISOString()
308
+ },
309
+ ...state.recentDecisions
310
+ ].slice(0, config.maxRecentDecisions);
311
+
312
+ return saveSessionState({ recentDecisions });
313
+ }
314
+
315
+ /**
316
+ * Get recent decisions
317
+ */
318
+ function getRecentDecisions(limit = 5) {
319
+ const state = loadSessionState();
320
+ return state.recentDecisions.slice(0, limit);
321
+ }
322
+
323
+ // ============================================================
324
+ // Context Snapshot
325
+ // ============================================================
326
+
327
+ /**
328
+ * Update context snapshot (key facts, blockers, etc.)
329
+ */
330
+ function updateContextSnapshot(updates) {
331
+ const state = loadSessionState();
332
+ const contextSnapshot = {
333
+ ...state.contextSnapshot,
334
+ ...updates
335
+ };
336
+ return saveSessionState({ contextSnapshot });
337
+ }
338
+
339
+ /**
340
+ * Add a key fact
341
+ */
342
+ function addKeyFact(fact) {
343
+ const state = loadSessionState();
344
+ const keyFacts = [...(state.contextSnapshot?.keyFacts || [])];
345
+
346
+ if (!keyFacts.includes(fact)) {
347
+ keyFacts.push(fact);
348
+ // Keep last 10
349
+ while (keyFacts.length > 10) {
350
+ keyFacts.shift();
351
+ }
352
+ }
353
+
354
+ return updateContextSnapshot({ keyFacts });
355
+ }
356
+
357
+ /**
358
+ * Set blockers
359
+ */
360
+ function setBlockers(blockers) {
361
+ return updateContextSnapshot({ blockers });
362
+ }
363
+
364
+ // ============================================================
365
+ // Session Summary
366
+ // ============================================================
367
+
368
+ /**
369
+ * Save session summary (at session end)
370
+ */
371
+ function saveSessionSummary(summary) {
372
+ const state = loadSessionState();
373
+ const newMetrics = {
374
+ ...state.metrics,
375
+ sessionCount: (state.metrics?.sessionCount || 0) + 1
376
+ };
377
+
378
+ return saveSessionState({
379
+ lastSessionSummary: {
380
+ ...summary,
381
+ timestamp: new Date().toISOString()
382
+ },
383
+ metrics: newMetrics
384
+ });
385
+ }
386
+
387
+ // ============================================================
388
+ // Resume Context
389
+ // ============================================================
390
+
391
+ /**
392
+ * Get resume context for Claude
393
+ * Returns formatted string with key context for picking up work
394
+ */
395
+ function getResumeContext() {
396
+ const config = getSessionStateConfig();
397
+ if (!config.enabled) return null;
398
+
399
+ const state = loadSessionState();
400
+ if (!state.lastActive) return null;
401
+
402
+ const lines = [];
403
+ const timeSince = getTimeSinceLastActive();
404
+
405
+ lines.push(`**Session resume** (last active: ${timeSince})`);
406
+ lines.push('');
407
+
408
+ // Current task
409
+ if (state.currentTask) {
410
+ lines.push(`**Resuming task**: ${state.currentTask.id} - ${state.currentTask.title}`);
411
+ }
412
+
413
+ // Recent files
414
+ if (state.recentFiles.length > 0) {
415
+ const recent = state.recentFiles.slice(0, 5);
416
+ lines.push(`**Recently modified**: ${recent.join(', ')}`);
417
+ }
418
+
419
+ // Key facts
420
+ if (state.contextSnapshot?.keyFacts?.length > 0) {
421
+ lines.push(`**Key context**:`);
422
+ for (const fact of state.contextSnapshot.keyFacts.slice(0, 5)) {
423
+ lines.push(` - ${fact}`);
424
+ }
425
+ }
426
+
427
+ // Blockers
428
+ if (state.contextSnapshot?.blockers?.length > 0) {
429
+ lines.push(`**Blockers**: ${state.contextSnapshot.blockers.join(', ')}`);
430
+ }
431
+
432
+ // Last session summary
433
+ if (state.lastSessionSummary?.summary) {
434
+ lines.push('');
435
+ lines.push(`**Last session**: ${state.lastSessionSummary.summary}`);
436
+ }
437
+
438
+ // Metrics
439
+ if (state.metrics?.tasksCompleted > 0) {
440
+ lines.push('');
441
+ lines.push(color('dim', `Metrics: ${state.metrics.tasksCompleted} tasks completed, ${state.metrics.filesModified} files modified`));
442
+ }
443
+
444
+ return lines.length > 2 ? lines.join('\n') : null;
445
+ }
446
+
447
+ /**
448
+ * Check for resume and display context if resuming
449
+ * Returns true if resume context was displayed
450
+ */
451
+ function checkAndDisplayResumeContext() {
452
+ if (!isResumingSession()) {
453
+ return false;
454
+ }
455
+
456
+ const context = getResumeContext();
457
+ if (!context) {
458
+ return false;
459
+ }
460
+
461
+ printHeader('Session Resume');
462
+ console.log(context);
463
+ console.log('');
464
+
465
+ return true;
466
+ }
467
+
468
+ // ============================================================
469
+ // Error Tracking
470
+ // ============================================================
471
+
472
+ /**
473
+ * Track an error encountered
474
+ */
475
+ function trackError(errorType = 'unknown') {
476
+ const state = loadSessionState();
477
+ const newMetrics = {
478
+ ...state.metrics,
479
+ errorsEncountered: (state.metrics?.errorsEncountered || 0) + 1
480
+ };
481
+
482
+ return saveSessionState({ metrics: newMetrics });
483
+ }
484
+
485
+ // ============================================================
486
+ // CLI Interface
487
+ // ============================================================
488
+
489
+ function printUsage() {
490
+ console.log(`
491
+ Usage: flow-session-state.js [command] [args]
492
+
493
+ Commands:
494
+ show Show current session state
495
+ resume Show resume context (if resuming)
496
+ clear Clear session state
497
+ task <id> <title> Set current task
498
+ task done [id] Mark task complete
499
+ file <path> Track file modification
500
+ decision <text> Track a decision
501
+ fact <text> Add a key fact
502
+ --help Show this help
503
+
504
+ Examples:
505
+ node scripts/flow-session-state.js show
506
+ node scripts/flow-session-state.js task TASK-042 "Add login"
507
+ node scripts/flow-session-state.js fact "Using JWT for auth"
508
+ `);
509
+ }
510
+
511
+ // Main CLI handler
512
+ if (require.main === module) {
513
+ const args = process.argv.slice(2);
514
+ const command = args[0];
515
+
516
+ switch (command) {
517
+ case 'show': {
518
+ const state = loadSessionState();
519
+ printHeader('Session State');
520
+ console.log(JSON.stringify(state, null, 2));
521
+ break;
522
+ }
523
+
524
+ case 'resume': {
525
+ const displayed = checkAndDisplayResumeContext();
526
+ if (!displayed) {
527
+ console.log('No resume context available (new session)');
528
+ }
529
+ break;
530
+ }
531
+
532
+ case 'clear': {
533
+ clearSession();
534
+ success('Session state cleared');
535
+ break;
536
+ }
537
+
538
+ case 'task': {
539
+ if (args[1] === 'done') {
540
+ const taskId = args[2] || getCurrentTask()?.id;
541
+ if (taskId) {
542
+ trackTaskComplete(taskId);
543
+ success(`Task ${taskId} marked complete`);
544
+ } else {
545
+ error('No task to complete');
546
+ }
547
+ } else if (args[1] && args[2]) {
548
+ trackTaskStart(args[1], args.slice(2).join(' '));
549
+ success(`Started task: ${args[1]}`);
550
+ } else {
551
+ const task = getCurrentTask();
552
+ if (task) {
553
+ console.log(`Current task: ${task.id} - ${task.title}`);
554
+ } else {
555
+ console.log('No current task');
556
+ }
557
+ }
558
+ break;
559
+ }
560
+
561
+ case 'file': {
562
+ if (args[1]) {
563
+ trackFileModified(args[1]);
564
+ success(`Tracked file: ${args[1]}`);
565
+ } else {
566
+ const files = getRecentFiles();
567
+ console.log('Recent files:', files.join(', ') || 'none');
568
+ }
569
+ break;
570
+ }
571
+
572
+ case 'decision': {
573
+ if (args[1]) {
574
+ trackDecision(args.slice(1).join(' '));
575
+ success('Decision tracked');
576
+ } else {
577
+ const decisions = getRecentDecisions();
578
+ if (decisions.length > 0) {
579
+ console.log('Recent decisions:');
580
+ for (const d of decisions) {
581
+ console.log(` - ${d.decision}`);
582
+ }
583
+ } else {
584
+ console.log('No recent decisions');
585
+ }
586
+ }
587
+ break;
588
+ }
589
+
590
+ case 'fact': {
591
+ if (args[1]) {
592
+ addKeyFact(args.slice(1).join(' '));
593
+ success('Key fact added');
594
+ } else {
595
+ const state = loadSessionState();
596
+ const facts = state.contextSnapshot?.keyFacts || [];
597
+ if (facts.length > 0) {
598
+ console.log('Key facts:');
599
+ facts.forEach((f, i) => console.log(` ${i}. ${f}`));
600
+ } else {
601
+ console.log('No key facts');
602
+ }
603
+ }
604
+ break;
605
+ }
606
+
607
+ case '--help':
608
+ case '-h':
609
+ printUsage();
610
+ break;
611
+
612
+ default:
613
+ if (command) {
614
+ error(`Unknown command: ${command}`);
615
+ }
616
+ printUsage();
617
+ process.exit(command ? 1 : 0);
618
+ }
619
+ }
620
+
621
+ // ============================================================
622
+ // Exports
623
+ // ============================================================
624
+
625
+ module.exports = {
626
+ // Configuration
627
+ getSessionStateConfig,
628
+ DEFAULTS,
629
+
630
+ // Core operations
631
+ loadSessionState,
632
+ saveSessionState,
633
+ saveSessionStateAsync, // Concurrent-safe version
634
+ clearSession,
635
+ getDefaultState,
636
+
637
+ // Session detection
638
+ isResumingSession,
639
+ getTimeSinceLastActive,
640
+
641
+ // Task tracking
642
+ trackTaskStart,
643
+ trackTaskComplete,
644
+ getCurrentTask,
645
+
646
+ // File tracking
647
+ trackFileModified,
648
+ getRecentFiles,
649
+
650
+ // Decision tracking
651
+ trackDecision,
652
+ getRecentDecisions,
653
+
654
+ // Context snapshot
655
+ updateContextSnapshot,
656
+ addKeyFact,
657
+ setBlockers,
658
+
659
+ // Session summary
660
+ saveSessionSummary,
661
+
662
+ // Resume context
663
+ getResumeContext,
664
+ checkAndDisplayResumeContext,
665
+
666
+ // Error tracking
667
+ trackError,
668
+
669
+ // Path
670
+ SESSION_PATH
671
+ };