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,282 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - PRD Manager
5
+ *
6
+ * Manages Project Requirements Documents (PRDs) with:
7
+ * - Loading and chunking PRD content
8
+ * - Semantic storage for contextual retrieval (via flow-memory-db)
9
+ * - Task-aware context extraction with embeddings
10
+ *
11
+ * Part of v1.8.0 Team Collaboration
12
+ *
13
+ * Usage:
14
+ * ./scripts/flow prd load <file> Load PRD into memory
15
+ * ./scripts/flow prd context <task> Get relevant PRD context for task
16
+ * ./scripts/flow prd list List loaded PRDs
17
+ * ./scripts/flow prd clear Clear PRD memory
18
+ */
19
+
20
+ const fs = require('fs');
21
+ const path = require('path');
22
+ const {
23
+ getConfig,
24
+ colors,
25
+ color,
26
+ success,
27
+ warn,
28
+ error,
29
+ info,
30
+ printHeader,
31
+ fileExists,
32
+ readFile
33
+ } = require('./flow-utils');
34
+
35
+ // Use shared memory database
36
+ const memoryDb = require('./flow-memory-db');
37
+
38
+ // ============================================================
39
+ // Constants
40
+ // ============================================================
41
+
42
+ const DEFAULT_MAX_CONTEXT_TOKENS = 2000;
43
+
44
+ // ============================================================
45
+ // PRD Loading
46
+ // ============================================================
47
+
48
+ /**
49
+ * Load a PRD file into storage (using shared database)
50
+ */
51
+ async function loadPRD(filePath, options = {}) {
52
+ if (!fileExists(filePath)) {
53
+ throw new Error(`File not found: ${filePath}`);
54
+ }
55
+
56
+ const content = readFile(filePath);
57
+ const fileName = path.basename(filePath);
58
+ const prdId = options.id || fileName.replace(/\.[^.]+$/, '');
59
+
60
+ // Use shared database to store PRD with embeddings
61
+ const result = await memoryDb.storePRD({
62
+ content,
63
+ prdId,
64
+ fileName
65
+ });
66
+
67
+ return result;
68
+ }
69
+
70
+ // ============================================================
71
+ // Context Retrieval
72
+ // ============================================================
73
+
74
+ /**
75
+ * Get relevant PRD context for a task (using semantic search)
76
+ *
77
+ * @param {string} taskDescription - The task description or query
78
+ * @param {object} options - Retrieval options
79
+ * @returns {string} Formatted context for the task
80
+ */
81
+ async function getPRDContext(taskDescription, options = {}) {
82
+ const config = getConfig();
83
+ const maxTokens = options.maxTokens || config.prd?.maxContextTokens || DEFAULT_MAX_CONTEXT_TOKENS;
84
+
85
+ const result = await memoryDb.getPRDContext({
86
+ query: taskDescription,
87
+ maxTokens,
88
+ prdId: options.prdId
89
+ });
90
+
91
+ return result ? result.context : null;
92
+ }
93
+
94
+ /**
95
+ * List loaded PRDs
96
+ */
97
+ async function listPRDs() {
98
+ const prds = await memoryDb.listPRDs();
99
+ return prds.map(p => ({
100
+ id: p.prd_id,
101
+ fileName: p.file_name,
102
+ chunkCount: p.chunk_count,
103
+ loadedAt: p.created_at
104
+ }));
105
+ }
106
+
107
+ /**
108
+ * Clear all PRD data
109
+ */
110
+ async function clearPRDs() {
111
+ await memoryDb.clearPRDs();
112
+ }
113
+
114
+ /**
115
+ * Remove a specific PRD
116
+ */
117
+ async function removePRD(prdId) {
118
+ const result = await memoryDb.deletePRD(prdId);
119
+ return result.deleted;
120
+ }
121
+
122
+ // ============================================================
123
+ // CLI
124
+ // ============================================================
125
+
126
+ function printUsage() {
127
+ console.log(`
128
+ Wogi Flow - PRD Manager
129
+
130
+ Usage: ./scripts/flow prd <command> [args]
131
+
132
+ Commands:
133
+ load <file> Load PRD markdown file into memory
134
+ context <task> Get relevant PRD context for task
135
+ list List loaded PRDs
136
+ remove <prd-id> Remove a PRD from memory
137
+ clear Clear all PRD data
138
+ stats Show PRD memory statistics
139
+
140
+ Examples:
141
+ ./scripts/flow prd load docs/PRD.md
142
+ ./scripts/flow prd context "implement user login"
143
+ ./scripts/flow prd list
144
+
145
+ Configuration (config.json):
146
+ prd.enabled Enable PRD features (default: false)
147
+ prd.maxContextTokens Max tokens for context (default: 2000)
148
+ `);
149
+ }
150
+
151
+ async function main() {
152
+ const args = process.argv.slice(2);
153
+ const command = args[0];
154
+
155
+ switch (command) {
156
+ case 'load': {
157
+ printHeader('Load PRD');
158
+
159
+ const filePath = args[1];
160
+ if (!filePath) {
161
+ error('Please provide a file path');
162
+ process.exit(1);
163
+ }
164
+
165
+ try {
166
+ info('Loading PRD with embeddings (this may take a moment on first run)...');
167
+ const result = await loadPRD(filePath);
168
+ success(`Loaded PRD: ${result.prdId}`);
169
+ console.log(` Chunks: ${result.chunkCount}`);
170
+ console.log(` Sections: ${result.sections.join(', ')}`);
171
+ } catch (err) {
172
+ error(err.message);
173
+ process.exit(1);
174
+ }
175
+ break;
176
+ }
177
+
178
+ case 'context': {
179
+ const task = args.slice(1).join(' ');
180
+ if (!task) {
181
+ error('Please provide a task description');
182
+ process.exit(1);
183
+ }
184
+
185
+ const context = await getPRDContext(task);
186
+ if (!context) {
187
+ warn('No PRD loaded. Run: ./scripts/flow prd load <file>');
188
+ process.exit(0);
189
+ }
190
+
191
+ console.log(context);
192
+ break;
193
+ }
194
+
195
+ case 'list': {
196
+ printHeader('Loaded PRDs');
197
+
198
+ const prds = await listPRDs();
199
+ if (prds.length === 0) {
200
+ info('No PRDs loaded.');
201
+ info('Load one with: ./scripts/flow prd load <file>');
202
+ break;
203
+ }
204
+
205
+ console.log('');
206
+ for (const prd of prds) {
207
+ console.log(` ${color('green', prd.id)}`);
208
+ console.log(` File: ${prd.fileName || 'N/A'}`);
209
+ console.log(` Chunks: ${prd.chunkCount}`);
210
+ console.log(` Loaded: ${prd.loadedAt}`);
211
+ console.log('');
212
+ }
213
+ break;
214
+ }
215
+
216
+ case 'remove': {
217
+ const prdId = args[1];
218
+ if (!prdId) {
219
+ error('Please provide a PRD ID');
220
+ process.exit(1);
221
+ }
222
+
223
+ if (await removePRD(prdId)) {
224
+ success(`Removed PRD: ${prdId}`);
225
+ } else {
226
+ error(`PRD not found: ${prdId}`);
227
+ process.exit(1);
228
+ }
229
+ break;
230
+ }
231
+
232
+ case 'clear': {
233
+ await clearPRDs();
234
+ success('All PRD data cleared');
235
+ break;
236
+ }
237
+
238
+ case 'stats': {
239
+ printHeader('PRD Memory Stats');
240
+ const stats = await memoryDb.getStats();
241
+ console.log('');
242
+ console.log(` PRDs: ${stats.prds.total}`);
243
+ console.log(` Chunks: ${stats.prds.chunks}`);
244
+ console.log(` Facts: ${stats.facts.total}`);
245
+ console.log(` Proposals: ${stats.proposals.total} (${stats.proposals.pending} pending)`);
246
+ console.log('');
247
+ break;
248
+ }
249
+
250
+ case '--help':
251
+ case '-h':
252
+ case 'help':
253
+ printUsage();
254
+ break;
255
+
256
+ default:
257
+ if (command) {
258
+ error(`Unknown command: ${command}`);
259
+ }
260
+ printUsage();
261
+ process.exit(command ? 1 : 0);
262
+ }
263
+ }
264
+
265
+ // ============================================================
266
+ // Exports
267
+ // ============================================================
268
+
269
+ module.exports = {
270
+ loadPRD,
271
+ getPRDContext,
272
+ listPRDs,
273
+ clearPRDs,
274
+ removePRD
275
+ };
276
+
277
+ if (require.main === module) {
278
+ main().catch(e => {
279
+ error(err.message);
280
+ process.exit(1);
281
+ });
282
+ }
@@ -0,0 +1,323 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Progress Display Utilities
5
+ *
6
+ * Provides visual feedback during hybrid execution.
7
+ */
8
+
9
+ const readline = require('readline');
10
+ const { colors } = require('./flow-utils');
11
+
12
+ const symbols = {
13
+ success: '✅',
14
+ error: '❌',
15
+ warning: '⚠️',
16
+ info: 'ℹ️',
17
+ pending: '⏳',
18
+ running: '🔄',
19
+ skip: '⏭️',
20
+ parallel: '⚡',
21
+ rollback: '🔙',
22
+ plan: '📋',
23
+ step: '📍',
24
+ file: '📄',
25
+ folder: '📁',
26
+ check: '✓',
27
+ cross: '✗',
28
+ arrow: '→',
29
+ bullet: '•'
30
+ };
31
+
32
+ class ProgressBar {
33
+ constructor(total, width = 30) {
34
+ this.total = total;
35
+ this.current = 0;
36
+ this.width = width;
37
+ this.startTime = Date.now();
38
+ }
39
+
40
+ update(current, label = '') {
41
+ this.current = current;
42
+ const percent = Math.floor((current / this.total) * 100);
43
+ const filled = Math.floor((current / this.total) * this.width);
44
+ const empty = this.width - filled;
45
+
46
+ const bar = colors.green + '█'.repeat(filled) + colors.dim + '░'.repeat(empty) + colors.reset;
47
+ const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
48
+
49
+ const line = ` [${bar}] ${percent}% ${label} ${colors.dim}(${elapsed}s)${colors.reset}`;
50
+
51
+ process.stdout.write('\r' + ' '.repeat(80) + '\r');
52
+ process.stdout.write(line);
53
+ }
54
+
55
+ complete(label = 'Complete') {
56
+ this.update(this.total, label);
57
+ console.log('');
58
+ }
59
+ }
60
+
61
+ class Spinner {
62
+ constructor(text = 'Loading') {
63
+ this.text = text;
64
+ this.frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
65
+ this.frameIndex = 0;
66
+ this.interval = null;
67
+ }
68
+
69
+ start() {
70
+ this.interval = setInterval(() => {
71
+ const frame = this.frames[this.frameIndex];
72
+ process.stdout.write(`\r${colors.cyan}${frame}${colors.reset} ${this.text}`);
73
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
74
+ }, 80);
75
+ }
76
+
77
+ stop(finalText = '', success = true) {
78
+ clearInterval(this.interval);
79
+ const symbol = success ? colors.green + symbols.check : colors.red + symbols.cross;
80
+ process.stdout.write(`\r${symbol}${colors.reset} ${finalText || this.text}\n`);
81
+ }
82
+
83
+ update(text) {
84
+ this.text = text;
85
+ }
86
+ }
87
+
88
+ class StepDisplay {
89
+ constructor() {
90
+ this.steps = [];
91
+ this.currentStep = -1;
92
+ }
93
+
94
+ setSteps(steps) {
95
+ this.steps = steps.map(s => ({
96
+ id: s.id,
97
+ title: s.title,
98
+ status: 'pending',
99
+ duration: null,
100
+ error: null
101
+ }));
102
+ }
103
+
104
+ startStep(stepId) {
105
+ this.currentStep = this.steps.findIndex(s => s.id === stepId);
106
+ if (this.currentStep >= 0) {
107
+ this.steps[this.currentStep].status = 'running';
108
+ this.steps[this.currentStep].startTime = Date.now();
109
+ }
110
+ this.render();
111
+ }
112
+
113
+ completeStep(stepId, success = true, error = null) {
114
+ const step = this.steps.find(s => s.id === stepId);
115
+ if (step) {
116
+ step.status = success ? 'success' : 'error';
117
+ step.duration = ((Date.now() - step.startTime) / 1000).toFixed(1);
118
+ step.error = error;
119
+ }
120
+ this.render();
121
+ }
122
+
123
+ skipStep(stepId) {
124
+ const step = this.steps.find(s => s.id === stepId);
125
+ if (step) {
126
+ step.status = 'skipped';
127
+ }
128
+ this.render();
129
+ }
130
+
131
+ render() {
132
+ if (this.steps.length > 0) {
133
+ process.stdout.write(`${ESC}[${this.steps.length + 2}A`);
134
+ }
135
+
136
+ console.log('');
137
+ for (const step of this.steps) {
138
+ let statusIcon, statusColor;
139
+
140
+ switch (step.status) {
141
+ case 'success':
142
+ statusIcon = symbols.success;
143
+ statusColor = colors.green;
144
+ break;
145
+ case 'error':
146
+ statusIcon = symbols.error;
147
+ statusColor = colors.red;
148
+ break;
149
+ case 'running':
150
+ statusIcon = symbols.running;
151
+ statusColor = colors.cyan;
152
+ break;
153
+ case 'skipped':
154
+ statusIcon = symbols.skip;
155
+ statusColor = colors.dim;
156
+ break;
157
+ default:
158
+ statusIcon = symbols.pending;
159
+ statusColor = colors.dim;
160
+ }
161
+
162
+ const duration = step.duration ? ` ${colors.dim}(${step.duration}s)${colors.reset}` : '';
163
+ console.log(` ${statusIcon} ${statusColor}Step ${step.id}:${colors.reset} ${step.title}${duration}`);
164
+ }
165
+ console.log('');
166
+ }
167
+ }
168
+
169
+ function formatBox(title, content, width = 60) {
170
+ const lines = content.split('\n');
171
+ const topBorder = '═'.repeat(width);
172
+ const bottomBorder = '═'.repeat(width);
173
+
174
+ let output = `${colors.cyan}╔${topBorder}╗${colors.reset}\n`;
175
+
176
+ const titlePadding = Math.floor((width - title.length) / 2);
177
+ output += `${colors.cyan}║${colors.reset}${' '.repeat(titlePadding)}${colors.bold}${title}${colors.reset}${' '.repeat(width - titlePadding - title.length)}${colors.cyan}║${colors.reset}\n`;
178
+ output += `${colors.cyan}╠${topBorder}╣${colors.reset}\n`;
179
+
180
+ for (const line of lines) {
181
+ const paddedLine = line.slice(0, width - 2).padEnd(width - 2);
182
+ output += `${colors.cyan}║${colors.reset} ${paddedLine}${colors.cyan}║${colors.reset}\n`;
183
+ }
184
+
185
+ output += `${colors.cyan}╚${bottomBorder}╝${colors.reset}`;
186
+
187
+ return output;
188
+ }
189
+
190
+ function formatPlanSummary(plan) {
191
+ let output = '';
192
+
193
+ output += `\n${colors.cyan}${'═'.repeat(60)}${colors.reset}\n`;
194
+ output += `${colors.cyan}${' '.repeat(20)}EXECUTION PLAN${colors.reset}\n`;
195
+ output += `${colors.cyan}${'═'.repeat(60)}${colors.reset}\n\n`;
196
+
197
+ output += `${symbols.plan} Task: ${colors.bold}${plan.task}${colors.reset}\n`;
198
+ output += `🤖 Executor: ${plan.model || 'Local LLM'}\n`;
199
+ output += `📊 Steps: ${plan.steps.length}\n`;
200
+ output += `💰 Est. savings: ~${(plan.estimatedTokensSaved || 0).toLocaleString()} tokens\n\n`;
201
+
202
+ for (const step of plan.steps) {
203
+ output += `${colors.dim}┌${'─'.repeat(58)}${colors.reset}\n`;
204
+ output += `${colors.dim}│${colors.reset} ${colors.bold}Step ${step.id}:${colors.reset} ${step.title}\n`;
205
+ output += `${colors.dim}│${colors.reset} Type: ${step.type}\n`;
206
+ if (step.params?.path) {
207
+ output += `${colors.dim}│${colors.reset} Path: ${colors.cyan}${step.params.path}${colors.reset}\n`;
208
+ }
209
+ const deps = step.dependsOn?.length > 0 ? `Steps ${step.dependsOn.join(', ')}` : 'None';
210
+ output += `${colors.dim}│${colors.reset} Dependencies: ${deps}\n`;
211
+ }
212
+ output += `${colors.dim}└${'─'.repeat(58)}${colors.reset}\n`;
213
+
214
+ return output;
215
+ }
216
+
217
+ function formatOptions(options) {
218
+ let output = '\nHow would you like to proceed?\n\n';
219
+
220
+ for (const opt of options) {
221
+ output += ` ${colors.cyan}[${opt.key}]${colors.reset} ${opt.icon} ${opt.label}\n`;
222
+ }
223
+
224
+ return output + '\nYour choice: ';
225
+ }
226
+
227
+ async function prompt(question) {
228
+ const rl = readline.createInterface({
229
+ input: process.stdin,
230
+ output: process.stdout
231
+ });
232
+
233
+ return new Promise(resolve => {
234
+ rl.question(question, answer => {
235
+ rl.close();
236
+ resolve(answer.trim());
237
+ });
238
+ });
239
+ }
240
+
241
+ function formatResults(results) {
242
+ let output = '';
243
+
244
+ output += `\n${colors.cyan}${'═'.repeat(60)}${colors.reset}\n`;
245
+ output += `${colors.cyan}${' '.repeat(18)}EXECUTION SUMMARY${colors.reset}\n`;
246
+ output += `${colors.cyan}${'═'.repeat(60)}${colors.reset}\n\n`;
247
+
248
+ if (results.success) {
249
+ output += `${symbols.success} ${colors.green}Plan executed successfully!${colors.reset}\n`;
250
+ } else {
251
+ output += `${symbols.error} ${colors.red}Plan execution failed${colors.reset}\n`;
252
+ }
253
+
254
+ const successCount = results.steps?.filter(s => s.success).length || 0;
255
+ const totalCount = results.steps?.length || 0;
256
+
257
+ output += `\nSteps completed: ${successCount}/${totalCount}\n`;
258
+ output += `Tokens saved: ~${(results.tokensSaved || 0).toLocaleString()}\n`;
259
+
260
+ if (results.escalateToCloud?.length > 0) {
261
+ output += `\n${colors.yellow}${symbols.warning} Steps requiring escalation:${colors.reset}\n`;
262
+ for (const step of results.escalateToCloud) {
263
+ output += ` ${symbols.bullet} Step ${step.id}: ${step.title}\n`;
264
+ }
265
+ }
266
+
267
+ output += `\n${colors.dim}Results saved to: .workflow/state/hybrid-results.json${colors.reset}\n`;
268
+
269
+ return output;
270
+ }
271
+
272
+ module.exports = {
273
+ colors,
274
+ symbols,
275
+ ProgressBar,
276
+ Spinner,
277
+ StepDisplay,
278
+ formatBox,
279
+ formatPlanSummary,
280
+ formatOptions,
281
+ formatResults,
282
+ prompt
283
+ };
284
+
285
+ if (require.main === module) {
286
+ const demo = async () => {
287
+ console.log('\n=== Progress Display Demo ===\n');
288
+
289
+ const spinner = new Spinner('Detecting providers...');
290
+ spinner.start();
291
+ await new Promise(r => setTimeout(r, 2000));
292
+ spinner.stop('Providers detected', true);
293
+
294
+ console.log('\nProgress bar:');
295
+ const bar = new ProgressBar(10);
296
+ for (let i = 0; i <= 10; i++) {
297
+ bar.update(i, `Step ${i}/10`);
298
+ await new Promise(r => setTimeout(r, 200));
299
+ }
300
+ bar.complete('All steps done');
301
+
302
+ const plan = {
303
+ task: 'Add user authentication',
304
+ model: 'nemotron-3-nano',
305
+ estimatedTokensSaved: 12000,
306
+ steps: [
307
+ { id: 1, type: 'create-service', title: 'Create authService', params: { path: 'src/services/authService.ts' }, dependsOn: [] },
308
+ { id: 2, type: 'create-hook', title: 'Create useAuth hook', params: { path: 'src/hooks/useAuth.ts' }, dependsOn: [1] }
309
+ ]
310
+ };
311
+ console.log(formatPlanSummary(plan));
312
+
313
+ const options = [
314
+ { key: '1', icon: '✅', label: 'Execute this plan' },
315
+ { key: '2', icon: '✅', label: 'Execute, skip future reviews' },
316
+ { key: '3', icon: '✏️', label: 'Modify something' },
317
+ { key: '4', icon: '❌', label: 'Cancel' }
318
+ ];
319
+ console.log(formatOptions(options));
320
+ };
321
+
322
+ demo();
323
+ }