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,606 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Task Analyzer
5
+ *
6
+ * Analyzes task descriptions to determine complexity, domains,
7
+ * languages, and estimated token effort for intelligent model routing.
8
+ *
9
+ * Part of Phase 2: Multi-Model Core
10
+ *
11
+ * Usage:
12
+ * flow task-analyze "<description>" [--type feature]
13
+ * flow task-analyze --file .workflow/changes/general/wf-xxx.md
14
+ * flow task-analyze --json
15
+ */
16
+
17
+ const fs = require('fs');
18
+ const path = require('path');
19
+ const {
20
+ PROJECT_ROOT,
21
+ parseFlags,
22
+ outputJson,
23
+ color,
24
+ info,
25
+ error,
26
+ fileExists,
27
+ printHeader,
28
+ printSection,
29
+ isPathWithinProject
30
+ } = require('./flow-utils');
31
+
32
+ // ============================================================
33
+ // Constants
34
+ // ============================================================
35
+
36
+ const COMPLEXITY_THRESHOLDS = {
37
+ HIGH: {
38
+ minIndicators: 5,
39
+ keywords: ['architecture', 'refactor', 'migrate', 'redesign', 'overhaul', 'system', 'infrastructure'],
40
+ fileCountEstimate: 10
41
+ },
42
+ MEDIUM: {
43
+ minIndicators: 3,
44
+ keywords: ['feature', 'implement', 'add', 'create', 'integrate', 'update', 'modify'],
45
+ fileCountEstimate: 5
46
+ },
47
+ LOW: {
48
+ minIndicators: 0,
49
+ keywords: ['fix', 'bug', 'typo', 'rename', 'comment', 'format', 'simple', 'quick'],
50
+ fileCountEstimate: 2
51
+ }
52
+ };
53
+
54
+ const DOMAIN_PATTERNS = {
55
+ api: {
56
+ keywords: ['api', 'endpoint', 'rest', 'graphql', 'route', 'controller', 'request', 'response'],
57
+ weight: 1.0
58
+ },
59
+ database: {
60
+ keywords: ['database', 'db', 'query', 'model', 'entity', 'schema', 'migration', 'sql', 'orm'],
61
+ weight: 1.0
62
+ },
63
+ frontend: {
64
+ keywords: ['component', 'ui', 'view', 'page', 'screen', 'button', 'form', 'modal', 'layout'],
65
+ weight: 1.0
66
+ },
67
+ auth: {
68
+ keywords: ['auth', 'login', 'session', 'token', 'permission', 'role', 'security', 'oauth'],
69
+ weight: 1.2
70
+ },
71
+ testing: {
72
+ keywords: ['test', 'spec', 'mock', 'fixture', 'coverage', 'e2e', 'unit', 'integration'],
73
+ weight: 0.8
74
+ },
75
+ config: {
76
+ keywords: ['config', 'setting', 'env', 'environment', 'variable', 'option'],
77
+ weight: 0.6
78
+ },
79
+ cli: {
80
+ keywords: ['cli', 'command', 'script', 'terminal', 'shell', 'bash'],
81
+ weight: 0.8
82
+ },
83
+ infrastructure: {
84
+ keywords: ['deploy', 'ci', 'cd', 'docker', 'kubernetes', 'aws', 'cloud', 'pipeline'],
85
+ weight: 1.2
86
+ }
87
+ };
88
+
89
+ const LANGUAGE_PATTERNS = {
90
+ typescript: {
91
+ keywords: ['typescript', 'ts', 'tsx', '.ts', 'interface', 'type ', 'generic'],
92
+ extensions: ['.ts', '.tsx'],
93
+ weight: 1.0
94
+ },
95
+ javascript: {
96
+ keywords: ['javascript', 'js', 'jsx', '.js', 'node', 'npm'],
97
+ extensions: ['.js', '.jsx', '.mjs'],
98
+ weight: 1.0
99
+ },
100
+ python: {
101
+ keywords: ['python', 'py', '.py', 'pip', 'django', 'flask', 'fastapi'],
102
+ extensions: ['.py'],
103
+ weight: 1.0
104
+ },
105
+ go: {
106
+ keywords: ['golang', 'go ', '.go'],
107
+ extensions: ['.go'],
108
+ weight: 1.0
109
+ },
110
+ rust: {
111
+ keywords: ['rust', 'cargo', '.rs'],
112
+ extensions: ['.rs'],
113
+ weight: 1.0
114
+ },
115
+ java: {
116
+ keywords: ['java', 'spring', 'maven', 'gradle', '.java'],
117
+ extensions: ['.java'],
118
+ weight: 1.0
119
+ },
120
+ sql: {
121
+ keywords: ['sql', 'query', 'select', 'insert', 'update', 'delete', 'join'],
122
+ extensions: ['.sql'],
123
+ weight: 0.5
124
+ },
125
+ shell: {
126
+ keywords: ['bash', 'shell', 'sh', 'script'],
127
+ extensions: ['.sh', '.bash'],
128
+ weight: 0.5
129
+ }
130
+ };
131
+
132
+ /**
133
+ * Capability requirements mapped to task keywords
134
+ *
135
+ * Note: 'vision' and 'extended-thinking' capabilities are future-proofing
136
+ * for when model registry includes multimodal and o1-style models.
137
+ * Currently maps to Claude-4 capabilities.
138
+ */
139
+ const CAPABILITY_REQUIREMENTS = {
140
+ 'reasoning': ['architecture', 'design', 'system', 'algorithm', 'optimize', 'complex'],
141
+ 'code-gen': ['implement', 'create', 'add', 'build', 'feature', 'component'],
142
+ 'analysis': ['review', 'analyze', 'audit', 'evaluate', 'assess'],
143
+ 'structured-output': ['schema', 'json', 'config', 'template', 'format'],
144
+ 'vision': ['design', 'figma', 'screenshot', 'mockup', 'ui'], // Future: multimodal models
145
+ 'extended-thinking': ['difficult', 'challenging', 'intricate', 'debug'] // Future: o1-style models
146
+ };
147
+
148
+ /**
149
+ * Token estimation factors
150
+ *
151
+ * Values calibrated from observed Claude Code task executions:
152
+ * - BASE_INPUT: Minimum tokens for task context, instructions, and rules
153
+ * - PER_FILE: Average tokens per file change (read + edit operations)
154
+ * - PER_DOMAIN: Additional context tokens per technical domain
155
+ * - COMPLEXITY_MULTIPLIER: Accounts for more iterations/exploration
156
+ * - OUTPUT_RATIO: Model output typically 60% of input for code tasks
157
+ */
158
+ const TOKEN_FACTORS = {
159
+ BASE_INPUT: 500,
160
+ PER_FILE: 200,
161
+ PER_DOMAIN: 150,
162
+ COMPLEXITY_MULTIPLIER: {
163
+ low: 1.0,
164
+ medium: 1.5,
165
+ high: 2.5
166
+ },
167
+ OUTPUT_RATIO: 0.6
168
+ };
169
+
170
+ // ============================================================
171
+ // Analysis Functions
172
+ // ============================================================
173
+
174
+ /**
175
+ * Analyze task complexity
176
+ * @param {string} text - Combined task text (title + description)
177
+ * @param {string} taskType - Task type (feature, bugfix, refactor, etc.)
178
+ * @returns {Object} Complexity analysis
179
+ */
180
+ function analyzeComplexity(text, taskType) {
181
+ const lowerText = text.toLowerCase();
182
+ const indicators = [];
183
+
184
+ // Check high complexity indicators
185
+ for (const keyword of COMPLEXITY_THRESHOLDS.HIGH.keywords) {
186
+ if (lowerText.includes(keyword)) {
187
+ indicators.push({ keyword, level: 'high', weight: 2 });
188
+ }
189
+ }
190
+
191
+ // Check medium complexity indicators
192
+ for (const keyword of COMPLEXITY_THRESHOLDS.MEDIUM.keywords) {
193
+ if (lowerText.includes(keyword)) {
194
+ indicators.push({ keyword, level: 'medium', weight: 1 });
195
+ }
196
+ }
197
+
198
+ // Check low complexity indicators
199
+ for (const keyword of COMPLEXITY_THRESHOLDS.LOW.keywords) {
200
+ if (lowerText.includes(keyword)) {
201
+ indicators.push({ keyword, level: 'low', weight: 0.5 });
202
+ }
203
+ }
204
+
205
+ // Task type influences complexity
206
+ const typeComplexity = {
207
+ architecture: 'high',
208
+ refactor: 'medium',
209
+ feature: 'medium',
210
+ bugfix: 'low',
211
+ 'quick-edit': 'low',
212
+ documentation: 'low'
213
+ };
214
+
215
+ // Calculate weighted score
216
+ const score = indicators.reduce((sum, ind) => sum + ind.weight, 0);
217
+ const typeHint = typeComplexity[taskType] || 'medium';
218
+
219
+ // Determine final complexity
220
+ let complexity;
221
+ if (score >= COMPLEXITY_THRESHOLDS.HIGH.minIndicators || typeHint === 'high') {
222
+ complexity = 'high';
223
+ } else if (score >= COMPLEXITY_THRESHOLDS.MEDIUM.minIndicators || typeHint === 'medium') {
224
+ complexity = 'medium';
225
+ } else {
226
+ complexity = 'low';
227
+ }
228
+
229
+ // Estimate file count
230
+ const fileEstimate = COMPLEXITY_THRESHOLDS[complexity.toUpperCase()].fileCountEstimate;
231
+
232
+ return {
233
+ level: complexity,
234
+ score,
235
+ indicators: indicators.slice(0, 5), // Top 5 indicators
236
+ estimatedFiles: fileEstimate,
237
+ confidence: Math.min(0.9, 0.5 + (score * 0.1))
238
+ };
239
+ }
240
+
241
+ /**
242
+ * Detect domains involved in task
243
+ * @param {string} text - Combined task text
244
+ * @returns {Object} Domain analysis
245
+ */
246
+ function detectDomains(text) {
247
+ const lowerText = text.toLowerCase();
248
+ const detected = {};
249
+
250
+ for (const [domain, config] of Object.entries(DOMAIN_PATTERNS)) {
251
+ const matches = config.keywords.filter(kw => lowerText.includes(kw));
252
+ if (matches.length > 0) {
253
+ detected[domain] = {
254
+ matches,
255
+ score: matches.length * config.weight,
256
+ weight: config.weight
257
+ };
258
+ }
259
+ }
260
+
261
+ // Sort by score and return top domains
262
+ const sorted = Object.entries(detected)
263
+ .sort(([, a], [, b]) => b.score - a.score);
264
+
265
+ return {
266
+ primary: sorted[0]?.[0] || 'general',
267
+ all: sorted.map(([name, data]) => ({
268
+ name,
269
+ score: data.score,
270
+ matches: data.matches
271
+ })),
272
+ count: sorted.length
273
+ };
274
+ }
275
+
276
+ /**
277
+ * Detect languages required
278
+ * @param {string} text - Combined task text
279
+ * @returns {Object} Language analysis
280
+ */
281
+ function detectLanguages(text) {
282
+ const lowerText = text.toLowerCase();
283
+ const detected = {};
284
+
285
+ for (const [lang, config] of Object.entries(LANGUAGE_PATTERNS)) {
286
+ const matches = config.keywords.filter(kw => lowerText.includes(kw));
287
+ if (matches.length > 0) {
288
+ detected[lang] = {
289
+ matches,
290
+ score: matches.length * config.weight
291
+ };
292
+ }
293
+ }
294
+
295
+ // If no explicit language detected, infer from project
296
+ if (Object.keys(detected).length === 0) {
297
+ // Default to project's primary language (check package.json existence)
298
+ if (fileExists(path.join(PROJECT_ROOT, 'package.json'))) {
299
+ if (fileExists(path.join(PROJECT_ROOT, 'tsconfig.json'))) {
300
+ detected.typescript = { matches: ['inferred'], score: 0.5 };
301
+ } else {
302
+ detected.javascript = { matches: ['inferred'], score: 0.5 };
303
+ }
304
+ }
305
+ }
306
+
307
+ const sorted = Object.entries(detected)
308
+ .sort(([, a], [, b]) => b.score - a.score);
309
+
310
+ return {
311
+ primary: sorted[0]?.[0] || 'unknown',
312
+ all: sorted.map(([name, data]) => ({
313
+ name,
314
+ score: data.score
315
+ })),
316
+ count: sorted.length
317
+ };
318
+ }
319
+
320
+ /**
321
+ * Determine required model capabilities
322
+ * @param {string} text - Combined task text
323
+ * @param {Object} complexity - Complexity analysis
324
+ * @returns {string[]} Required capabilities
325
+ */
326
+ function determineCapabilities(text, complexity) {
327
+ const lowerText = text.toLowerCase();
328
+ const required = new Set();
329
+
330
+ for (const [capability, keywords] of Object.entries(CAPABILITY_REQUIREMENTS)) {
331
+ for (const kw of keywords) {
332
+ if (lowerText.includes(kw)) {
333
+ required.add(capability);
334
+ break;
335
+ }
336
+ }
337
+ }
338
+
339
+ // High complexity always needs reasoning
340
+ if (complexity.level === 'high') {
341
+ required.add('reasoning');
342
+ }
343
+
344
+ // All tasks need code generation unless pure analysis
345
+ if (!required.has('analysis') || required.size > 1) {
346
+ required.add('code-gen');
347
+ }
348
+
349
+ return Array.from(required);
350
+ }
351
+
352
+ /**
353
+ * Estimate token usage
354
+ * @param {Object} analysis - Full analysis object
355
+ * @returns {Object} Token estimates
356
+ */
357
+ function estimateTokens(analysis) {
358
+ const { complexity, domains, languages } = analysis;
359
+
360
+ const multiplier = TOKEN_FACTORS.COMPLEXITY_MULTIPLIER[complexity.level];
361
+ const baseInput = TOKEN_FACTORS.BASE_INPUT;
362
+ const fileTokens = complexity.estimatedFiles * TOKEN_FACTORS.PER_FILE;
363
+ const domainTokens = domains.count * TOKEN_FACTORS.PER_DOMAIN;
364
+
365
+ const inputTokens = Math.round((baseInput + fileTokens + domainTokens) * multiplier);
366
+ const outputTokens = Math.round(inputTokens * TOKEN_FACTORS.OUTPUT_RATIO);
367
+
368
+ return {
369
+ estimated: {
370
+ input: inputTokens,
371
+ output: outputTokens,
372
+ total: inputTokens + outputTokens
373
+ },
374
+ confidence: complexity.confidence,
375
+ factors: {
376
+ baseInput,
377
+ fileTokens,
378
+ domainTokens,
379
+ multiplier
380
+ }
381
+ };
382
+ }
383
+
384
+ /**
385
+ * Full task analysis
386
+ * @param {Object} params - Analysis parameters
387
+ * @returns {Object} Complete analysis
388
+ */
389
+ function analyzeTask(params) {
390
+ const { title, description = '', type = 'feature', acceptanceCriteria = [] } = params;
391
+
392
+ // Combine all text for analysis
393
+ const text = [
394
+ title,
395
+ description,
396
+ ...acceptanceCriteria
397
+ ].join(' ');
398
+
399
+ // Run all analyses
400
+ const complexity = analyzeComplexity(text, type);
401
+ const domains = detectDomains(text);
402
+ const languages = detectLanguages(text);
403
+ const capabilities = determineCapabilities(text, complexity);
404
+
405
+ const analysis = {
406
+ complexity,
407
+ domains,
408
+ languages,
409
+ capabilities,
410
+ taskType: type
411
+ };
412
+
413
+ // Add token estimates
414
+ analysis.tokens = estimateTokens(analysis);
415
+
416
+ // Add timestamp
417
+ analysis.analyzedAt = new Date().toISOString();
418
+
419
+ return analysis;
420
+ }
421
+
422
+ // ============================================================
423
+ // File Parsing
424
+ // ============================================================
425
+
426
+ /**
427
+ * Parse story file for analysis input
428
+ * @param {string} filePath - Path to story file
429
+ * @returns {Object} Parsed content
430
+ */
431
+ function parseStoryFile(filePath) {
432
+ if (!fileExists(filePath)) {
433
+ return null;
434
+ }
435
+
436
+ try {
437
+ const content = fs.readFileSync(filePath, 'utf-8');
438
+
439
+ // Extract title from header
440
+ const titleMatch = content.match(/^#\s*\[[\w-]+\]\s*(.+)$/m);
441
+ const title = titleMatch ? titleMatch[1].trim() : '';
442
+
443
+ // Extract description
444
+ const descMatch = content.match(/## Description\s*\n([\s\S]*?)(?=\n## |\n$)/);
445
+ const description = descMatch ? descMatch[1].trim() : '';
446
+
447
+ // Extract acceptance criteria
448
+ const criteriaMatches = content.matchAll(/\*\*(Given|When|Then|And)\*\*\s*(.+)/g);
449
+ const criteria = Array.from(criteriaMatches).map(m => m[2].trim());
450
+
451
+ // Extract task type from content
452
+ let type = 'feature';
453
+ if (content.includes('bugfix') || content.includes('fix bug')) {
454
+ type = 'bugfix';
455
+ } else if (content.includes('refactor')) {
456
+ type = 'refactor';
457
+ } else if (content.includes('architecture')) {
458
+ type = 'architecture';
459
+ }
460
+
461
+ return {
462
+ title,
463
+ description,
464
+ acceptanceCriteria: criteria,
465
+ type
466
+ };
467
+ } catch (err) {
468
+ // File read error (permission denied, race condition, etc.)
469
+ console.error(`Warning: Could not read story file ${filePath}: ${err.message}`);
470
+ return null;
471
+ }
472
+ }
473
+
474
+ // ============================================================
475
+ // CLI Output
476
+ // ============================================================
477
+
478
+ /**
479
+ * Print analysis results
480
+ * @param {Object} analysis - Analysis result
481
+ */
482
+ function printAnalysis(analysis) {
483
+ printHeader('TASK ANALYSIS');
484
+
485
+ // Complexity
486
+ printSection('Complexity');
487
+ const complexityColor = {
488
+ low: 'green',
489
+ medium: 'yellow',
490
+ high: 'red'
491
+ }[analysis.complexity.level];
492
+ console.log(` Level: ${color(complexityColor, analysis.complexity.level.toUpperCase())}`);
493
+ console.log(` Score: ${analysis.complexity.score}`);
494
+ console.log(` Estimated files: ${analysis.complexity.estimatedFiles}`);
495
+ console.log(` Confidence: ${(analysis.complexity.confidence * 100).toFixed(0)}%`);
496
+ if (analysis.complexity.indicators.length > 0) {
497
+ console.log(` Indicators: ${analysis.complexity.indicators.map(i => i.keyword).join(', ')}`);
498
+ }
499
+
500
+ // Domains
501
+ printSection('Domains');
502
+ console.log(` Primary: ${color('cyan', analysis.domains.primary)}`);
503
+ if (analysis.domains.all.length > 1) {
504
+ console.log(` All: ${analysis.domains.all.map(d => d.name).join(', ')}`);
505
+ }
506
+
507
+ // Languages
508
+ printSection('Languages');
509
+ console.log(` Primary: ${color('cyan', analysis.languages.primary)}`);
510
+ if (analysis.languages.all.length > 1) {
511
+ console.log(` All: ${analysis.languages.all.map(l => l.name).join(', ')}`);
512
+ }
513
+
514
+ // Capabilities
515
+ printSection('Required Capabilities');
516
+ for (const cap of analysis.capabilities) {
517
+ console.log(` - ${cap}`);
518
+ }
519
+
520
+ // Token Estimates
521
+ printSection('Token Estimates');
522
+ const tokens = analysis.tokens.estimated;
523
+ console.log(` Input: ~${tokens.input.toLocaleString()} tokens`);
524
+ console.log(` Output: ~${tokens.output.toLocaleString()} tokens`);
525
+ console.log(` Total: ~${tokens.total.toLocaleString()} tokens`);
526
+
527
+ console.log('');
528
+ }
529
+
530
+ // ============================================================
531
+ // Main
532
+ // ============================================================
533
+
534
+ async function main() {
535
+ const { positional, flags } = parseFlags(process.argv.slice(2));
536
+
537
+ let taskData;
538
+
539
+ // Parse from file or positional args
540
+ if (flags.file) {
541
+ const filePath = path.isAbsolute(flags.file)
542
+ ? flags.file
543
+ : path.join(PROJECT_ROOT, flags.file);
544
+
545
+ // Validate path is within project to prevent path traversal
546
+ if (!isPathWithinProject(filePath)) {
547
+ error('File path must be within project directory');
548
+ process.exit(1);
549
+ }
550
+
551
+ taskData = parseStoryFile(filePath);
552
+ if (!taskData) {
553
+ error(`Could not read story file: ${flags.file}`);
554
+ process.exit(1);
555
+ }
556
+ } else if (positional.length > 0) {
557
+ taskData = {
558
+ title: positional.join(' '),
559
+ description: '',
560
+ acceptanceCriteria: [],
561
+ type: flags.type || 'feature'
562
+ };
563
+ } else {
564
+ error('Usage: flow task-analyze "<description>" [--type feature]');
565
+ error(' flow task-analyze --file .workflow/changes/general/wf-xxx.md');
566
+ process.exit(1);
567
+ }
568
+
569
+ // Run analysis
570
+ const analysis = analyzeTask(taskData);
571
+
572
+ // Output
573
+ if (flags.json) {
574
+ outputJson({
575
+ success: true,
576
+ input: taskData,
577
+ analysis
578
+ });
579
+ } else {
580
+ info(`Analyzing: "${taskData.title}"`);
581
+ console.log('');
582
+ printAnalysis(analysis);
583
+ }
584
+ }
585
+
586
+ // Export for use by other scripts
587
+ module.exports = {
588
+ analyzeTask,
589
+ analyzeComplexity,
590
+ detectDomains,
591
+ detectLanguages,
592
+ determineCapabilities,
593
+ estimateTokens,
594
+ parseStoryFile,
595
+ COMPLEXITY_THRESHOLDS,
596
+ DOMAIN_PATTERNS,
597
+ LANGUAGE_PATTERNS,
598
+ CAPABILITY_REQUIREMENTS
599
+ };
600
+
601
+ if (require.main === module) {
602
+ main().catch(err => {
603
+ error(err.message);
604
+ process.exit(1);
605
+ });
606
+ }