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,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
|
+
}
|