aiwf 0.3.22 → 0.3.23
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/package.json +3 -2
- package/src/cli/index.js +39 -336
- package/src/lib/installer.js +294 -90
- package/src/utils/messages.js +24 -0
- package/src/DEPENDENCY_MAP.md +0 -90
- package/src/cli/cache-cli.js +0 -459
- package/src/cli/checkpoint-cli.js +0 -417
- package/src/cli/language-cli.js +0 -286
- package/src/cli/sprint-cli.js +0 -287
- package/src/commands/ai-tool.js +0 -383
- package/src/commands/compress.js +0 -60
- package/src/commands/create-project.js +0 -160
- package/src/commands/evaluate.js +0 -459
- package/src/commands/persona.js +0 -309
- package/src/commands/sprint-task.js +0 -262
- package/src/commands/token.js +0 -312
- package/src/lib/ai-persona-manager.js +0 -711
- package/src/lib/cache-system.js +0 -332
- package/src/lib/context-engine.js +0 -568
- package/src/lib/github-integration.js +0 -402
- package/src/lib/memory-profiler.js +0 -471
- package/src/lib/metrics-collector.js +0 -885
- package/src/lib/offline-detector.js +0 -386
- package/src/lib/resource-loader-enhanced.js +0 -399
- package/src/lib/resource-loader.js +0 -250
- package/src/lib/resources/commands/ai-persona.js +0 -602
- package/src/lib/resources/commands/compress-context.js +0 -389
- package/src/lib/resources/commands/evaluate.js +0 -246
- package/src/lib/resources/commands/persona-context-apply.js +0 -562
- package/src/lib/resources/commands/token-tracking.js +0 -443
- package/src/lib/resources/config/commit-patterns.js +0 -103
- package/src/lib/resources/config/language.json +0 -6
- package/src/lib/resources/personas/PERSONA_INDEX.md +0 -57
- package/src/lib/resources/personas/analyst.json +0 -44
- package/src/lib/resources/personas/architect/best_practices.md +0 -136
- package/src/lib/resources/personas/architect/knowledge_base.md +0 -377
- package/src/lib/resources/personas/architect.json +0 -44
- package/src/lib/resources/personas/backend/best_practices.md +0 -766
- package/src/lib/resources/personas/backend/knowledge_base.md +0 -1070
- package/src/lib/resources/personas/data_analyst/best_practices.md +0 -594
- package/src/lib/resources/personas/data_analyst/knowledge_base.md +0 -1057
- package/src/lib/resources/personas/developer.json +0 -44
- package/src/lib/resources/personas/developer.md +0 -37
- package/src/lib/resources/personas/evaluation_criteria.json +0 -137
- package/src/lib/resources/personas/frontend/best_practices.md +0 -445
- package/src/lib/resources/personas/frontend/knowledge_base.md +0 -729
- package/src/lib/resources/personas/persona-index.json +0 -31
- package/src/lib/resources/personas/reviewer.json +0 -44
- package/src/lib/resources/personas/security/best_practices.md +0 -307
- package/src/lib/resources/personas/security/knowledge_base.md +0 -498
- package/src/lib/resources/personas/tester.json +0 -44
- package/src/lib/resources/templates/README.md +0 -78
- package/src/lib/resources/templates/api-server/config.json +0 -64
- package/src/lib/resources/templates/api-server/template/.aiwf/config.json +0 -56
- package/src/lib/resources/templates/api-server/template/.aiwf/feature-ledger.json +0 -42
- package/src/lib/resources/templates/api-server/template/.aiwf/personas/backend-engineer.json +0 -40
- package/src/lib/resources/templates/api-server/template/.aiwf/scripts/cli.js +0 -111
- package/src/lib/resources/templates/api-server/template/.env.example +0 -29
- package/src/lib/resources/templates/api-server/template/.eslintrc.json +0 -23
- package/src/lib/resources/templates/api-server/template/README.md +0 -171
- package/src/lib/resources/templates/api-server/template/jest.config.js +0 -26
- package/src/lib/resources/templates/api-server/template/nodemon.json +0 -9
- package/src/lib/resources/templates/api-server/template/package.json +0 -61
- package/src/lib/resources/templates/api-server/template/src/app.ts +0 -60
- package/src/lib/resources/templates/api-server/template/src/config/swagger.ts +0 -38
- package/src/lib/resources/templates/api-server/template/src/controllers/aiwfController.ts +0 -54
- package/src/lib/resources/templates/api-server/template/src/controllers/authController.ts +0 -128
- package/src/lib/resources/templates/api-server/template/src/controllers/statusController.ts +0 -28
- package/src/lib/resources/templates/api-server/template/src/index.ts +0 -28
- package/src/lib/resources/templates/api-server/template/src/middleware/aiwfMiddleware.ts +0 -50
- package/src/lib/resources/templates/api-server/template/src/middleware/authMiddleware.ts +0 -41
- package/src/lib/resources/templates/api-server/template/src/middleware/errorHandler.ts +0 -42
- package/src/lib/resources/templates/api-server/template/src/middleware/notFoundHandler.ts +0 -14
- package/src/lib/resources/templates/api-server/template/src/middleware/requestLogger.ts +0 -20
- package/src/lib/resources/templates/api-server/template/src/routes/aiwf.ts +0 -31
- package/src/lib/resources/templates/api-server/template/src/routes/index.ts +0 -13
- package/src/lib/resources/templates/api-server/template/src/routes/v1/index.ts +0 -67
- package/src/lib/resources/templates/api-server/template/src/utils/logger.ts +0 -37
- package/src/lib/resources/templates/api-server/template/tests/app.test.ts +0 -40
- package/src/lib/resources/templates/api-server/template/tsconfig.json +0 -43
- package/src/lib/resources/templates/npm-library/config.json +0 -61
- package/src/lib/resources/templates/npm-library/template/LICENSE +0 -21
- package/src/lib/resources/templates/npm-library/template/README.md +0 -201
- package/src/lib/resources/templates/npm-library/template/package.json +0 -78
- package/src/lib/resources/templates/web-app/config.json +0 -53
- package/src/lib/resources/templates/web-app/template/.aiwf/config.json +0 -54
- package/src/lib/resources/templates/web-app/template/.aiwf/feature-ledger.json +0 -37
- package/src/lib/resources/templates/web-app/template/.aiwf/personas/fullstack-developer.json +0 -40
- package/src/lib/resources/templates/web-app/template/.aiwf/scripts/cli.js +0 -110
- package/src/lib/resources/templates/web-app/template/.eslintrc.cjs +0 -20
- package/src/lib/resources/templates/web-app/template/README.md +0 -151
- package/src/lib/resources/templates/web-app/template/index.html +0 -14
- package/src/lib/resources/templates/web-app/template/package.json +0 -44
- package/src/lib/resources/templates/web-app/template/postcss.config.js +0 -6
- package/src/lib/resources/templates/web-app/template/public/vite.svg +0 -1
- package/src/lib/resources/templates/web-app/template/src/App.tsx +0 -21
- package/src/lib/resources/templates/web-app/template/src/components/Layout.tsx +0 -57
- package/src/lib/resources/templates/web-app/template/src/components/aiwf/ContextStatus.tsx +0 -107
- package/src/lib/resources/templates/web-app/template/src/components/aiwf/TokenUsage.tsx +0 -71
- package/src/lib/resources/templates/web-app/template/src/index.css +0 -60
- package/src/lib/resources/templates/web-app/template/src/main.tsx +0 -10
- package/src/lib/resources/templates/web-app/template/src/pages/AiwfDashboard.tsx +0 -57
- package/src/lib/resources/templates/web-app/template/src/pages/HomePage.tsx +0 -89
- package/src/lib/resources/templates/web-app/template/src/pages/NotFound.tsx +0 -25
- package/src/lib/resources/templates/web-app/template/src/stores/aiwfStore.ts +0 -126
- package/src/lib/resources/templates/web-app/template/src/types/global.d.ts +0 -9
- package/src/lib/resources/templates/web-app/template/src/vite-env.d.ts +0 -1
- package/src/lib/resources/templates/web-app/template/tailwind.config.js +0 -30
- package/src/lib/resources/templates/web-app/template/tsconfig.json +0 -37
- package/src/lib/resources/templates/web-app/template/tsconfig.node.json +0 -10
- package/src/lib/resources/templates/web-app/template/vite.config.ts +0 -23
- package/src/lib/resources/utils/background-monitor.js +0 -223
- package/src/lib/resources/utils/compression-metrics.js +0 -1031
- package/src/lib/resources/utils/compression-strategies.js +0 -557
- package/src/lib/resources/utils/content-normalizer.js +0 -455
- package/src/lib/resources/utils/context-compressor.js +0 -583
- package/src/lib/resources/utils/context-rule-parser.js +0 -180
- package/src/lib/resources/utils/context-token-monitor.js +0 -463
- package/src/lib/resources/utils/context-update-manager.js +0 -502
- package/src/lib/resources/utils/git-utils.js +0 -220
- package/src/lib/resources/utils/importance-classifier.js +0 -645
- package/src/lib/resources/utils/information-filter.js +0 -710
- package/src/lib/resources/utils/persona-aware-compressor.js +0 -598
- package/src/lib/resources/utils/prompt-injector.js +0 -242
- package/src/lib/resources/utils/simplified-evaluator.js +0 -119
- package/src/lib/resources/utils/text-summarizer.js +0 -492
- package/src/lib/resources/utils/token-counter.js +0 -177
- package/src/lib/resources/utils/token-monitor.js +0 -484
- package/src/lib/resources/utils/token-reporter.js +0 -452
- package/src/lib/resources/utils/token-storage.js +0 -433
- package/src/lib/resources/utils/token-tracker.js +0 -283
- package/src/lib/state/priority-calculator.js +0 -217
- package/src/lib/state/state-index.js +0 -155
- package/src/lib/state/task-scanner.js +0 -328
- package/src/lib/task-analyzer.js +0 -599
- package/src/lib/template-cache-system.js +0 -559
- package/src/lib/template-downloader.js +0 -467
- package/src/lib/template-version-manager.js +0 -491
- package/src/lib/token-optimizer.js +0 -517
- package/src/utils/engineering-guard.js +0 -399
package/src/lib/task-analyzer.js
DELETED
|
@@ -1,599 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Task Analyzer
|
|
3
|
-
* Analyzes tasks to determine the optimal AI persona
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import fs from 'fs/promises';
|
|
7
|
-
import path from 'path';
|
|
8
|
-
import { EventEmitter } from 'events';
|
|
9
|
-
import { execSync } from 'child_process';
|
|
10
|
-
|
|
11
|
-
class TaskAnalyzer extends EventEmitter {
|
|
12
|
-
constructor(options = {}) {
|
|
13
|
-
super();
|
|
14
|
-
|
|
15
|
-
this.options = {
|
|
16
|
-
recentFileLimit: options.recentFileLimit || 10,
|
|
17
|
-
keywordWeight: options.keywordWeight || 0.4,
|
|
18
|
-
patternWeight: options.patternWeight || 0.3,
|
|
19
|
-
contextWeight: options.contextWeight || 0.2,
|
|
20
|
-
historyWeight: options.historyWeight || 0.1,
|
|
21
|
-
...options
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
// Pattern definitions for each persona
|
|
25
|
-
this.patterns = {
|
|
26
|
-
architect: {
|
|
27
|
-
keywords: [
|
|
28
|
-
'design', 'architect', 'structure', 'system', 'scalab',
|
|
29
|
-
'interface', 'api', 'contract', 'schema', 'pattern',
|
|
30
|
-
'module', 'component', 'integration', 'dependency'
|
|
31
|
-
],
|
|
32
|
-
filePatterns: ['*.md', '*.yml', '*.yaml', 'architecture/**', 'design/**'],
|
|
33
|
-
contextIndicators: ['creating new feature', 'planning', 'designing']
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
debugger: {
|
|
37
|
-
keywords: [
|
|
38
|
-
'bug', 'error', 'fix', 'issue', 'problem', 'debug',
|
|
39
|
-
'trace', 'stack', 'exception', 'fail', 'crash',
|
|
40
|
-
'investigate', 'diagnose', 'troubleshoot'
|
|
41
|
-
],
|
|
42
|
-
filePatterns: ['*.log', '*.test.*', 'test/**', 'debug/**'],
|
|
43
|
-
contextIndicators: ['error in', 'not working', 'broken', 'failing']
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
reviewer: {
|
|
47
|
-
keywords: [
|
|
48
|
-
'review', 'audit', 'check', 'quality', 'standard',
|
|
49
|
-
'security', 'vulnerability', 'smell', 'refactor',
|
|
50
|
-
'improve', 'optimize', 'clean', 'lint'
|
|
51
|
-
],
|
|
52
|
-
filePatterns: ['*.js', '*.ts', '*.py', 'src/**', 'lib/**'],
|
|
53
|
-
contextIndicators: ['review code', 'check quality', 'pull request']
|
|
54
|
-
},
|
|
55
|
-
|
|
56
|
-
documenter: {
|
|
57
|
-
keywords: [
|
|
58
|
-
'document', 'docs', 'readme', 'guide', 'tutorial',
|
|
59
|
-
'explain', 'describe', 'comment', 'annotation',
|
|
60
|
-
'example', 'usage', 'api', 'reference'
|
|
61
|
-
],
|
|
62
|
-
filePatterns: ['*.md', 'README*', 'docs/**', 'examples/**'],
|
|
63
|
-
contextIndicators: ['write documentation', 'explain', 'document']
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
optimizer: {
|
|
67
|
-
keywords: [
|
|
68
|
-
'optimiz', 'performance', 'speed', 'efficient',
|
|
69
|
-
'benchmark', 'profile', 'memory', 'token',
|
|
70
|
-
'slow', 'bottleneck', 'improve', 'fast'
|
|
71
|
-
],
|
|
72
|
-
filePatterns: ['*.bench.*', 'benchmark/**', 'perf/**'],
|
|
73
|
-
contextIndicators: ['optimize', 'improve performance', 'too slow']
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
developer: {
|
|
77
|
-
keywords: [
|
|
78
|
-
'implement', 'create', 'build', 'develop',
|
|
79
|
-
'add', 'feature', 'function', 'code',
|
|
80
|
-
'write', 'make', 'new', 'update'
|
|
81
|
-
],
|
|
82
|
-
filePatterns: ['*.js', '*.ts', '*.py', 'src/**', 'lib/**'],
|
|
83
|
-
contextIndicators: ['implement', 'create', 'build', 'add feature']
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
// Compile regex patterns
|
|
88
|
-
this.compiledPatterns = {};
|
|
89
|
-
for (const [persona, config] of Object.entries(this.patterns)) {
|
|
90
|
-
this.compiledPatterns[persona] = {
|
|
91
|
-
keywordRegex: new RegExp(config.keywords.join('|'), 'i'),
|
|
92
|
-
contextRegex: new RegExp(config.contextIndicators.join('|'), 'i')
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Analyze a task to determine characteristics
|
|
99
|
-
*/
|
|
100
|
-
async analyzeTask(taskContext) {
|
|
101
|
-
const analysis = {
|
|
102
|
-
command: taskContext.command || '',
|
|
103
|
-
description: taskContext.description || '',
|
|
104
|
-
recentFiles: await this.getRecentFiles(),
|
|
105
|
-
keywords: this.extractKeywords(taskContext),
|
|
106
|
-
gitContext: await this.getGitContext(),
|
|
107
|
-
errorState: await this.checkErrorState(),
|
|
108
|
-
fileTypes: await this.analyzeFileTypes(),
|
|
109
|
-
scores: {},
|
|
110
|
-
timestamp: new Date()
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
// Calculate scores for each persona
|
|
114
|
-
for (const personaName of Object.keys(this.patterns)) {
|
|
115
|
-
analysis.scores[personaName] = await this.calculatePersonaScore(
|
|
116
|
-
personaName,
|
|
117
|
-
analysis
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Determine primary task type
|
|
122
|
-
analysis.primaryType = this.determinePrimaryType(analysis.scores);
|
|
123
|
-
|
|
124
|
-
this.emit('taskAnalyzed', analysis);
|
|
125
|
-
return analysis;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Extract keywords from task context
|
|
130
|
-
*/
|
|
131
|
-
extractKeywords(taskContext) {
|
|
132
|
-
const text = `${taskContext.command || ''} ${taskContext.description || ''}`;
|
|
133
|
-
|
|
134
|
-
// Split into words and filter
|
|
135
|
-
const words = text
|
|
136
|
-
.toLowerCase()
|
|
137
|
-
.split(/\s+/)
|
|
138
|
-
.filter(word => word.length > 2)
|
|
139
|
-
.filter(word => !this.isStopWord(word));
|
|
140
|
-
|
|
141
|
-
// Find matching keywords for any persona
|
|
142
|
-
const keywords = [];
|
|
143
|
-
for (const [persona, config] of Object.entries(this.patterns)) {
|
|
144
|
-
for (const keyword of config.keywords) {
|
|
145
|
-
if (words.some(word => word.includes(keyword))) {
|
|
146
|
-
keywords.push(keyword);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return [...new Set(keywords)]; // Remove duplicates
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Check if word is a stop word
|
|
156
|
-
*/
|
|
157
|
-
isStopWord(word) {
|
|
158
|
-
const stopWords = [
|
|
159
|
-
'the', 'is', 'at', 'which', 'on', 'and', 'a', 'an',
|
|
160
|
-
'as', 'are', 'been', 'by', 'for', 'from', 'has', 'have',
|
|
161
|
-
'in', 'it', 'of', 'or', 'that', 'to', 'was', 'will', 'with'
|
|
162
|
-
];
|
|
163
|
-
return stopWords.includes(word);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Get recently modified files
|
|
168
|
-
*/
|
|
169
|
-
async getRecentFiles() {
|
|
170
|
-
try {
|
|
171
|
-
// Use git to get recently modified files
|
|
172
|
-
const output = execSync(
|
|
173
|
-
`git log --name-only --pretty=format: -${this.options.recentFileLimit} | sort | uniq | grep -v '^$'`,
|
|
174
|
-
{ encoding: 'utf-8', cwd: process.cwd() }
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
return output
|
|
178
|
-
.split('\n')
|
|
179
|
-
.filter(file => file.trim())
|
|
180
|
-
.slice(0, this.options.recentFileLimit);
|
|
181
|
-
} catch (error) {
|
|
182
|
-
// Fallback to file system scan
|
|
183
|
-
return await this.scanRecentFiles();
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Scan file system for recent files
|
|
189
|
-
*/
|
|
190
|
-
async scanRecentFiles() {
|
|
191
|
-
const files = [];
|
|
192
|
-
const now = Date.now();
|
|
193
|
-
const maxAge = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
194
|
-
|
|
195
|
-
async function scan(dir, level = 0) {
|
|
196
|
-
if (level > 3) return; // Max depth
|
|
197
|
-
|
|
198
|
-
try {
|
|
199
|
-
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
200
|
-
|
|
201
|
-
for (const entry of entries) {
|
|
202
|
-
if (entry.name.startsWith('.') || entry.name === 'node_modules') {
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const fullPath = path.join(dir, entry.name);
|
|
207
|
-
|
|
208
|
-
if (entry.isDirectory()) {
|
|
209
|
-
await scan(fullPath, level + 1);
|
|
210
|
-
} else {
|
|
211
|
-
const stats = await fs.stat(fullPath);
|
|
212
|
-
if (now - stats.mtime.getTime() < maxAge) {
|
|
213
|
-
files.push({
|
|
214
|
-
path: path.relative(process.cwd(), fullPath),
|
|
215
|
-
modified: stats.mtime
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
} catch (error) {
|
|
221
|
-
// Ignore errors
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
await scan(process.cwd());
|
|
226
|
-
|
|
227
|
-
// Sort by modification time and return paths
|
|
228
|
-
return files
|
|
229
|
-
.sort((a, b) => b.modified - a.modified)
|
|
230
|
-
.slice(0, this.options.recentFileLimit)
|
|
231
|
-
.map(f => f.path);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Get git context
|
|
236
|
-
*/
|
|
237
|
-
async getGitContext() {
|
|
238
|
-
const context = {
|
|
239
|
-
branch: null,
|
|
240
|
-
isPullRequest: false,
|
|
241
|
-
hasUncommittedChanges: false,
|
|
242
|
-
recentCommitTypes: []
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
try {
|
|
246
|
-
// Get current branch
|
|
247
|
-
context.branch = execSync('git branch --show-current', {
|
|
248
|
-
encoding: 'utf-8'
|
|
249
|
-
}).trim();
|
|
250
|
-
|
|
251
|
-
// Check if PR branch
|
|
252
|
-
context.isPullRequest = context.branch.includes('feature/') ||
|
|
253
|
-
context.branch.includes('fix/') ||
|
|
254
|
-
context.branch.includes('pr/');
|
|
255
|
-
|
|
256
|
-
// Check for uncommitted changes
|
|
257
|
-
const status = execSync('git status --porcelain', {
|
|
258
|
-
encoding: 'utf-8'
|
|
259
|
-
});
|
|
260
|
-
context.hasUncommittedChanges = status.trim().length > 0;
|
|
261
|
-
|
|
262
|
-
// Get recent commit types
|
|
263
|
-
const commits = execSync('git log --pretty=format:%s -10', {
|
|
264
|
-
encoding: 'utf-8'
|
|
265
|
-
}).split('\n');
|
|
266
|
-
|
|
267
|
-
context.recentCommitTypes = commits
|
|
268
|
-
.map(msg => {
|
|
269
|
-
const match = msg.match(/^(\w+)(\(.*?\))?:/);
|
|
270
|
-
return match ? match[1] : null;
|
|
271
|
-
})
|
|
272
|
-
.filter(Boolean);
|
|
273
|
-
} catch (error) {
|
|
274
|
-
// Git not available or not a git repo
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
return context;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Check for error state
|
|
282
|
-
*/
|
|
283
|
-
async checkErrorState() {
|
|
284
|
-
const errorState = {
|
|
285
|
-
hasErrors: false,
|
|
286
|
-
errorFiles: [],
|
|
287
|
-
testFailures: false
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
try {
|
|
291
|
-
// Check for error logs
|
|
292
|
-
const errorLogPatterns = ['error.log', 'errors.log', '*.error.log'];
|
|
293
|
-
for (const pattern of errorLogPatterns) {
|
|
294
|
-
try {
|
|
295
|
-
const files = await this.findFiles(pattern);
|
|
296
|
-
if (files.length > 0) {
|
|
297
|
-
errorState.hasErrors = true;
|
|
298
|
-
errorState.errorFiles.push(...files);
|
|
299
|
-
}
|
|
300
|
-
} catch (error) {
|
|
301
|
-
// Ignore
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// Check test status
|
|
306
|
-
try {
|
|
307
|
-
execSync('npm test -- --passWithNoTests', {
|
|
308
|
-
encoding: 'utf-8',
|
|
309
|
-
stdio: 'pipe'
|
|
310
|
-
});
|
|
311
|
-
} catch (error) {
|
|
312
|
-
errorState.testFailures = true;
|
|
313
|
-
errorState.hasErrors = true;
|
|
314
|
-
}
|
|
315
|
-
} catch (error) {
|
|
316
|
-
// Ignore errors in error checking
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
return errorState;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Analyze file types in project
|
|
324
|
-
*/
|
|
325
|
-
async analyzeFileTypes() {
|
|
326
|
-
const fileTypes = {
|
|
327
|
-
hasTests: false,
|
|
328
|
-
hasDocumentation: false,
|
|
329
|
-
hasConfiguration: false,
|
|
330
|
-
primaryLanguage: null,
|
|
331
|
-
frameworks: []
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
try {
|
|
335
|
-
// Check for test files
|
|
336
|
-
fileTypes.hasTests = await this.checkFileExists(['*.test.*', '*.spec.*', 'test/**']);
|
|
337
|
-
|
|
338
|
-
// Check for documentation
|
|
339
|
-
fileTypes.hasDocumentation = await this.checkFileExists(['*.md', 'docs/**']);
|
|
340
|
-
|
|
341
|
-
// Check for configuration
|
|
342
|
-
fileTypes.hasConfiguration = await this.checkFileExists(['*.config.*', '*.json', '*.yml']);
|
|
343
|
-
|
|
344
|
-
// Determine primary language
|
|
345
|
-
const languages = {
|
|
346
|
-
javascript: await this.countFiles(['*.js', '*.jsx']),
|
|
347
|
-
typescript: await this.countFiles(['*.ts', '*.tsx']),
|
|
348
|
-
python: await this.countFiles(['*.py']),
|
|
349
|
-
java: await this.countFiles(['*.java'])
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
fileTypes.primaryLanguage = Object.entries(languages)
|
|
353
|
-
.sort(([,a], [,b]) => b - a)[0][0];
|
|
354
|
-
|
|
355
|
-
// Detect frameworks
|
|
356
|
-
if (await this.checkFileExists('package.json')) {
|
|
357
|
-
try {
|
|
358
|
-
const pkg = JSON.parse(
|
|
359
|
-
await fs.readFile(path.join(process.cwd(), 'package.json'), 'utf-8')
|
|
360
|
-
);
|
|
361
|
-
|
|
362
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
363
|
-
|
|
364
|
-
if (deps.react) fileTypes.frameworks.push('react');
|
|
365
|
-
if (deps.vue) fileTypes.frameworks.push('vue');
|
|
366
|
-
if (deps.angular) fileTypes.frameworks.push('angular');
|
|
367
|
-
if (deps.express) fileTypes.frameworks.push('express');
|
|
368
|
-
if (deps.next) fileTypes.frameworks.push('nextjs');
|
|
369
|
-
} catch (error) {
|
|
370
|
-
// Ignore
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
} catch (error) {
|
|
374
|
-
// Ignore errors
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
return fileTypes;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* Calculate score for a specific persona
|
|
382
|
-
*/
|
|
383
|
-
async calculatePersonaScore(personaName, analysis) {
|
|
384
|
-
const config = this.patterns[personaName];
|
|
385
|
-
const compiled = this.compiledPatterns[personaName];
|
|
386
|
-
|
|
387
|
-
let score = 0;
|
|
388
|
-
|
|
389
|
-
// Keyword matching in command/description
|
|
390
|
-
const text = `${analysis.command} ${analysis.description}`.toLowerCase();
|
|
391
|
-
const keywordMatches = (text.match(compiled.keywordRegex) || []).length;
|
|
392
|
-
score += keywordMatches * 10;
|
|
393
|
-
|
|
394
|
-
// Context indicator matching
|
|
395
|
-
if (compiled.contextRegex.test(text)) {
|
|
396
|
-
score += 20;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// File pattern matching
|
|
400
|
-
if (analysis.recentFiles?.length) {
|
|
401
|
-
const matchingFiles = analysis.recentFiles.filter(file =>
|
|
402
|
-
config.filePatterns.some(pattern => this.matchesFilePattern(file, pattern))
|
|
403
|
-
);
|
|
404
|
-
score += (matchingFiles.length / analysis.recentFiles.length) * 30;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Special context bonuses
|
|
408
|
-
score += this.applyContextBonuses(personaName, analysis);
|
|
409
|
-
|
|
410
|
-
return Math.min(score, 100); // Cap at 100
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
/**
|
|
414
|
-
* Apply context-specific bonuses
|
|
415
|
-
*/
|
|
416
|
-
applyContextBonuses(personaName, analysis) {
|
|
417
|
-
let bonus = 0;
|
|
418
|
-
|
|
419
|
-
switch (personaName) {
|
|
420
|
-
case 'debugger':
|
|
421
|
-
if (analysis.errorState?.hasErrors) bonus += 30;
|
|
422
|
-
if (analysis.errorState?.testFailures) bonus += 20;
|
|
423
|
-
if (analysis.gitContext?.recentCommitTypes.includes('fix')) bonus += 10;
|
|
424
|
-
break;
|
|
425
|
-
|
|
426
|
-
case 'reviewer':
|
|
427
|
-
if (analysis.gitContext?.isPullRequest) bonus += 25;
|
|
428
|
-
if (analysis.command?.includes('review')) bonus += 20;
|
|
429
|
-
break;
|
|
430
|
-
|
|
431
|
-
case 'architect':
|
|
432
|
-
if (analysis.command?.includes('design')) bonus += 20;
|
|
433
|
-
if (analysis.fileTypes?.hasConfiguration) bonus += 10;
|
|
434
|
-
if (!analysis.fileTypes?.hasTests) bonus += 15; // Early stage project
|
|
435
|
-
break;
|
|
436
|
-
|
|
437
|
-
case 'documenter':
|
|
438
|
-
if (analysis.fileTypes?.hasDocumentation) bonus += 15;
|
|
439
|
-
if (analysis.command?.includes('document')) bonus += 25;
|
|
440
|
-
break;
|
|
441
|
-
|
|
442
|
-
case 'optimizer':
|
|
443
|
-
if (analysis.command?.includes('slow')) bonus += 20;
|
|
444
|
-
if (analysis.command?.includes('performance')) bonus += 25;
|
|
445
|
-
break;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
return bonus;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* Determine primary task type from scores
|
|
453
|
-
*/
|
|
454
|
-
determinePrimaryType(scores) {
|
|
455
|
-
const sorted = Object.entries(scores)
|
|
456
|
-
.sort(([,a], [,b]) => b - a);
|
|
457
|
-
|
|
458
|
-
// If top score is significantly higher, return it
|
|
459
|
-
if (sorted[0][1] > sorted[1][1] * 1.5) {
|
|
460
|
-
return sorted[0][0];
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
// Otherwise, consider it mixed and return developer
|
|
464
|
-
return 'developer';
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Calculate similarity between two tasks
|
|
469
|
-
*/
|
|
470
|
-
calculateSimilarity(task1, task2) {
|
|
471
|
-
if (!task1 || !task2) return 0;
|
|
472
|
-
|
|
473
|
-
// Extract features from both tasks
|
|
474
|
-
const features1 = this.extractTaskFeatures(task1);
|
|
475
|
-
const features2 = this.extractTaskFeatures(task2);
|
|
476
|
-
|
|
477
|
-
// Calculate Jaccard similarity
|
|
478
|
-
const intersection = features1.filter(f => features2.includes(f));
|
|
479
|
-
const union = [...new Set([...features1, ...features2])];
|
|
480
|
-
|
|
481
|
-
return union.length > 0 ? intersection.length / union.length : 0;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
/**
|
|
485
|
-
* Extract features from a task for similarity comparison
|
|
486
|
-
*/
|
|
487
|
-
extractTaskFeatures(task) {
|
|
488
|
-
const features = [];
|
|
489
|
-
|
|
490
|
-
// Add keywords
|
|
491
|
-
if (task.keywords) {
|
|
492
|
-
features.push(...task.keywords);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
// Add primary type
|
|
496
|
-
if (task.primaryType) {
|
|
497
|
-
features.push(`type:${task.primaryType}`);
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
// Add file types
|
|
501
|
-
if (task.fileTypes) {
|
|
502
|
-
if (task.fileTypes.primaryLanguage) {
|
|
503
|
-
features.push(`lang:${task.fileTypes.primaryLanguage}`);
|
|
504
|
-
}
|
|
505
|
-
if (task.fileTypes.frameworks) {
|
|
506
|
-
task.fileTypes.frameworks.forEach(fw =>
|
|
507
|
-
features.push(`framework:${fw}`)
|
|
508
|
-
);
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
return features;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
/**
|
|
516
|
-
* Check if file exists matching patterns
|
|
517
|
-
*/
|
|
518
|
-
async checkFileExists(patterns) {
|
|
519
|
-
for (const pattern of (Array.isArray(patterns) ? patterns : [patterns])) {
|
|
520
|
-
const files = await this.findFiles(pattern);
|
|
521
|
-
if (files.length > 0) return true;
|
|
522
|
-
}
|
|
523
|
-
return false;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
/**
|
|
527
|
-
* Count files matching patterns
|
|
528
|
-
*/
|
|
529
|
-
async countFiles(patterns) {
|
|
530
|
-
let count = 0;
|
|
531
|
-
for (const pattern of (Array.isArray(patterns) ? patterns : [patterns])) {
|
|
532
|
-
const files = await this.findFiles(pattern);
|
|
533
|
-
count += files.length;
|
|
534
|
-
}
|
|
535
|
-
return count;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
/**
|
|
539
|
-
* Find files matching a pattern
|
|
540
|
-
*/
|
|
541
|
-
async findFiles(pattern, dir = process.cwd(), results = []) {
|
|
542
|
-
try {
|
|
543
|
-
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
544
|
-
|
|
545
|
-
for (const entry of entries) {
|
|
546
|
-
if (entry.name.startsWith('.') || entry.name === 'node_modules') {
|
|
547
|
-
continue;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
const fullPath = path.join(dir, entry.name);
|
|
551
|
-
|
|
552
|
-
if (entry.isDirectory() && pattern.includes('**')) {
|
|
553
|
-
await this.findFiles(pattern, fullPath, results);
|
|
554
|
-
} else if (entry.isFile() && this.matchesFilePattern(entry.name, pattern)) {
|
|
555
|
-
results.push(path.relative(process.cwd(), fullPath));
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
} catch (error) {
|
|
559
|
-
// Ignore errors
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
return results;
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
/**
|
|
566
|
-
* Check if file matches pattern
|
|
567
|
-
*/
|
|
568
|
-
matchesFilePattern(file, pattern) {
|
|
569
|
-
// Simple glob pattern matching
|
|
570
|
-
const regex = pattern
|
|
571
|
-
.replace(/\*\*/g, '.*')
|
|
572
|
-
.replace(/\*/g, '[^/]*')
|
|
573
|
-
.replace(/\?/g, '.')
|
|
574
|
-
.replace(/\./g, '\\.');
|
|
575
|
-
|
|
576
|
-
return new RegExp(regex).test(file);
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
/**
|
|
580
|
-
* Get task analysis for a command
|
|
581
|
-
*/
|
|
582
|
-
async analyzeCommand(command) {
|
|
583
|
-
return await this.analyzeTask({
|
|
584
|
-
command,
|
|
585
|
-
description: '',
|
|
586
|
-
timestamp: new Date()
|
|
587
|
-
});
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
/**
|
|
591
|
-
* Cleanup resources
|
|
592
|
-
*/
|
|
593
|
-
cleanup() {
|
|
594
|
-
this.removeAllListeners();
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
export default TaskAnalyzer;
|
|
599
|
-
export { TaskAnalyzer };
|