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,517 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Morning Briefing Command
5
+ *
6
+ * Generates a "where am I?" briefing for starting your day.
7
+ * Pulls from session state, ready tasks, request log, progress, and git.
8
+ *
9
+ * Usage:
10
+ * flow morning Human-readable briefing
11
+ * flow morning --json JSON output for programmatic access
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const { execSync } = require('child_process');
17
+ const {
18
+ PATHS,
19
+ fileExists,
20
+ readJson,
21
+ readFile,
22
+ getConfig,
23
+ getConfigValue,
24
+ getReadyData,
25
+ getGitStatus,
26
+ isGitRepo,
27
+ parseFlags,
28
+ outputJson,
29
+ color,
30
+ printHeader,
31
+ printSection
32
+ } = require('./flow-utils');
33
+
34
+ // Use centralized session state module
35
+ const { loadSessionState } = require('./flow-session-state');
36
+
37
+ /**
38
+ * Priority order for sorting (P0 highest, P4 lowest)
39
+ */
40
+ const PRIORITY_ORDER = { P0: 0, P1: 1, P2: 2, P3: 3, P4: 4 };
41
+
42
+ /**
43
+ * Calculate hours since last active
44
+ */
45
+ function getHoursSince(timestamp) {
46
+ if (!timestamp) return null;
47
+ const lastActive = new Date(timestamp);
48
+ const now = new Date();
49
+ const diffMs = now - lastActive;
50
+ return Math.round(diffMs / (1000 * 60 * 60) * 10) / 10; // 1 decimal
51
+ }
52
+
53
+ /**
54
+ * Format a timestamp for display
55
+ */
56
+ function formatTimestamp(timestamp) {
57
+ if (!timestamp) return 'Unknown';
58
+ const date = new Date(timestamp);
59
+ return date.toLocaleString('en-US', {
60
+ weekday: 'short',
61
+ month: 'short',
62
+ day: 'numeric',
63
+ hour: '2-digit',
64
+ minute: '2-digit'
65
+ });
66
+ }
67
+
68
+ /**
69
+ * Get session state (delegates to centralized module)
70
+ */
71
+ function getSessionState() {
72
+ return loadSessionState();
73
+ }
74
+
75
+ /**
76
+ * Get progress data from progress.md
77
+ */
78
+ function getProgressData() {
79
+ const progressPath = PATHS.progress;
80
+ if (!fileExists(progressPath)) {
81
+ return { keyFacts: [], lastSession: null, inProgress: null, next: null, blockers: [] };
82
+ }
83
+
84
+ const content = readFile(progressPath, '');
85
+ const result = {
86
+ keyFacts: [],
87
+ lastSession: null,
88
+ inProgress: null,
89
+ next: null,
90
+ blockers: []
91
+ };
92
+
93
+ // Extract memory blocks JSON
94
+ const memoryMatch = content.match(/<!-- MEMORY-BLOCKS-START -->\s*```json\s*([\s\S]*?)```\s*<!-- MEMORY-BLOCKS-END -->/);
95
+ if (memoryMatch) {
96
+ try {
97
+ const memoryData = JSON.parse(memoryMatch[1]);
98
+ result.keyFacts = memoryData.keyFacts || [];
99
+ if (memoryData.sessionContext) {
100
+ result.blockers = memoryData.sessionContext.blockers || [];
101
+ }
102
+ } catch {
103
+ // Ignore parse errors
104
+ }
105
+ }
106
+
107
+ // Extract sections
108
+ const lastSessionMatch = content.match(/## Last Session\s*\n([\s\S]*?)(?=\n## |$)/);
109
+ if (lastSessionMatch) {
110
+ const lines = lastSessionMatch[1].trim().split('\n').filter(l => l.startsWith('- '));
111
+ result.lastSession = lines.map(l => l.replace(/^- /, '').trim()).filter(l => l && !l.startsWith('['));
112
+ }
113
+
114
+ const inProgressMatch = content.match(/## In Progress\s*\n([\s\S]*?)(?=\n## |$)/);
115
+ if (inProgressMatch) {
116
+ const lines = inProgressMatch[1].trim().split('\n').filter(l => l.startsWith('- '));
117
+ result.inProgress = lines.map(l => l.replace(/^- /, '').trim()).filter(l => l && !l.startsWith('['));
118
+ }
119
+
120
+ const nextMatch = content.match(/## Next\s*\n([\s\S]*?)(?=\n## |$)/);
121
+ if (nextMatch) {
122
+ const lines = nextMatch[1].trim().split('\n').filter(l => l.startsWith('- '));
123
+ result.next = lines.map(l => l.replace(/^- /, '').trim()).filter(l => l && !l.startsWith('['));
124
+ }
125
+
126
+ const blockersMatch = content.match(/## Blockers\s*\n([\s\S]*?)(?=\n## |$)/);
127
+ if (blockersMatch) {
128
+ const lines = blockersMatch[1].trim().split('\n').filter(l => l.startsWith('- '));
129
+ result.blockers = lines.map(l => l.replace(/^- /, '').trim()).filter(l => l && l !== 'None');
130
+ }
131
+
132
+ return result;
133
+ }
134
+
135
+ /**
136
+ * Get git changes since last session
137
+ */
138
+ function getGitChangesSinceSession(lastActiveTimestamp) {
139
+ if (!isGitRepo() || !lastActiveTimestamp) {
140
+ return { commits: 0, commitDetails: [], newBugs: 0, filesChanged: [] };
141
+ }
142
+
143
+ try {
144
+ // Use ISO format for git --since (git understands ISO 8601)
145
+ const sinceDate = new Date(lastActiveTimestamp);
146
+ const since = sinceDate.toISOString();
147
+
148
+ // Get commits since last session
149
+ const logCmd = `git log --oneline --since="${since}" --all 2>/dev/null || echo ""`;
150
+ const logOutput = execSync(logCmd, { encoding: 'utf-8', cwd: PATHS.root }).trim();
151
+ const commits = logOutput ? logOutput.split('\n').filter(Boolean) : [];
152
+
153
+ // Get files changed since last session using --since with diff
154
+ // Note: git diff doesn't support --since, so we use git log instead
155
+ let filesChanged = [];
156
+ try {
157
+ const diffCmd = `git log --name-only --since="${since}" --format="" --all 2>/dev/null || echo ""`;
158
+ const diffOutput = execSync(diffCmd, { encoding: 'utf-8', cwd: PATHS.root }).trim();
159
+ // Deduplicate file list
160
+ filesChanged = [...new Set(diffOutput.split('\n').filter(Boolean))];
161
+ } catch {
162
+ // Git command may fail on some systems
163
+ }
164
+
165
+ // Check for new bugs filed
166
+ const bugsDir = PATHS.bugs;
167
+ let newBugs = 0;
168
+ if (fileExists(bugsDir)) {
169
+ const files = fs.readdirSync(bugsDir);
170
+ for (const file of files) {
171
+ if (!file.endsWith('.md')) continue;
172
+ const filePath = path.join(bugsDir, file);
173
+ const stat = fs.statSync(filePath);
174
+ if (new Date(stat.mtime) > new Date(lastActiveTimestamp)) {
175
+ newBugs++;
176
+ }
177
+ }
178
+ }
179
+
180
+ return {
181
+ commits: commits.length,
182
+ commitDetails: commits.slice(0, 5), // Last 5 commits
183
+ newBugs,
184
+ filesChanged: filesChanged.slice(0, 10) // Limit to 10
185
+ };
186
+ } catch {
187
+ return { commits: 0, commitDetails: [], newBugs: 0, filesChanged: [] };
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Get recommended tasks sorted by priority
193
+ */
194
+ function getRecommendedTasks(limit = 3) {
195
+ const data = getReadyData();
196
+ const tasks = [];
197
+
198
+ // Add in-progress tasks first (highest priority - resume what you started)
199
+ const inProgress = data.inProgress || [];
200
+ for (const task of inProgress) {
201
+ const t = typeof task === 'object' ? task : { id: task, title: task };
202
+ tasks.push({
203
+ ...t,
204
+ priority: t.priority || 'P0', // In-progress always top priority
205
+ status: 'in_progress',
206
+ recommendation: 'Continue where you left off'
207
+ });
208
+ }
209
+
210
+ // Add ready tasks sorted by priority
211
+ const ready = data.ready || [];
212
+ const sortedReady = [...ready].sort((a, b) => {
213
+ const aPriority = typeof a === 'object' ? (a.priority || 'P2') : 'P2';
214
+ const bPriority = typeof b === 'object' ? (b.priority || 'P2') : 'P2';
215
+ const aOrder = PRIORITY_ORDER[aPriority] ?? 2;
216
+ const bOrder = PRIORITY_ORDER[bPriority] ?? 2;
217
+ return aOrder - bOrder;
218
+ });
219
+
220
+ for (const task of sortedReady) {
221
+ const t = typeof task === 'object' ? task : { id: task, title: task };
222
+ tasks.push({
223
+ ...t,
224
+ priority: t.priority || 'P2',
225
+ status: 'ready',
226
+ recommendation: null
227
+ });
228
+ }
229
+
230
+ return tasks.slice(0, limit);
231
+ }
232
+
233
+ /**
234
+ * Generate a suggested prompt based on current state
235
+ */
236
+ function generateSuggestedPrompt(briefingData) {
237
+ const { currentTask, keyContext, recommendedTasks, progress } = briefingData;
238
+
239
+ let prompt = '';
240
+
241
+ // If there's a task in progress
242
+ if (currentTask) {
243
+ prompt = `Continue implementing ${currentTask.id}${currentTask.title ? `: ${currentTask.title}` : ''}.`;
244
+
245
+ if (keyContext && keyContext.length > 0) {
246
+ prompt += '\n\nContext:\n';
247
+ for (const fact of keyContext.slice(0, 3)) {
248
+ prompt += `- ${fact}\n`;
249
+ }
250
+ }
251
+
252
+ if (currentTask.files && currentTask.files.length > 0) {
253
+ prompt += '\nFiles to review:\n';
254
+ for (const file of currentTask.files.slice(0, 3)) {
255
+ prompt += `- ${file}\n`;
256
+ }
257
+ }
258
+
259
+ if (progress && progress.inProgress && progress.inProgress.length > 0) {
260
+ prompt += '\nCurrent state:\n';
261
+ for (const item of progress.inProgress.slice(0, 2)) {
262
+ prompt += `- ${item}\n`;
263
+ }
264
+ }
265
+
266
+ return prompt.trim();
267
+ }
268
+
269
+ // No task in progress - suggest starting the top recommended task
270
+ if (recommendedTasks && recommendedTasks.length > 0) {
271
+ const topTask = recommendedTasks[0];
272
+ prompt = `Start working on ${topTask.id}${topTask.title ? `: ${topTask.title}` : ''}.`;
273
+
274
+ if (topTask.priority && topTask.priority !== 'P2') {
275
+ prompt += `\n\nPriority: ${topTask.priority}`;
276
+ }
277
+
278
+ return prompt.trim();
279
+ }
280
+
281
+ // No tasks at all
282
+ return 'No tasks in queue. Create a new task with: flow story "Your task title"';
283
+ }
284
+
285
+ /**
286
+ * Collect all briefing data
287
+ */
288
+ function collectBriefingData() {
289
+ const config = getConfig();
290
+ const morningConfig = config.morningBriefing || {};
291
+
292
+ const sessionState = getSessionState();
293
+ const progressData = getProgressData();
294
+ const gitStatus = getGitStatus();
295
+
296
+ const hoursAgo = getHoursSince(sessionState.lastActive);
297
+ const gitChanges = morningConfig.showChanges !== false
298
+ ? getGitChangesSinceSession(sessionState.lastActive)
299
+ : { commits: 0, commitDetails: [], newBugs: 0, filesChanged: [] };
300
+
301
+ // Determine current task
302
+ let currentTask = null;
303
+ if (sessionState.currentTask) {
304
+ currentTask = typeof sessionState.currentTask === 'object'
305
+ ? sessionState.currentTask
306
+ : { id: sessionState.currentTask };
307
+ currentTask.files = sessionState.recentFiles || [];
308
+ }
309
+
310
+ // Get key context
311
+ const keyContext = [];
312
+ if (sessionState.contextSnapshot && sessionState.contextSnapshot.keyFacts) {
313
+ keyContext.push(...sessionState.contextSnapshot.keyFacts);
314
+ }
315
+ if (progressData.keyFacts) {
316
+ keyContext.push(...progressData.keyFacts);
317
+ }
318
+ // Dedupe
319
+ const uniqueContext = [...new Set(keyContext)];
320
+
321
+ // Get blockers
322
+ const blockers = [];
323
+ if (sessionState.contextSnapshot && sessionState.contextSnapshot.blockers) {
324
+ blockers.push(...sessionState.contextSnapshot.blockers);
325
+ }
326
+ if (progressData.blockers) {
327
+ blockers.push(...progressData.blockers);
328
+ }
329
+ // Dedupe
330
+ const uniqueBlockers = [...new Set(blockers)];
331
+
332
+ // Get recommended tasks
333
+ const maxTasks = morningConfig.showRecommendedTasks || 3;
334
+ const recommendedTasks = getRecommendedTasks(maxTasks);
335
+
336
+ const briefing = {
337
+ lastActive: sessionState.lastActive,
338
+ lastActiveFormatted: formatTimestamp(sessionState.lastActive),
339
+ hoursAgo,
340
+ currentTask,
341
+ keyContext: uniqueContext.slice(0, 5),
342
+ blockers: uniqueBlockers,
343
+ changesSinceLastSession: gitChanges,
344
+ recommendedTasks,
345
+ git: gitStatus,
346
+ progress: progressData,
347
+ metrics: sessionState.metrics,
348
+ suggestedPrompt: null // Will be generated if enabled
349
+ };
350
+
351
+ // Generate suggested prompt if enabled
352
+ if (morningConfig.generatePrompt !== false) {
353
+ briefing.suggestedPrompt = generateSuggestedPrompt(briefing);
354
+ }
355
+
356
+ return briefing;
357
+ }
358
+
359
+ /**
360
+ * Print human-readable briefing
361
+ */
362
+ function printBriefing(briefing) {
363
+ const config = getConfig();
364
+ const morningConfig = config.morningBriefing || {};
365
+
366
+ printHeader('MORNING BRIEFING');
367
+
368
+ // Last active
369
+ if (morningConfig.showLastSession !== false && briefing.lastActive) {
370
+ const hoursStr = briefing.hoursAgo !== null
371
+ ? ` (${briefing.hoursAgo} hours ago)`
372
+ : '';
373
+ console.log(`${color('yellow', 'Last active:')} ${briefing.lastActiveFormatted}${hoursStr}`);
374
+ console.log('');
375
+ }
376
+
377
+ // Current task (where you left off)
378
+ if (briefing.currentTask) {
379
+ printSection('WHERE YOU LEFT OFF');
380
+ const task = briefing.currentTask;
381
+ console.log(` Task: ${color('cyan', task.id)}${task.title ? ` - ${task.title}` : ''}`);
382
+ if (task.progress) {
383
+ console.log(` Progress: ${task.progress}`);
384
+ }
385
+ console.log(` Status: ${color('yellow', 'in_progress')}`);
386
+ if (task.files && task.files.length > 0) {
387
+ console.log(` Files: ${task.files.slice(0, 3).join(', ')}${task.files.length > 3 ? '...' : ''}`);
388
+ }
389
+ console.log('');
390
+ }
391
+
392
+ // Key context
393
+ if (morningConfig.showKeyContext !== false && briefing.keyContext.length > 0) {
394
+ printSection('KEY CONTEXT');
395
+ for (const fact of briefing.keyContext) {
396
+ console.log(` ${color('dim', '\u2022')} ${fact}`);
397
+ }
398
+ console.log('');
399
+ }
400
+
401
+ // Blockers
402
+ if (morningConfig.showBlockers !== false && briefing.blockers.length > 0) {
403
+ printSection('BLOCKERS');
404
+ for (const blocker of briefing.blockers) {
405
+ console.log(` ${color('red', '\u2022')} ${blocker}`);
406
+ }
407
+ console.log('');
408
+ }
409
+
410
+ // Changes since last session
411
+ if (morningConfig.showChanges !== false) {
412
+ const changes = briefing.changesSinceLastSession;
413
+ if (changes.commits > 0 || changes.newBugs > 0) {
414
+ printSection('CHANGES SINCE LAST SESSION');
415
+ if (changes.commits > 0) {
416
+ console.log(` ${color('cyan', '\u2022')} ${changes.commits} new commit${changes.commits !== 1 ? 's' : ''}`);
417
+ for (const commit of changes.commitDetails.slice(0, 3)) {
418
+ console.log(` ${color('dim', commit)}`);
419
+ }
420
+ }
421
+ if (changes.newBugs > 0) {
422
+ console.log(` ${color('yellow', '\u2022')} ${changes.newBugs} new bug${changes.newBugs !== 1 ? 's' : ''} filed`);
423
+ }
424
+ console.log('');
425
+ }
426
+ }
427
+
428
+ // Recommended tasks
429
+ if (briefing.recommendedTasks.length > 0) {
430
+ printSection('RECOMMENDED NEXT');
431
+ for (let i = 0; i < briefing.recommendedTasks.length; i++) {
432
+ const task = briefing.recommendedTasks[i];
433
+ const priority = task.priority || 'P2';
434
+ const priorityColor = priority === 'P0' ? 'red' : priority === 'P1' ? 'yellow' : 'dim';
435
+ const status = task.status === 'in_progress' ? color('yellow', ' (in progress)') : '';
436
+ console.log(` ${i + 1}. ${color(priorityColor, `[${priority}]`)} ${task.id}: ${task.title || 'No title'}${status}`);
437
+ if (task.recommendation) {
438
+ console.log(` ${color('dim', task.recommendation)}`);
439
+ }
440
+ }
441
+ console.log('');
442
+ }
443
+
444
+ // Git status
445
+ if (briefing.git.isRepo && briefing.git.uncommitted > 0) {
446
+ printSection('GIT STATUS');
447
+ console.log(` ${color('yellow', '\u26a0')} ${briefing.git.uncommitted} uncommitted file${briefing.git.uncommitted !== 1 ? 's' : ''}`);
448
+ console.log('');
449
+ }
450
+
451
+ // Suggested prompt
452
+ if (morningConfig.generatePrompt !== false && briefing.suggestedPrompt) {
453
+ printSection('SUGGESTED PROMPT');
454
+ console.log(color('dim', ' \u2500'.repeat(40)));
455
+ const lines = briefing.suggestedPrompt.split('\n');
456
+ for (const line of lines) {
457
+ console.log(` ${line}`);
458
+ }
459
+ console.log(color('dim', ' \u2500'.repeat(40)));
460
+ console.log('');
461
+ }
462
+
463
+ console.log(color('cyan', '\u2550'.repeat(50)));
464
+ }
465
+
466
+ /**
467
+ * Main function
468
+ */
469
+ function main() {
470
+ const { flags } = parseFlags(process.argv.slice(2));
471
+
472
+ // Check if morning briefing is enabled
473
+ const config = getConfig();
474
+ const morningConfig = config.morningBriefing || {};
475
+
476
+ if (morningConfig.enabled === false) {
477
+ if (flags.json) {
478
+ outputJson({
479
+ success: false,
480
+ error: 'Morning briefing is disabled in config',
481
+ hint: 'Set morningBriefing.enabled: true in .workflow/config.json'
482
+ });
483
+ }
484
+ console.log(color('yellow', 'Morning briefing is disabled.'));
485
+ console.log(color('dim', 'Enable it in .workflow/config.json: morningBriefing.enabled: true'));
486
+ process.exit(0);
487
+ }
488
+
489
+ // Collect briefing data
490
+ const briefing = collectBriefingData();
491
+
492
+ // JSON output
493
+ if (flags.json) {
494
+ outputJson({
495
+ success: true,
496
+ briefing
497
+ });
498
+ // outputJson exits
499
+ }
500
+
501
+ // Human-readable output
502
+ printBriefing(briefing);
503
+ }
504
+
505
+ // Run
506
+ if (require.main === module) {
507
+ main();
508
+ }
509
+
510
+ module.exports = {
511
+ collectBriefingData,
512
+ getSessionState,
513
+ getProgressData,
514
+ getGitChangesSinceSession,
515
+ getRecommendedTasks,
516
+ generateSuggestedPrompt
517
+ };