claude-flow-novice 1.3.1 โ†’ 1.3.3

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.
@@ -0,0 +1,619 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { LanguageDetector } from './language-detector.js';
4
+ import { ClaudeMdGenerator } from './claude-md-generator.js';
5
+
6
+ /**
7
+ * Integration System for Language Detection and CLAUDE.md Generation
8
+ *
9
+ * Orchestrates the complete workflow from project analysis to CLAUDE.md creation
10
+ * Handles project initialization, updates, and user preferences
11
+ */
12
+ export class IntegrationSystem {
13
+ constructor(projectPath = process.cwd(), options = {}) {
14
+ this.projectPath = projectPath;
15
+ this.options = {
16
+ autoDetect: true,
17
+ autoGenerate: true,
18
+ watchForChanges: false,
19
+ backupExisting: true,
20
+ ...options,
21
+ };
22
+
23
+ this.detector = new LanguageDetector(projectPath);
24
+ this.generator = new ClaudeMdGenerator(projectPath, options);
25
+ this.preferencesPath = path.join(projectPath, '.claude-flow-novice', 'preferences');
26
+ this.configPath = path.join(this.preferencesPath, 'integration.json');
27
+ }
28
+
29
+ /**
30
+ * Main initialization method - sets up the entire system
31
+ */
32
+ async initialize() {
33
+ console.log('๐Ÿš€ Initializing Claude Flow language detection and CLAUDE.md generation...');
34
+
35
+ try {
36
+ // Step 1: Ensure directories exist
37
+ await this.ensureDirectoryStructure();
38
+
39
+ // Step 2: Load existing configuration
40
+ const config = await this.loadConfiguration();
41
+
42
+ // Step 3: Check if auto-detection is enabled
43
+ if (!config.autoDetect) {
44
+ console.log('โญ๏ธ Auto-detection disabled, skipping...');
45
+ return { skipped: true, reason: 'Auto-detection disabled' };
46
+ }
47
+
48
+ // Step 4: Detect project languages and frameworks
49
+ console.log('๐Ÿ” Detecting project languages and frameworks...');
50
+ const detectionResults = await this.detector.detectProject();
51
+
52
+ // Step 5: Check if we need to generate CLAUDE.md
53
+ const shouldGenerate = await this.shouldGenerateClaudeMd(detectionResults, config);
54
+
55
+ if (!shouldGenerate.generate) {
56
+ console.log(`โญ๏ธ ${shouldGenerate.reason}`);
57
+ return { skipped: true, reason: shouldGenerate.reason, detection: detectionResults };
58
+ }
59
+
60
+ // Step 6: Generate CLAUDE.md
61
+ console.log('๐Ÿ“ Generating CLAUDE.md...');
62
+ const claudeContent = await this.generator.generateClaudeMd();
63
+
64
+ // Step 7: Save current detection results for future comparisons
65
+ await this.saveDetectionResults(detectionResults);
66
+
67
+ // Step 8: Update configuration with last generation info
68
+ await this.updateConfiguration({
69
+ ...config,
70
+ lastGenerated: new Date().toISOString(),
71
+ lastDetection: detectionResults,
72
+ });
73
+
74
+ // Step 9: Setup file watching if enabled
75
+ if (config.watchForChanges) {
76
+ await this.setupFileWatcher();
77
+ }
78
+
79
+ console.log('โœ… Integration system initialized successfully');
80
+
81
+ return {
82
+ success: true,
83
+ detection: detectionResults,
84
+ claudeGenerated: true,
85
+ contentLength: claudeContent.length,
86
+ };
87
+ } catch (error) {
88
+ console.error(`โŒ Integration system initialization failed: ${error.message}`);
89
+ throw error;
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Update system when new technologies are detected
95
+ */
96
+ async updateForNewTechnology() {
97
+ console.log('๐Ÿ”„ Checking for new technologies...');
98
+
99
+ try {
100
+ const currentDetection = await this.detector.detectProject();
101
+ const previousDetection = await this.loadPreviousDetection();
102
+
103
+ if (!previousDetection) {
104
+ console.log('โš ๏ธ No previous detection found, running full update...');
105
+ return await this.initialize();
106
+ }
107
+
108
+ const changes = this.compareDetections(previousDetection, currentDetection);
109
+
110
+ if (changes.hasChanges) {
111
+ console.log(`๐Ÿ“ˆ Detected changes: ${changes.summary}`);
112
+
113
+ // Update CLAUDE.md with new technologies
114
+ for (const newTech of changes.newTechnologies) {
115
+ await this.generator.updateForNewTechnology(newTech.name, newTech.type);
116
+ }
117
+
118
+ // Save updated detection results
119
+ await this.saveDetectionResults(currentDetection);
120
+
121
+ return {
122
+ success: true,
123
+ changes: changes,
124
+ updated: true,
125
+ };
126
+ } else {
127
+ console.log('โœจ No new technologies detected');
128
+ return {
129
+ success: true,
130
+ changes: changes,
131
+ updated: false,
132
+ };
133
+ }
134
+ } catch (error) {
135
+ console.error(`โŒ Update failed: ${error.message}`);
136
+ throw error;
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Handle project type change (e.g., adding React to existing Node.js project)
142
+ */
143
+ async handleProjectTypeChange(newProjectType) {
144
+ console.log(`๐Ÿ”„ Handling project type change to: ${newProjectType}`);
145
+
146
+ try {
147
+ const config = await this.loadConfiguration();
148
+
149
+ // Update configuration
150
+ config.overrideProjectType = newProjectType;
151
+ config.lastManualUpdate = new Date().toISOString();
152
+ await this.updateConfiguration(config);
153
+
154
+ // Re-generate CLAUDE.md with new project type
155
+ const claudeContent = await this.generator.generateClaudeMd();
156
+
157
+ return {
158
+ success: true,
159
+ newProjectType,
160
+ claudeUpdated: true,
161
+ contentLength: claudeContent.length,
162
+ };
163
+ } catch (error) {
164
+ console.error(`โŒ Project type change failed: ${error.message}`);
165
+ throw error;
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Interactive setup for user preferences
171
+ */
172
+ async interactiveSetup() {
173
+ console.log('๐ŸŽฏ Starting interactive setup...');
174
+
175
+ const prompts = [
176
+ {
177
+ name: 'autoDetect',
178
+ message: 'Enable automatic language detection?',
179
+ type: 'confirm',
180
+ default: true,
181
+ },
182
+ {
183
+ name: 'autoGenerate',
184
+ message: 'Auto-generate CLAUDE.md when languages are detected?',
185
+ type: 'confirm',
186
+ default: true,
187
+ },
188
+ {
189
+ name: 'backupExisting',
190
+ message: 'Create backups of existing CLAUDE.md files?',
191
+ type: 'confirm',
192
+ default: true,
193
+ },
194
+ {
195
+ name: 'watchForChanges',
196
+ message: 'Watch for file changes and update automatically?',
197
+ type: 'confirm',
198
+ default: false,
199
+ },
200
+ {
201
+ name: 'includeFrameworkSpecific',
202
+ message: 'Include framework-specific best practices?',
203
+ type: 'confirm',
204
+ default: true,
205
+ },
206
+ {
207
+ name: 'includeBestPractices',
208
+ message: 'Include general best practices sections?',
209
+ type: 'confirm',
210
+ default: true,
211
+ },
212
+ {
213
+ name: 'includeTestingPatterns',
214
+ message: 'Include testing patterns and examples?',
215
+ type: 'confirm',
216
+ default: true,
217
+ },
218
+ ];
219
+
220
+ // For now, use defaults (in a real implementation, you'd use inquirer or similar)
221
+ const preferences = {
222
+ autoDetect: true,
223
+ autoGenerate: true,
224
+ backupExisting: true,
225
+ watchForChanges: false,
226
+ includeFrameworkSpecific: true,
227
+ includeBestPractices: true,
228
+ includeTestingPatterns: true,
229
+ setupDate: new Date().toISOString(),
230
+ };
231
+
232
+ await this.updateConfiguration(preferences);
233
+
234
+ console.log('โœ… Interactive setup completed');
235
+ console.log('๐Ÿ“‹ Preferences saved to:', this.configPath);
236
+
237
+ return preferences;
238
+ }
239
+
240
+ /**
241
+ * Validate project configuration and detect issues
242
+ */
243
+ async validateProject() {
244
+ console.log('๐Ÿ”Ž Validating project configuration...');
245
+
246
+ const issues = [];
247
+ const suggestions = [];
248
+
249
+ try {
250
+ // Check for package.json or similar
251
+ const packageFiles = ['package.json', 'requirements.txt', 'pom.xml', 'Cargo.toml', 'go.mod'];
252
+ let hasPackageFile = false;
253
+
254
+ for (const file of packageFiles) {
255
+ try {
256
+ await fs.access(path.join(this.projectPath, file));
257
+ hasPackageFile = true;
258
+ break;
259
+ } catch {}
260
+ }
261
+
262
+ if (!hasPackageFile) {
263
+ issues.push({
264
+ type: 'warning',
265
+ message: 'No package management file found',
266
+ suggestion: 'Consider initializing with npm init, pip, or appropriate package manager',
267
+ });
268
+ }
269
+
270
+ // Check for Git repository
271
+ try {
272
+ await fs.access(path.join(this.projectPath, '.git'));
273
+ } catch {
274
+ suggestions.push({
275
+ type: 'info',
276
+ message: 'No Git repository detected',
277
+ suggestion: 'Consider initializing with: git init',
278
+ });
279
+ }
280
+
281
+ // Check for existing CLAUDE.md
282
+ try {
283
+ await fs.access(path.join(this.projectPath, 'CLAUDE.md'));
284
+ } catch {
285
+ suggestions.push({
286
+ type: 'info',
287
+ message: 'No CLAUDE.md found',
288
+ suggestion: 'Will be generated automatically if auto-generation is enabled',
289
+ });
290
+ }
291
+
292
+ // Check directory structure
293
+ const commonDirs = ['src', 'lib', 'app', 'tests', 'test'];
294
+ let hasSourceDir = false;
295
+
296
+ for (const dir of commonDirs) {
297
+ try {
298
+ const stat = await fs.stat(path.join(this.projectPath, dir));
299
+ if (stat.isDirectory()) {
300
+ hasSourceDir = true;
301
+ break;
302
+ }
303
+ } catch {}
304
+ }
305
+
306
+ if (!hasSourceDir) {
307
+ suggestions.push({
308
+ type: 'info',
309
+ message: 'No standard source directory found',
310
+ suggestion: 'Consider organizing code in src/ or app/ directory',
311
+ });
312
+ }
313
+
314
+ const validation = {
315
+ valid: issues.length === 0,
316
+ issues,
317
+ suggestions,
318
+ checkedAt: new Date().toISOString(),
319
+ };
320
+
321
+ console.log(
322
+ `โœ… Validation complete: ${issues.length} issues, ${suggestions.length} suggestions`,
323
+ );
324
+ return validation;
325
+ } catch (error) {
326
+ console.error(`โŒ Validation failed: ${error.message}`);
327
+ throw error;
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Generate project report with recommendations
333
+ */
334
+ async generateProjectReport() {
335
+ console.log('๐Ÿ“Š Generating project report...');
336
+
337
+ try {
338
+ const [detection, validation, config] = await Promise.all([
339
+ this.detector.detectProject(),
340
+ this.validateProject(),
341
+ this.loadConfiguration(),
342
+ ]);
343
+
344
+ const report = {
345
+ project: {
346
+ path: this.projectPath,
347
+ name: path.basename(this.projectPath),
348
+ analyzedAt: new Date().toISOString(),
349
+ },
350
+ detection: {
351
+ ...detection,
352
+ recommendations: detection.getRecommendations ? detection.getRecommendations() : {},
353
+ },
354
+ validation,
355
+ configuration: config,
356
+ suggestions: this.generateSuggestions(detection, validation),
357
+ nextSteps: this.generateNextSteps(detection, validation, config),
358
+ };
359
+
360
+ // Save report
361
+ const reportPath = path.join(this.preferencesPath, 'project-report.json');
362
+ await fs.writeFile(reportPath, JSON.stringify(report, null, 2));
363
+
364
+ console.log('โœ… Project report generated');
365
+ console.log('๐Ÿ“„ Report saved to:', reportPath);
366
+
367
+ return report;
368
+ } catch (error) {
369
+ console.error(`โŒ Report generation failed: ${error.message}`);
370
+ throw error;
371
+ }
372
+ }
373
+
374
+ // Private methods
375
+
376
+ async ensureDirectoryStructure() {
377
+ const dirs = [
378
+ this.preferencesPath,
379
+ path.join(this.preferencesPath, 'language-configs'),
380
+ path.join(this.preferencesPath, 'backups'),
381
+ path.join(this.preferencesPath, 'reports'),
382
+ ];
383
+
384
+ for (const dir of dirs) {
385
+ await fs.mkdir(dir, { recursive: true });
386
+ }
387
+ }
388
+
389
+ async loadConfiguration() {
390
+ try {
391
+ const content = await fs.readFile(this.configPath, 'utf8');
392
+ return JSON.parse(content);
393
+ } catch (error) {
394
+ // Return default configuration
395
+ return {
396
+ autoDetect: true,
397
+ autoGenerate: true,
398
+ backupExisting: true,
399
+ watchForChanges: false,
400
+ includeFrameworkSpecific: true,
401
+ includeBestPractices: true,
402
+ includeTestingPatterns: true,
403
+ createdAt: new Date().toISOString(),
404
+ };
405
+ }
406
+ }
407
+
408
+ async updateConfiguration(config) {
409
+ await fs.writeFile(this.configPath, JSON.stringify(config, null, 2));
410
+ }
411
+
412
+ async shouldGenerateClaudeMd(detectionResults, config) {
413
+ if (!config.autoGenerate) {
414
+ return { generate: false, reason: 'Auto-generation disabled in config' };
415
+ }
416
+
417
+ if (detectionResults.confidence < 0.3) {
418
+ return { generate: false, reason: 'Detection confidence too low' };
419
+ }
420
+
421
+ if (Object.keys(detectionResults.languages).length === 0) {
422
+ return { generate: false, reason: 'No languages detected' };
423
+ }
424
+
425
+ // Check if CLAUDE.md exists and when it was last modified
426
+ try {
427
+ const claudeMdPath = path.join(this.projectPath, 'CLAUDE.md');
428
+ const stat = await fs.stat(claudeMdPath);
429
+ const lastModified = stat.mtime;
430
+ const daysSinceModified = (Date.now() - lastModified.getTime()) / (1000 * 60 * 60 * 24);
431
+
432
+ if (daysSinceModified < 1 && !config.forceRegenerate) {
433
+ return { generate: false, reason: 'CLAUDE.md was recently modified' };
434
+ }
435
+ } catch {
436
+ // File doesn't exist, we should generate it
437
+ }
438
+
439
+ return { generate: true, reason: 'Conditions met for generation' };
440
+ }
441
+
442
+ async saveDetectionResults(results) {
443
+ const resultsPath = path.join(this.preferencesPath, 'last-detection.json');
444
+ await fs.writeFile(resultsPath, JSON.stringify(results, null, 2));
445
+ }
446
+
447
+ async loadPreviousDetection() {
448
+ try {
449
+ const resultsPath = path.join(this.preferencesPath, 'last-detection.json');
450
+ const content = await fs.readFile(resultsPath, 'utf8');
451
+ return JSON.parse(content);
452
+ } catch {
453
+ return null;
454
+ }
455
+ }
456
+
457
+ compareDetections(previous, current) {
458
+ const changes = {
459
+ hasChanges: false,
460
+ newLanguages: [],
461
+ newFrameworks: [],
462
+ newTechnologies: [],
463
+ removedLanguages: [],
464
+ removedFrameworks: [],
465
+ confidenceChanges: {},
466
+ summary: '',
467
+ };
468
+
469
+ // Compare languages
470
+ for (const [lang, confidence] of Object.entries(current.languages)) {
471
+ if (!previous.languages[lang]) {
472
+ changes.newLanguages.push({ name: lang, confidence });
473
+ changes.newTechnologies.push({ name: lang, type: 'language' });
474
+ changes.hasChanges = true;
475
+ }
476
+ }
477
+
478
+ // Compare frameworks
479
+ for (const [framework, confidence] of Object.entries(current.frameworks)) {
480
+ if (!previous.frameworks[framework]) {
481
+ changes.newFrameworks.push({ name: framework, confidence });
482
+ changes.newTechnologies.push({ name: framework, type: 'framework' });
483
+ changes.hasChanges = true;
484
+ }
485
+ }
486
+
487
+ // Check for removed technologies
488
+ for (const lang of Object.keys(previous.languages)) {
489
+ if (!current.languages[lang]) {
490
+ changes.removedLanguages.push(lang);
491
+ changes.hasChanges = true;
492
+ }
493
+ }
494
+
495
+ for (const framework of Object.keys(previous.frameworks)) {
496
+ if (!current.frameworks[framework]) {
497
+ changes.removedFrameworks.push(framework);
498
+ changes.hasChanges = true;
499
+ }
500
+ }
501
+
502
+ // Generate summary
503
+ const summaryParts = [];
504
+ if (changes.newLanguages.length)
505
+ summaryParts.push(`${changes.newLanguages.length} new languages`);
506
+ if (changes.newFrameworks.length)
507
+ summaryParts.push(`${changes.newFrameworks.length} new frameworks`);
508
+ if (changes.removedLanguages.length)
509
+ summaryParts.push(`${changes.removedLanguages.length} removed languages`);
510
+ if (changes.removedFrameworks.length)
511
+ summaryParts.push(`${changes.removedFrameworks.length} removed frameworks`);
512
+
513
+ changes.summary = summaryParts.join(', ') || 'No significant changes';
514
+
515
+ return changes;
516
+ }
517
+
518
+ generateSuggestions(detection, validation) {
519
+ const suggestions = [];
520
+
521
+ // Language-specific suggestions
522
+ if (detection.languages.javascript && !detection.languages.typescript) {
523
+ suggestions.push({
524
+ type: 'enhancement',
525
+ message: 'Consider migrating to TypeScript for better type safety',
526
+ priority: 'medium',
527
+ });
528
+ }
529
+
530
+ if (detection.frameworks.react && !detection.frameworks.nextjs) {
531
+ suggestions.push({
532
+ type: 'enhancement',
533
+ message: 'Consider Next.js for better SEO and performance',
534
+ priority: 'low',
535
+ });
536
+ }
537
+
538
+ // Testing suggestions
539
+ const hasTestFramework = Object.keys(detection.dependencies).some((dep) =>
540
+ ['jest', 'pytest', 'mocha', 'jasmine', 'vitest'].includes(dep.toLowerCase()),
541
+ );
542
+
543
+ if (!hasTestFramework) {
544
+ suggestions.push({
545
+ type: 'quality',
546
+ message: 'No testing framework detected - consider adding automated tests',
547
+ priority: 'high',
548
+ });
549
+ }
550
+
551
+ return suggestions;
552
+ }
553
+
554
+ generateNextSteps(detection, validation, config) {
555
+ const steps = [];
556
+
557
+ if (!config.setupCompleted) {
558
+ steps.push('Run interactive setup: npm run claude-flow:setup');
559
+ }
560
+
561
+ if (validation.issues.length > 0) {
562
+ steps.push('Address validation issues found in project');
563
+ }
564
+
565
+ if (detection.confidence < 0.7) {
566
+ steps.push('Review and verify detected languages/frameworks');
567
+ }
568
+
569
+ steps.push('Review generated CLAUDE.md file');
570
+ steps.push('Commit changes to version control');
571
+
572
+ return steps;
573
+ }
574
+
575
+ async setupFileWatcher() {
576
+ // In a real implementation, this would set up file system watching
577
+ // using something like chokidar to watch for changes to package.json,
578
+ // new files, etc., and trigger re-detection
579
+ console.log('๐Ÿ“‚ File watching setup completed (placeholder)');
580
+ }
581
+
582
+ /**
583
+ * Clean up old backups and reports
584
+ */
585
+ async cleanup(olderThanDays = 30) {
586
+ console.log(`๐Ÿงน Cleaning up files older than ${olderThanDays} days...`);
587
+
588
+ const backupsDir = path.join(this.preferencesPath, 'backups');
589
+ const reportsDir = path.join(this.preferencesPath, 'reports');
590
+
591
+ const cutoffDate = new Date();
592
+ cutoffDate.setDate(cutoffDate.getDate() - olderThanDays);
593
+
594
+ let cleanedCount = 0;
595
+
596
+ for (const dir of [backupsDir, reportsDir]) {
597
+ try {
598
+ const files = await fs.readdir(dir);
599
+ for (const file of files) {
600
+ const filePath = path.join(dir, file);
601
+ const stat = await fs.stat(filePath);
602
+
603
+ if (stat.mtime < cutoffDate) {
604
+ await fs.unlink(filePath);
605
+ cleanedCount++;
606
+ }
607
+ }
608
+ } catch (error) {
609
+ // Directory might not exist, continue
610
+ continue;
611
+ }
612
+ }
613
+
614
+ console.log(`โœ… Cleanup complete: ${cleanedCount} files removed`);
615
+ return { cleanedCount, cutoffDate };
616
+ }
617
+ }
618
+
619
+ export default IntegrationSystem;