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.
- package/.workflow/agents/reviewer.md +81 -0
- package/.workflow/agents/security.md +94 -0
- package/.workflow/agents/story-writer.md +58 -0
- package/.workflow/bridges/base-bridge.js +395 -0
- package/.workflow/bridges/claude-bridge.js +434 -0
- package/.workflow/bridges/index.js +130 -0
- package/.workflow/lib/assumption-detector.js +481 -0
- package/.workflow/lib/config-substitution.js +371 -0
- package/.workflow/lib/failure-categories.js +478 -0
- package/.workflow/state/app-map.md.template +15 -0
- package/.workflow/state/architecture.md.template +24 -0
- package/.workflow/state/component-index.json.template +5 -0
- package/.workflow/state/decisions.md.template +15 -0
- package/.workflow/state/feedback-patterns.md.template +9 -0
- package/.workflow/state/knowledge-sync.json.template +6 -0
- package/.workflow/state/progress.md.template +14 -0
- package/.workflow/state/ready.json.template +7 -0
- package/.workflow/state/request-log.md.template +14 -0
- package/.workflow/state/session-state.json.template +11 -0
- package/.workflow/state/stack.md.template +33 -0
- package/.workflow/state/testing.md.template +36 -0
- package/.workflow/templates/claude-md.hbs +257 -0
- package/.workflow/templates/correction-report.md +67 -0
- package/.workflow/templates/gemini-md.hbs +52 -0
- package/README.md +1802 -0
- package/bin/flow +205 -0
- package/lib/index.js +33 -0
- package/lib/installer.js +467 -0
- package/lib/release-channel.js +269 -0
- package/lib/skill-registry.js +526 -0
- package/lib/upgrader.js +401 -0
- package/lib/utils.js +305 -0
- package/package.json +64 -0
- package/scripts/flow +985 -0
- package/scripts/flow-adaptive-learning.js +1259 -0
- package/scripts/flow-aggregate.js +488 -0
- package/scripts/flow-archive +133 -0
- package/scripts/flow-auto-context.js +1015 -0
- package/scripts/flow-auto-learn.js +615 -0
- package/scripts/flow-bridge.js +223 -0
- package/scripts/flow-browser-suggest.js +316 -0
- package/scripts/flow-bug.js +247 -0
- package/scripts/flow-cascade.js +711 -0
- package/scripts/flow-changelog +85 -0
- package/scripts/flow-checkpoint.js +483 -0
- package/scripts/flow-cli.js +403 -0
- package/scripts/flow-code-intelligence.js +760 -0
- package/scripts/flow-complexity.js +502 -0
- package/scripts/flow-config-set.js +152 -0
- package/scripts/flow-constants.js +157 -0
- package/scripts/flow-context +152 -0
- package/scripts/flow-context-init.js +482 -0
- package/scripts/flow-context-monitor.js +384 -0
- package/scripts/flow-context-scoring.js +886 -0
- package/scripts/flow-correct.js +458 -0
- package/scripts/flow-damage-control.js +985 -0
- package/scripts/flow-deps +101 -0
- package/scripts/flow-diff.js +700 -0
- package/scripts/flow-done +151 -0
- package/scripts/flow-done.js +489 -0
- package/scripts/flow-durable-session.js +1541 -0
- package/scripts/flow-entropy-monitor.js +345 -0
- package/scripts/flow-export-profile +349 -0
- package/scripts/flow-export-scanner.js +1046 -0
- package/scripts/flow-figma-confirm.js +400 -0
- package/scripts/flow-figma-extract.js +496 -0
- package/scripts/flow-figma-generate.js +683 -0
- package/scripts/flow-figma-index.js +909 -0
- package/scripts/flow-figma-match.js +617 -0
- package/scripts/flow-figma-mcp-server.js +518 -0
- package/scripts/flow-figma-pipeline.js +414 -0
- package/scripts/flow-file-ops.js +301 -0
- package/scripts/flow-gate-confidence.js +825 -0
- package/scripts/flow-guided-edit.js +659 -0
- package/scripts/flow-health +185 -0
- package/scripts/flow-health.js +413 -0
- package/scripts/flow-hooks.js +556 -0
- package/scripts/flow-http-client.js +249 -0
- package/scripts/flow-hybrid-detect.js +167 -0
- package/scripts/flow-hybrid-interactive.js +591 -0
- package/scripts/flow-hybrid-test.js +152 -0
- package/scripts/flow-import-profile +439 -0
- package/scripts/flow-init +253 -0
- package/scripts/flow-instruction-richness.js +827 -0
- package/scripts/flow-jira-integration.js +579 -0
- package/scripts/flow-knowledge-router.js +522 -0
- package/scripts/flow-knowledge-sync.js +589 -0
- package/scripts/flow-linear-integration.js +631 -0
- package/scripts/flow-links.js +774 -0
- package/scripts/flow-log-manager.js +559 -0
- package/scripts/flow-loop-enforcer.js +1246 -0
- package/scripts/flow-loop-retry-learning.js +630 -0
- package/scripts/flow-lsp.js +923 -0
- package/scripts/flow-map-index +348 -0
- package/scripts/flow-map-sync +201 -0
- package/scripts/flow-memory-blocks.js +668 -0
- package/scripts/flow-memory-compactor.js +350 -0
- package/scripts/flow-memory-db.js +1110 -0
- package/scripts/flow-memory-sync.js +484 -0
- package/scripts/flow-metrics.js +353 -0
- package/scripts/flow-migrate-ids.js +370 -0
- package/scripts/flow-model-adapter.js +802 -0
- package/scripts/flow-model-router.js +884 -0
- package/scripts/flow-models.js +1231 -0
- package/scripts/flow-morning.js +517 -0
- package/scripts/flow-multi-approach.js +660 -0
- package/scripts/flow-new-feature +86 -0
- package/scripts/flow-onboard +1042 -0
- package/scripts/flow-orchestrate-llm.js +459 -0
- package/scripts/flow-orchestrate.js +3592 -0
- package/scripts/flow-output.js +123 -0
- package/scripts/flow-parallel-detector.js +399 -0
- package/scripts/flow-parallel-dispatch.js +987 -0
- package/scripts/flow-parallel.js +428 -0
- package/scripts/flow-pattern-enforcer.js +600 -0
- package/scripts/flow-prd-manager.js +282 -0
- package/scripts/flow-progress.js +323 -0
- package/scripts/flow-project-analyzer.js +975 -0
- package/scripts/flow-prompt-composer.js +487 -0
- package/scripts/flow-providers.js +1381 -0
- package/scripts/flow-queue.js +308 -0
- package/scripts/flow-ready +82 -0
- package/scripts/flow-ready.js +189 -0
- package/scripts/flow-regression.js +396 -0
- package/scripts/flow-response-parser.js +450 -0
- package/scripts/flow-resume.js +284 -0
- package/scripts/flow-rules-sync.js +439 -0
- package/scripts/flow-run-trace.js +718 -0
- package/scripts/flow-safety.js +587 -0
- package/scripts/flow-search +104 -0
- package/scripts/flow-security.js +481 -0
- package/scripts/flow-session-end +106 -0
- package/scripts/flow-session-end.js +437 -0
- package/scripts/flow-session-state.js +671 -0
- package/scripts/flow-setup-hooks +216 -0
- package/scripts/flow-setup-hooks.js +377 -0
- package/scripts/flow-skill-create.js +329 -0
- package/scripts/flow-skill-creator.js +572 -0
- package/scripts/flow-skill-generator.js +1046 -0
- package/scripts/flow-skill-learn.js +880 -0
- package/scripts/flow-skill-matcher.js +578 -0
- package/scripts/flow-spec-generator.js +820 -0
- package/scripts/flow-stack-wizard.js +895 -0
- package/scripts/flow-standup +162 -0
- package/scripts/flow-start +74 -0
- package/scripts/flow-start.js +235 -0
- package/scripts/flow-status +110 -0
- package/scripts/flow-status.js +301 -0
- package/scripts/flow-step-browser.js +83 -0
- package/scripts/flow-step-changelog.js +217 -0
- package/scripts/flow-step-comments.js +306 -0
- package/scripts/flow-step-complexity.js +234 -0
- package/scripts/flow-step-coverage.js +218 -0
- package/scripts/flow-step-knowledge.js +193 -0
- package/scripts/flow-step-pr-tests.js +364 -0
- package/scripts/flow-step-regression.js +89 -0
- package/scripts/flow-step-review.js +516 -0
- package/scripts/flow-step-security.js +162 -0
- package/scripts/flow-step-silent-failures.js +290 -0
- package/scripts/flow-step-simplifier.js +346 -0
- package/scripts/flow-story +105 -0
- package/scripts/flow-story.js +500 -0
- package/scripts/flow-suspend.js +252 -0
- package/scripts/flow-sync-daemon.js +654 -0
- package/scripts/flow-task-analyzer.js +606 -0
- package/scripts/flow-team-dashboard.js +748 -0
- package/scripts/flow-team-sync.js +752 -0
- package/scripts/flow-team.js +977 -0
- package/scripts/flow-tech-options.js +528 -0
- package/scripts/flow-templates.js +812 -0
- package/scripts/flow-tiered-learning.js +728 -0
- package/scripts/flow-trace +204 -0
- package/scripts/flow-transcript-chunking.js +1106 -0
- package/scripts/flow-transcript-digest.js +7918 -0
- package/scripts/flow-transcript-language.js +465 -0
- package/scripts/flow-transcript-parsing.js +1085 -0
- package/scripts/flow-transcript-stories.js +2194 -0
- package/scripts/flow-update-map +224 -0
- package/scripts/flow-utils.js +2242 -0
- package/scripts/flow-verification.js +644 -0
- package/scripts/flow-verify.js +1177 -0
- package/scripts/flow-voice-input.js +638 -0
- package/scripts/flow-watch +168 -0
- package/scripts/flow-workflow-steps.js +521 -0
- package/scripts/flow-workflow.js +1029 -0
- package/scripts/flow-worktree.js +489 -0
- package/scripts/hooks/adapters/base-adapter.js +102 -0
- package/scripts/hooks/adapters/claude-code.js +359 -0
- package/scripts/hooks/adapters/index.js +79 -0
- package/scripts/hooks/core/component-check.js +341 -0
- package/scripts/hooks/core/index.js +35 -0
- package/scripts/hooks/core/loop-check.js +241 -0
- package/scripts/hooks/core/session-context.js +294 -0
- package/scripts/hooks/core/task-gate.js +177 -0
- package/scripts/hooks/core/validation.js +230 -0
- package/scripts/hooks/entry/claude-code/post-tool-use.js +65 -0
- package/scripts/hooks/entry/claude-code/pre-tool-use.js +89 -0
- package/scripts/hooks/entry/claude-code/session-end.js +87 -0
- package/scripts/hooks/entry/claude-code/session-start.js +46 -0
- package/scripts/hooks/entry/claude-code/stop.js +43 -0
- package/scripts/postinstall.js +139 -0
- package/templates/browser-test-flow.json +56 -0
- package/templates/bug-report.md +43 -0
- package/templates/component-detail.md +42 -0
- package/templates/component.stories.tsx +49 -0
- package/templates/context/constraints.md +83 -0
- package/templates/context/conventions.md +177 -0
- package/templates/context/stack.md +60 -0
- package/templates/correction-report.md +90 -0
- package/templates/feature-proposal.md +35 -0
- package/templates/hybrid/_base.md +254 -0
- package/templates/hybrid/_patterns.md +45 -0
- package/templates/hybrid/create-component.md +127 -0
- package/templates/hybrid/create-file.md +56 -0
- package/templates/hybrid/create-hook.md +145 -0
- package/templates/hybrid/create-service.md +70 -0
- package/templates/hybrid/fix-bug.md +33 -0
- package/templates/hybrid/modify-file.md +55 -0
- package/templates/story.md +68 -0
- package/templates/task.json +56 -0
- 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
|
+
};
|