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,308 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Task Queue Management
|
|
5
|
+
*
|
|
6
|
+
* CLI for managing multi-task queues.
|
|
7
|
+
* v2.1: Supports natural language parsing and automatic queue continuation.
|
|
8
|
+
*
|
|
9
|
+
* Commands:
|
|
10
|
+
* flow queue init <task-ids...> - Initialize queue with task IDs
|
|
11
|
+
* flow queue status - Show current queue status
|
|
12
|
+
* flow queue clear - Clear the queue
|
|
13
|
+
* flow queue parse "<text>" - Parse natural language for task IDs
|
|
14
|
+
* flow queue advance - Manually advance to next task
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const {
|
|
20
|
+
PATHS,
|
|
21
|
+
fileExists,
|
|
22
|
+
color,
|
|
23
|
+
error,
|
|
24
|
+
getConfig
|
|
25
|
+
} = require('./flow-utils');
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
initTaskQueue,
|
|
29
|
+
getQueueStatus,
|
|
30
|
+
advanceTaskQueue,
|
|
31
|
+
clearTaskQueue
|
|
32
|
+
} = require('./flow-durable-session');
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Parse natural language input for task IDs
|
|
36
|
+
* Examples:
|
|
37
|
+
* "do story 1-3" → ["wf-001", "wf-002", "wf-003"]
|
|
38
|
+
* "wf-001, wf-002, wf-003" → ["wf-001", "wf-002", "wf-003"]
|
|
39
|
+
* "tasks 1, 2, 3" → gets from ready.json
|
|
40
|
+
*/
|
|
41
|
+
function parseNaturalLanguage(input) {
|
|
42
|
+
const taskIds = [];
|
|
43
|
+
|
|
44
|
+
// Try to extract wf-* task IDs directly
|
|
45
|
+
const wfMatches = input.match(/wf-[a-f0-9]{8}(?:-\d{2})?/gi);
|
|
46
|
+
if (wfMatches && wfMatches.length > 0) {
|
|
47
|
+
return wfMatches.map(id => id.toLowerCase());
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Try range patterns like "1-3", "story 1-5"
|
|
51
|
+
const rangeMatch = input.match(/(\d+)\s*[-–]\s*(\d+)/);
|
|
52
|
+
if (rangeMatch) {
|
|
53
|
+
const start = parseInt(rangeMatch[1], 10);
|
|
54
|
+
const end = parseInt(rangeMatch[2], 10);
|
|
55
|
+
const count = Math.min(end - start + 1, 10); // Max 10 tasks
|
|
56
|
+
|
|
57
|
+
// Get tasks from ready.json
|
|
58
|
+
return getTasksFromReady(count);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Try patterns like "3 tasks", "these 5 stories"
|
|
62
|
+
const countMatch = input.match(/(\d+)\s*(?:tasks?|stories?|features?)/i);
|
|
63
|
+
if (countMatch) {
|
|
64
|
+
const count = Math.min(parseInt(countMatch[1], 10), 10);
|
|
65
|
+
return getTasksFromReady(count);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Try comma-separated numbers
|
|
69
|
+
const numbersMatch = input.match(/\d+(?:\s*,\s*\d+)+/);
|
|
70
|
+
if (numbersMatch) {
|
|
71
|
+
const numbers = numbersMatch[0].split(/\s*,\s*/).map(n => parseInt(n, 10));
|
|
72
|
+
return getTasksFromReady(numbers.length);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// "all ready tasks"
|
|
76
|
+
if (/all\s+(?:ready\s+)?tasks?/i.test(input)) {
|
|
77
|
+
return getTasksFromReady(10); // Max 10
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return taskIds;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get N tasks from ready.json sorted by priority
|
|
85
|
+
*/
|
|
86
|
+
function getTasksFromReady(count) {
|
|
87
|
+
const readyPath = path.join(PATHS.state, 'ready.json');
|
|
88
|
+
|
|
89
|
+
if (!fileExists(readyPath)) {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const data = JSON.parse(fs.readFileSync(readyPath, 'utf-8'));
|
|
95
|
+
const ready = data.ready || [];
|
|
96
|
+
|
|
97
|
+
// Sort by priority (P0 first)
|
|
98
|
+
const sorted = ready.sort((a, b) => {
|
|
99
|
+
const priorityA = a.priority || 'P2';
|
|
100
|
+
const priorityB = b.priority || 'P2';
|
|
101
|
+
return priorityA.localeCompare(priorityB);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Return requested count
|
|
105
|
+
return sorted.slice(0, count).map(t => t.id);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.error(error(`Failed to read ready.json: ${err.message}`));
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Show queue status
|
|
114
|
+
*/
|
|
115
|
+
function showStatus() {
|
|
116
|
+
const status = getQueueStatus();
|
|
117
|
+
|
|
118
|
+
console.log('');
|
|
119
|
+
console.log(color('cyan', '📋 Task Queue Status'));
|
|
120
|
+
console.log(color('cyan', '─'.repeat(50)));
|
|
121
|
+
|
|
122
|
+
if (!status.enabled || status.tasks.length === 0) {
|
|
123
|
+
console.log(color('dim', 'No active queue.'));
|
|
124
|
+
console.log('');
|
|
125
|
+
console.log('Initialize a queue with:');
|
|
126
|
+
console.log(' flow queue init wf-001 wf-002 wf-003');
|
|
127
|
+
console.log(' flow queue parse "do story 1-3"');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.log(`Source: ${status.source || 'manual'}`);
|
|
132
|
+
console.log(`Queued at: ${status.queuedAt || 'N/A'}`);
|
|
133
|
+
console.log('');
|
|
134
|
+
|
|
135
|
+
console.log(color('white', `Tasks (${status.currentIndex + 1}/${status.tasks.length}):`));
|
|
136
|
+
|
|
137
|
+
status.tasks.forEach((taskId, i) => {
|
|
138
|
+
const isCompleted = status.completedTasks.includes(taskId);
|
|
139
|
+
const isCurrent = i === status.currentIndex;
|
|
140
|
+
|
|
141
|
+
let prefix = ' ';
|
|
142
|
+
let taskColor = 'dim';
|
|
143
|
+
let statusIcon = '○';
|
|
144
|
+
|
|
145
|
+
if (isCompleted) {
|
|
146
|
+
statusIcon = '✓';
|
|
147
|
+
taskColor = 'green';
|
|
148
|
+
} else if (isCurrent) {
|
|
149
|
+
statusIcon = '→';
|
|
150
|
+
taskColor = 'yellow';
|
|
151
|
+
prefix = '';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
console.log(color(taskColor, `${prefix}${statusIcon} ${taskId}`));
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
console.log('');
|
|
158
|
+
console.log(color('dim', `Completed: ${status.completedTasks.length}/${status.tasks.length}`));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Initialize queue
|
|
163
|
+
*/
|
|
164
|
+
function initQueue(taskIds, source = 'cli') {
|
|
165
|
+
if (taskIds.length === 0) {
|
|
166
|
+
console.log(error('No task IDs provided.'));
|
|
167
|
+
console.log('');
|
|
168
|
+
console.log('Usage:');
|
|
169
|
+
console.log(' flow queue init wf-001 wf-002 wf-003');
|
|
170
|
+
console.log(' flow queue init $(flow queue parse "do story 1-3")');
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const config = getConfig();
|
|
175
|
+
const maxQueueSize = config.taskQueue?.maxQueueSize || 10;
|
|
176
|
+
|
|
177
|
+
if (taskIds.length > maxQueueSize) {
|
|
178
|
+
console.log(color('yellow', `Warning: Queue size limited to ${maxQueueSize} tasks.`));
|
|
179
|
+
taskIds = taskIds.slice(0, maxQueueSize);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
initTaskQueue(taskIds, source);
|
|
183
|
+
|
|
184
|
+
console.log('');
|
|
185
|
+
console.log(color('green', '✓ Task queue initialized'));
|
|
186
|
+
console.log('');
|
|
187
|
+
console.log(`Tasks queued: ${taskIds.length}`);
|
|
188
|
+
taskIds.forEach((id, i) => {
|
|
189
|
+
console.log(color('dim', ` ${i + 1}. ${id}`));
|
|
190
|
+
});
|
|
191
|
+
console.log('');
|
|
192
|
+
console.log('Start the first task with:');
|
|
193
|
+
console.log(color('cyan', ` /wogi-start ${taskIds[0]}`));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Parse command and output task IDs
|
|
198
|
+
*/
|
|
199
|
+
function parseCommand(input) {
|
|
200
|
+
const taskIds = parseNaturalLanguage(input);
|
|
201
|
+
|
|
202
|
+
if (taskIds.length === 0) {
|
|
203
|
+
console.log(error('Could not parse task IDs from input.'));
|
|
204
|
+
console.log('');
|
|
205
|
+
console.log('Supported formats:');
|
|
206
|
+
console.log(' "wf-001, wf-002, wf-003" - Direct task IDs');
|
|
207
|
+
console.log(' "story 1-3" or "tasks 1-5" - Range from ready queue');
|
|
208
|
+
console.log(' "3 tasks" or "these 5 stories" - Count from ready queue');
|
|
209
|
+
console.log(' "all ready tasks" - All from ready queue (max 10)');
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Output task IDs for use in other commands
|
|
214
|
+
console.log(taskIds.join(' '));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Clear the queue
|
|
219
|
+
*/
|
|
220
|
+
function clearQueue() {
|
|
221
|
+
clearTaskQueue();
|
|
222
|
+
console.log(color('green', '✓ Task queue cleared'));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Advance to next task
|
|
227
|
+
*/
|
|
228
|
+
function advance() {
|
|
229
|
+
const status = getQueueStatus();
|
|
230
|
+
|
|
231
|
+
if (!status.enabled || status.tasks.length === 0) {
|
|
232
|
+
console.log(error('No active queue.'));
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (status.currentIndex >= status.tasks.length - 1) {
|
|
237
|
+
console.log(color('yellow', 'Already at the last task in queue.'));
|
|
238
|
+
process.exit(0);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
advanceTaskQueue();
|
|
242
|
+
|
|
243
|
+
const newStatus = getQueueStatus();
|
|
244
|
+
const nextTask = newStatus.tasks[newStatus.currentIndex];
|
|
245
|
+
|
|
246
|
+
console.log(color('green', '✓ Advanced to next task'));
|
|
247
|
+
console.log(`Next task: ${nextTask}`);
|
|
248
|
+
console.log('');
|
|
249
|
+
console.log('Start with:');
|
|
250
|
+
console.log(color('cyan', ` /wogi-start ${nextTask}`));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Main
|
|
254
|
+
function main() {
|
|
255
|
+
const command = process.argv[2];
|
|
256
|
+
const args = process.argv.slice(3);
|
|
257
|
+
|
|
258
|
+
switch (command) {
|
|
259
|
+
case 'init':
|
|
260
|
+
initQueue(args, 'cli');
|
|
261
|
+
break;
|
|
262
|
+
|
|
263
|
+
case 'status':
|
|
264
|
+
showStatus();
|
|
265
|
+
break;
|
|
266
|
+
|
|
267
|
+
case 'clear':
|
|
268
|
+
clearQueue();
|
|
269
|
+
break;
|
|
270
|
+
|
|
271
|
+
case 'parse':
|
|
272
|
+
if (args.length === 0) {
|
|
273
|
+
console.log(error('Missing input string.'));
|
|
274
|
+
console.log('Usage: flow queue parse "do story 1-3"');
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
parseCommand(args.join(' '));
|
|
278
|
+
break;
|
|
279
|
+
|
|
280
|
+
case 'advance':
|
|
281
|
+
advance();
|
|
282
|
+
break;
|
|
283
|
+
|
|
284
|
+
default:
|
|
285
|
+
console.log('Wogi Flow - Task Queue Management');
|
|
286
|
+
console.log('');
|
|
287
|
+
console.log('Commands:');
|
|
288
|
+
console.log(' flow queue init <task-ids...> - Initialize queue with task IDs');
|
|
289
|
+
console.log(' flow queue status - Show current queue status');
|
|
290
|
+
console.log(' flow queue clear - Clear the queue');
|
|
291
|
+
console.log(' flow queue parse "<text>" - Parse natural language for task IDs');
|
|
292
|
+
console.log(' flow queue advance - Manually advance to next task');
|
|
293
|
+
console.log('');
|
|
294
|
+
console.log('Examples:');
|
|
295
|
+
console.log(' flow queue init wf-001 wf-002 wf-003');
|
|
296
|
+
console.log(' flow queue init $(flow queue parse "do story 1-3")');
|
|
297
|
+
console.log(' flow queue parse "work on tasks 1-5"');
|
|
298
|
+
console.log(' flow queue status');
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
main();
|
|
304
|
+
|
|
305
|
+
module.exports = {
|
|
306
|
+
parseNaturalLanguage,
|
|
307
|
+
getTasksFromReady
|
|
308
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Wogi Flow - Show Ready Tasks
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
WORKFLOW_DIR=".workflow"
|
|
8
|
+
READY_JSON="$WORKFLOW_DIR/state/ready.json"
|
|
9
|
+
|
|
10
|
+
# Colors
|
|
11
|
+
GREEN='\033[0;32m'
|
|
12
|
+
YELLOW='\033[1;33m'
|
|
13
|
+
CYAN='\033[0;36m'
|
|
14
|
+
RED='\033[0;31m'
|
|
15
|
+
NC='\033[0m'
|
|
16
|
+
|
|
17
|
+
if [ ! -f "$READY_JSON" ]; then
|
|
18
|
+
echo -e "${RED}Error: No ready.json found${NC}"
|
|
19
|
+
echo "Run: ./scripts/flow init"
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
echo -e "${CYAN}Task Queue${NC}"
|
|
24
|
+
echo "==========="
|
|
25
|
+
echo ""
|
|
26
|
+
|
|
27
|
+
READY_FILE="$READY_JSON" python3 << 'EOF'
|
|
28
|
+
import json, os
|
|
29
|
+
|
|
30
|
+
with open(os.environ['READY_FILE']) as f:
|
|
31
|
+
data = json.load(f)
|
|
32
|
+
|
|
33
|
+
# Ready tasks
|
|
34
|
+
ready = data.get('ready', [])
|
|
35
|
+
if ready:
|
|
36
|
+
print('\033[0;32m✓ READY\033[0m')
|
|
37
|
+
for task in ready:
|
|
38
|
+
if isinstance(task, dict):
|
|
39
|
+
priority = task.get('priority', '-')
|
|
40
|
+
print(f" [{priority}] {task.get('id', '?')}: {task.get('title', 'No title')}")
|
|
41
|
+
else:
|
|
42
|
+
print(f" • {task}")
|
|
43
|
+
print()
|
|
44
|
+
|
|
45
|
+
# In progress
|
|
46
|
+
in_progress = data.get('inProgress', [])
|
|
47
|
+
if in_progress:
|
|
48
|
+
print('\033[1;33m⏳ IN PROGRESS\033[0m')
|
|
49
|
+
for task in in_progress:
|
|
50
|
+
if isinstance(task, dict):
|
|
51
|
+
print(f" • {task.get('id', '?')}: {task.get('title', 'No title')}")
|
|
52
|
+
else:
|
|
53
|
+
print(f" • {task}")
|
|
54
|
+
print()
|
|
55
|
+
|
|
56
|
+
# Blocked
|
|
57
|
+
blocked = data.get('blocked', [])
|
|
58
|
+
if blocked:
|
|
59
|
+
print('\033[0;31m🚫 BLOCKED\033[0m')
|
|
60
|
+
for task in blocked:
|
|
61
|
+
if isinstance(task, dict):
|
|
62
|
+
reason = task.get('reason', 'Unknown')
|
|
63
|
+
print(f" • {task.get('id', '?')}: {reason}")
|
|
64
|
+
else:
|
|
65
|
+
print(f" • {task}")
|
|
66
|
+
print()
|
|
67
|
+
|
|
68
|
+
# Recently completed
|
|
69
|
+
completed = data.get('recentlyCompleted', [])
|
|
70
|
+
if completed:
|
|
71
|
+
print('\033[0;36m✅ RECENTLY COMPLETED\033[0m')
|
|
72
|
+
for task in completed[:5]: # Last 5
|
|
73
|
+
if isinstance(task, dict):
|
|
74
|
+
print(f" • {task.get('id', '?')}: {task.get('title', 'No title')}")
|
|
75
|
+
else:
|
|
76
|
+
print(f" • {task}")
|
|
77
|
+
print()
|
|
78
|
+
|
|
79
|
+
# Summary
|
|
80
|
+
total = len(ready) + len(in_progress) + len(blocked)
|
|
81
|
+
print(f"Total active: {total} ({len(ready)} ready, {len(in_progress)} in progress, {len(blocked)} blocked)")
|
|
82
|
+
EOF
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Show Ready Tasks
|
|
5
|
+
*
|
|
6
|
+
* Displays tasks organized by status from ready.json
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* flow ready Show tasks (human-readable)
|
|
10
|
+
* flow ready --json Output JSON for programmatic access
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
PATHS,
|
|
15
|
+
fileExists,
|
|
16
|
+
getReadyData,
|
|
17
|
+
parseFlags,
|
|
18
|
+
outputJson,
|
|
19
|
+
color,
|
|
20
|
+
printSection,
|
|
21
|
+
error,
|
|
22
|
+
warn
|
|
23
|
+
} = require('./flow-utils');
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Priority order for sorting (P0 highest, P4 lowest)
|
|
27
|
+
*/
|
|
28
|
+
const PRIORITY_ORDER = { P0: 0, P1: 1, P2: 2, P3: 3, P4: 4 };
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Sort tasks by priority, then by date
|
|
32
|
+
*/
|
|
33
|
+
function sortByPriority(tasks) {
|
|
34
|
+
return [...tasks].sort((a, b) => {
|
|
35
|
+
// Handle both string and object tasks
|
|
36
|
+
const aPriority = typeof a === 'object' ? (a.priority || 'P2') : 'P2';
|
|
37
|
+
const bPriority = typeof b === 'object' ? (b.priority || 'P2') : 'P2';
|
|
38
|
+
|
|
39
|
+
const aOrder = PRIORITY_ORDER[aPriority] ?? 2;
|
|
40
|
+
const bOrder = PRIORITY_ORDER[bPriority] ?? 2;
|
|
41
|
+
|
|
42
|
+
if (aOrder !== bOrder) {
|
|
43
|
+
return aOrder - bOrder;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Secondary sort by date (newer first) - ISO dates can be compared directly
|
|
47
|
+
const aDate = typeof a === 'object' ? (a.createdAt || '') : '';
|
|
48
|
+
const bDate = typeof b === 'object' ? (b.createdAt || '') : '';
|
|
49
|
+
// Direct comparison works for ISO 8601 dates (YYYY-MM-DD...)
|
|
50
|
+
if (bDate > aDate) return 1;
|
|
51
|
+
if (bDate < aDate) return -1;
|
|
52
|
+
return 0;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Format task for display
|
|
58
|
+
*/
|
|
59
|
+
function formatTask(task) {
|
|
60
|
+
if (typeof task === 'object') {
|
|
61
|
+
const priority = task.priority || 'P2';
|
|
62
|
+
const id = task.id || '?';
|
|
63
|
+
const title = task.title || 'No title';
|
|
64
|
+
return { priority, id, title, raw: task };
|
|
65
|
+
}
|
|
66
|
+
return { priority: '-', id: task, title: '', raw: task };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function main() {
|
|
70
|
+
const { flags } = parseFlags(process.argv.slice(2));
|
|
71
|
+
|
|
72
|
+
if (!fileExists(PATHS.ready)) {
|
|
73
|
+
if (flags.json) {
|
|
74
|
+
outputJson({
|
|
75
|
+
success: false,
|
|
76
|
+
error: 'No ready.json found',
|
|
77
|
+
tasks: { ready: [], inProgress: [], blocked: [], recentlyCompleted: [] },
|
|
78
|
+
summary: { total: 0, ready: 0, inProgress: 0, blocked: 0 }
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
error('No ready.json found');
|
|
82
|
+
console.log('Run: ./scripts/flow init');
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const data = getReadyData();
|
|
87
|
+
|
|
88
|
+
// Validate data structure and warn if corrupted
|
|
89
|
+
if (!data || typeof data !== 'object') {
|
|
90
|
+
warn('ready.json appears corrupted (not an object)');
|
|
91
|
+
} else {
|
|
92
|
+
// Check for expected arrays
|
|
93
|
+
const expectedArrays = ['ready', 'inProgress', 'blocked', 'recentlyCompleted'];
|
|
94
|
+
for (const key of expectedArrays) {
|
|
95
|
+
if (data[key] !== undefined && !Array.isArray(data[key])) {
|
|
96
|
+
warn(`ready.json: "${key}" should be an array but found ${typeof data[key]}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Sort ready tasks by priority
|
|
102
|
+
const ready = sortByPriority(data.ready || []);
|
|
103
|
+
const inProgress = data.inProgress || [];
|
|
104
|
+
const blocked = data.blocked || [];
|
|
105
|
+
const completed = data.recentlyCompleted || [];
|
|
106
|
+
|
|
107
|
+
// Calculate summary
|
|
108
|
+
const summary = {
|
|
109
|
+
total: ready.length + inProgress.length + blocked.length,
|
|
110
|
+
ready: ready.length,
|
|
111
|
+
inProgress: inProgress.length,
|
|
112
|
+
blocked: blocked.length,
|
|
113
|
+
recentlyCompleted: completed.length
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// JSON output - exit after to avoid human-readable output
|
|
117
|
+
if (flags.json) {
|
|
118
|
+
outputJson({
|
|
119
|
+
success: true,
|
|
120
|
+
tasks: {
|
|
121
|
+
ready,
|
|
122
|
+
inProgress,
|
|
123
|
+
blocked,
|
|
124
|
+
recentlyCompleted: completed
|
|
125
|
+
},
|
|
126
|
+
summary
|
|
127
|
+
});
|
|
128
|
+
return; // Exit early for JSON mode
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Human-readable output
|
|
132
|
+
printSection('Task Queue');
|
|
133
|
+
console.log('===========');
|
|
134
|
+
console.log('');
|
|
135
|
+
|
|
136
|
+
// Ready tasks (sorted by priority)
|
|
137
|
+
if (ready.length > 0) {
|
|
138
|
+
console.log(color('green', '✓ READY'));
|
|
139
|
+
for (const task of ready) {
|
|
140
|
+
const { priority, id, title } = formatTask(task);
|
|
141
|
+
const priorityColor = priority === 'P0' ? 'red' : priority === 'P1' ? 'yellow' : 'dim';
|
|
142
|
+
console.log(` ${color(priorityColor, `[${priority}]`)} ${id}: ${title}`);
|
|
143
|
+
}
|
|
144
|
+
console.log('');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// In progress
|
|
148
|
+
if (inProgress.length > 0) {
|
|
149
|
+
console.log(color('yellow', '⏳ IN PROGRESS'));
|
|
150
|
+
for (const task of inProgress) {
|
|
151
|
+
const { id, title } = formatTask(task);
|
|
152
|
+
console.log(` • ${id}: ${title}`);
|
|
153
|
+
}
|
|
154
|
+
console.log('');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Blocked
|
|
158
|
+
if (blocked.length > 0) {
|
|
159
|
+
console.log(color('red', '🚫 BLOCKED'));
|
|
160
|
+
for (const task of blocked) {
|
|
161
|
+
if (typeof task === 'object') {
|
|
162
|
+
const reason = task.reason || task.blockedBy || 'Unknown';
|
|
163
|
+
console.log(` • ${task.id || '?'}: ${reason}`);
|
|
164
|
+
} else {
|
|
165
|
+
console.log(` • ${task}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
console.log('');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Recently completed
|
|
172
|
+
if (completed.length > 0) {
|
|
173
|
+
console.log(color('cyan', '✅ RECENTLY COMPLETED'));
|
|
174
|
+
for (const task of completed.slice(0, 5)) {
|
|
175
|
+
const { id, title } = formatTask(task);
|
|
176
|
+
console.log(` • ${id}: ${title}`);
|
|
177
|
+
}
|
|
178
|
+
console.log('');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Summary
|
|
182
|
+
console.log(`Total active: ${summary.total} (${summary.ready} ready, ${summary.inProgress} in progress, ${summary.blocked} blocked)`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (require.main === module) {
|
|
186
|
+
main();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
module.exports = { main, sortByPriority, PRIORITY_ORDER };
|