aiwf 0.3.21 → 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.
Files changed (140) hide show
  1. package/package.json +3 -2
  2. package/src/cli/index.js +39 -336
  3. package/src/lib/installer.js +310 -90
  4. package/src/utils/messages.js +24 -0
  5. package/src/DEPENDENCY_MAP.md +0 -90
  6. package/src/cli/cache-cli.js +0 -459
  7. package/src/cli/checkpoint-cli.js +0 -417
  8. package/src/cli/language-cli.js +0 -286
  9. package/src/cli/sprint-cli.js +0 -287
  10. package/src/commands/ai-tool.js +0 -383
  11. package/src/commands/compress.js +0 -60
  12. package/src/commands/create-project.js +0 -160
  13. package/src/commands/evaluate.js +0 -459
  14. package/src/commands/persona.js +0 -309
  15. package/src/commands/sprint-task.js +0 -262
  16. package/src/commands/token.js +0 -312
  17. package/src/lib/ai-persona-manager.js +0 -711
  18. package/src/lib/cache-system.js +0 -332
  19. package/src/lib/context-engine.js +0 -568
  20. package/src/lib/github-integration.js +0 -402
  21. package/src/lib/memory-profiler.js +0 -471
  22. package/src/lib/metrics-collector.js +0 -885
  23. package/src/lib/offline-detector.js +0 -386
  24. package/src/lib/resource-loader-enhanced.js +0 -399
  25. package/src/lib/resource-loader.js +0 -250
  26. package/src/lib/resources/commands/ai-persona.js +0 -602
  27. package/src/lib/resources/commands/compress-context.js +0 -389
  28. package/src/lib/resources/commands/evaluate.js +0 -246
  29. package/src/lib/resources/commands/persona-context-apply.js +0 -562
  30. package/src/lib/resources/commands/token-tracking.js +0 -443
  31. package/src/lib/resources/config/commit-patterns.js +0 -103
  32. package/src/lib/resources/config/language.json +0 -6
  33. package/src/lib/resources/personas/PERSONA_INDEX.md +0 -57
  34. package/src/lib/resources/personas/analyst.json +0 -44
  35. package/src/lib/resources/personas/architect/best_practices.md +0 -136
  36. package/src/lib/resources/personas/architect/knowledge_base.md +0 -377
  37. package/src/lib/resources/personas/architect.json +0 -44
  38. package/src/lib/resources/personas/backend/best_practices.md +0 -766
  39. package/src/lib/resources/personas/backend/knowledge_base.md +0 -1070
  40. package/src/lib/resources/personas/data_analyst/best_practices.md +0 -594
  41. package/src/lib/resources/personas/data_analyst/knowledge_base.md +0 -1057
  42. package/src/lib/resources/personas/developer.json +0 -44
  43. package/src/lib/resources/personas/developer.md +0 -37
  44. package/src/lib/resources/personas/evaluation_criteria.json +0 -137
  45. package/src/lib/resources/personas/frontend/best_practices.md +0 -445
  46. package/src/lib/resources/personas/frontend/knowledge_base.md +0 -729
  47. package/src/lib/resources/personas/persona-index.json +0 -31
  48. package/src/lib/resources/personas/reviewer.json +0 -44
  49. package/src/lib/resources/personas/security/best_practices.md +0 -307
  50. package/src/lib/resources/personas/security/knowledge_base.md +0 -498
  51. package/src/lib/resources/personas/tester.json +0 -44
  52. package/src/lib/resources/templates/README.md +0 -78
  53. package/src/lib/resources/templates/api-server/config.json +0 -64
  54. package/src/lib/resources/templates/api-server/template/.aiwf/config.json +0 -56
  55. package/src/lib/resources/templates/api-server/template/.aiwf/feature-ledger.json +0 -42
  56. package/src/lib/resources/templates/api-server/template/.aiwf/personas/backend-engineer.json +0 -40
  57. package/src/lib/resources/templates/api-server/template/.aiwf/scripts/cli.js +0 -111
  58. package/src/lib/resources/templates/api-server/template/.env.example +0 -29
  59. package/src/lib/resources/templates/api-server/template/.eslintrc.json +0 -23
  60. package/src/lib/resources/templates/api-server/template/README.md +0 -171
  61. package/src/lib/resources/templates/api-server/template/jest.config.js +0 -26
  62. package/src/lib/resources/templates/api-server/template/nodemon.json +0 -9
  63. package/src/lib/resources/templates/api-server/template/package.json +0 -61
  64. package/src/lib/resources/templates/api-server/template/src/app.ts +0 -60
  65. package/src/lib/resources/templates/api-server/template/src/config/swagger.ts +0 -38
  66. package/src/lib/resources/templates/api-server/template/src/controllers/aiwfController.ts +0 -54
  67. package/src/lib/resources/templates/api-server/template/src/controllers/authController.ts +0 -128
  68. package/src/lib/resources/templates/api-server/template/src/controllers/statusController.ts +0 -28
  69. package/src/lib/resources/templates/api-server/template/src/index.ts +0 -28
  70. package/src/lib/resources/templates/api-server/template/src/middleware/aiwfMiddleware.ts +0 -50
  71. package/src/lib/resources/templates/api-server/template/src/middleware/authMiddleware.ts +0 -41
  72. package/src/lib/resources/templates/api-server/template/src/middleware/errorHandler.ts +0 -42
  73. package/src/lib/resources/templates/api-server/template/src/middleware/notFoundHandler.ts +0 -14
  74. package/src/lib/resources/templates/api-server/template/src/middleware/requestLogger.ts +0 -20
  75. package/src/lib/resources/templates/api-server/template/src/routes/aiwf.ts +0 -31
  76. package/src/lib/resources/templates/api-server/template/src/routes/index.ts +0 -13
  77. package/src/lib/resources/templates/api-server/template/src/routes/v1/index.ts +0 -67
  78. package/src/lib/resources/templates/api-server/template/src/utils/logger.ts +0 -37
  79. package/src/lib/resources/templates/api-server/template/tests/app.test.ts +0 -40
  80. package/src/lib/resources/templates/api-server/template/tsconfig.json +0 -43
  81. package/src/lib/resources/templates/npm-library/config.json +0 -61
  82. package/src/lib/resources/templates/npm-library/template/LICENSE +0 -21
  83. package/src/lib/resources/templates/npm-library/template/README.md +0 -201
  84. package/src/lib/resources/templates/npm-library/template/package.json +0 -78
  85. package/src/lib/resources/templates/web-app/config.json +0 -53
  86. package/src/lib/resources/templates/web-app/template/.aiwf/config.json +0 -54
  87. package/src/lib/resources/templates/web-app/template/.aiwf/feature-ledger.json +0 -37
  88. package/src/lib/resources/templates/web-app/template/.aiwf/personas/fullstack-developer.json +0 -40
  89. package/src/lib/resources/templates/web-app/template/.aiwf/scripts/cli.js +0 -110
  90. package/src/lib/resources/templates/web-app/template/.eslintrc.cjs +0 -20
  91. package/src/lib/resources/templates/web-app/template/README.md +0 -151
  92. package/src/lib/resources/templates/web-app/template/index.html +0 -14
  93. package/src/lib/resources/templates/web-app/template/package.json +0 -44
  94. package/src/lib/resources/templates/web-app/template/postcss.config.js +0 -6
  95. package/src/lib/resources/templates/web-app/template/public/vite.svg +0 -1
  96. package/src/lib/resources/templates/web-app/template/src/App.tsx +0 -21
  97. package/src/lib/resources/templates/web-app/template/src/components/Layout.tsx +0 -57
  98. package/src/lib/resources/templates/web-app/template/src/components/aiwf/ContextStatus.tsx +0 -107
  99. package/src/lib/resources/templates/web-app/template/src/components/aiwf/TokenUsage.tsx +0 -71
  100. package/src/lib/resources/templates/web-app/template/src/index.css +0 -60
  101. package/src/lib/resources/templates/web-app/template/src/main.tsx +0 -10
  102. package/src/lib/resources/templates/web-app/template/src/pages/AiwfDashboard.tsx +0 -57
  103. package/src/lib/resources/templates/web-app/template/src/pages/HomePage.tsx +0 -89
  104. package/src/lib/resources/templates/web-app/template/src/pages/NotFound.tsx +0 -25
  105. package/src/lib/resources/templates/web-app/template/src/stores/aiwfStore.ts +0 -126
  106. package/src/lib/resources/templates/web-app/template/src/types/global.d.ts +0 -9
  107. package/src/lib/resources/templates/web-app/template/src/vite-env.d.ts +0 -1
  108. package/src/lib/resources/templates/web-app/template/tailwind.config.js +0 -30
  109. package/src/lib/resources/templates/web-app/template/tsconfig.json +0 -37
  110. package/src/lib/resources/templates/web-app/template/tsconfig.node.json +0 -10
  111. package/src/lib/resources/templates/web-app/template/vite.config.ts +0 -23
  112. package/src/lib/resources/utils/background-monitor.js +0 -223
  113. package/src/lib/resources/utils/compression-metrics.js +0 -1031
  114. package/src/lib/resources/utils/compression-strategies.js +0 -557
  115. package/src/lib/resources/utils/content-normalizer.js +0 -455
  116. package/src/lib/resources/utils/context-compressor.js +0 -583
  117. package/src/lib/resources/utils/context-rule-parser.js +0 -180
  118. package/src/lib/resources/utils/context-token-monitor.js +0 -463
  119. package/src/lib/resources/utils/context-update-manager.js +0 -502
  120. package/src/lib/resources/utils/git-utils.js +0 -220
  121. package/src/lib/resources/utils/importance-classifier.js +0 -645
  122. package/src/lib/resources/utils/information-filter.js +0 -710
  123. package/src/lib/resources/utils/persona-aware-compressor.js +0 -598
  124. package/src/lib/resources/utils/prompt-injector.js +0 -242
  125. package/src/lib/resources/utils/simplified-evaluator.js +0 -119
  126. package/src/lib/resources/utils/text-summarizer.js +0 -492
  127. package/src/lib/resources/utils/token-counter.js +0 -177
  128. package/src/lib/resources/utils/token-monitor.js +0 -484
  129. package/src/lib/resources/utils/token-reporter.js +0 -452
  130. package/src/lib/resources/utils/token-storage.js +0 -433
  131. package/src/lib/resources/utils/token-tracker.js +0 -283
  132. package/src/lib/state/priority-calculator.js +0 -217
  133. package/src/lib/state/state-index.js +0 -155
  134. package/src/lib/state/task-scanner.js +0 -328
  135. package/src/lib/task-analyzer.js +0 -599
  136. package/src/lib/template-cache-system.js +0 -559
  137. package/src/lib/template-downloader.js +0 -467
  138. package/src/lib/template-version-manager.js +0 -491
  139. package/src/lib/token-optimizer.js +0 -517
  140. package/src/utils/engineering-guard.js +0 -399
@@ -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 };