create-universal-ai-context 2.0.0 → 2.1.2
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/README.md +55 -23
- package/bin/create-ai-context.js +159 -1
- package/lib/adapters/claude.js +180 -29
- package/lib/doc-discovery.js +741 -0
- package/lib/drift-checker.js +920 -0
- package/lib/index.js +89 -7
- package/lib/installer.js +1 -0
- package/lib/placeholder.js +11 -1
- package/lib/prompts.js +55 -1
- package/lib/smart-merge.js +540 -0
- package/lib/spinner.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,36 +29,68 @@ That's it. The CLI automatically:
|
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
31
|
# Basic usage
|
|
32
|
-
npx create-ai-context # Auto-detect and generate for all tools
|
|
32
|
+
npx create-universal-ai-context # Auto-detect and generate for all tools
|
|
33
33
|
|
|
34
34
|
# Select specific AI tools
|
|
35
|
-
npx create-ai-context --ai claude # Claude Code only
|
|
36
|
-
npx create-ai-context --ai copilot # GitHub Copilot only
|
|
37
|
-
npx create-ai-context --ai cline # Cline only
|
|
38
|
-
npx create-ai-context --ai antigravity # Antigravity only
|
|
39
|
-
npx create-ai-context --ai all # All tools (default)
|
|
35
|
+
npx create-universal-ai-context --ai claude # Claude Code only
|
|
36
|
+
npx create-universal-ai-context --ai copilot # GitHub Copilot only
|
|
37
|
+
npx create-universal-ai-context --ai cline # Cline only
|
|
38
|
+
npx create-universal-ai-context --ai antigravity # Antigravity only
|
|
39
|
+
npx create-universal-ai-context --ai all # All tools (default)
|
|
40
40
|
|
|
41
41
|
# Analysis modes
|
|
42
|
-
npx create-ai-context --static # Force static analysis only
|
|
43
|
-
npx create-ai-context --force-ai # Require Claude Code session
|
|
42
|
+
npx create-universal-ai-context --static # Force static analysis only
|
|
43
|
+
npx create-universal-ai-context --force-ai # Require Claude Code session
|
|
44
44
|
|
|
45
45
|
# Other options
|
|
46
|
-
npx create-ai-context --yes # Accept all defaults
|
|
47
|
-
npx create-ai-context --dry-run # Preview without changes
|
|
48
|
-
npx create-ai-context my-project # Create in new directory
|
|
46
|
+
npx create-universal-ai-context --yes # Accept all defaults
|
|
47
|
+
npx create-universal-ai-context --dry-run # Preview without changes
|
|
48
|
+
npx create-universal-ai-context my-project # Create in new directory
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
## Subcommands
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
54
|
# Generate context for specific tools
|
|
55
|
-
npx create-ai-context generate --ai copilot
|
|
55
|
+
npx create-universal-ai-context generate --ai copilot
|
|
56
56
|
|
|
57
57
|
# Check installation status
|
|
58
|
-
npx create-ai-context status
|
|
58
|
+
npx create-universal-ai-context status
|
|
59
59
|
|
|
60
60
|
# Migrate from v1.x
|
|
61
|
-
npx create-ai-context migrate
|
|
61
|
+
npx create-universal-ai-context migrate
|
|
62
|
+
|
|
63
|
+
# Check documentation drift
|
|
64
|
+
npx create-universal-ai-context drift --all # Check all docs
|
|
65
|
+
npx create-universal-ai-context drift --file README.md # Check specific file
|
|
66
|
+
npx create-universal-ai-context drift --fix # Auto-fix issues
|
|
67
|
+
npx create-universal-ai-context drift --strict # Exit 1 on issues (CI)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Existing Documentation Detection
|
|
71
|
+
|
|
72
|
+
The CLI automatically detects existing AI context files:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx create-universal-ai-context
|
|
76
|
+
|
|
77
|
+
# Found existing documentation: Claude context (v1), README.md
|
|
78
|
+
# ? How would you like to proceed?
|
|
79
|
+
# > Merge: Use existing docs as base, add new structure (recommended)
|
|
80
|
+
# Fresh: Start fresh but import key values
|
|
81
|
+
# Overwrite: Replace everything with new templates
|
|
82
|
+
# Skip: Cancel initialization
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Merge Mode Options
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npx create-universal-ai-context --mode merge # Preserve customizations (default)
|
|
89
|
+
npx create-universal-ai-context --mode fresh # New structure, keep values
|
|
90
|
+
npx create-universal-ai-context --mode overwrite # Replace everything
|
|
91
|
+
npx create-universal-ai-context --preserve-custom # Keep user customizations
|
|
92
|
+
npx create-universal-ai-context --update-refs # Auto-fix line numbers
|
|
93
|
+
npx create-universal-ai-context --backup # Create backup first
|
|
62
94
|
```
|
|
63
95
|
|
|
64
96
|
## What Gets Analyzed
|
|
@@ -104,14 +136,14 @@ your-project/
|
|
|
104
136
|
## Tech Stack Presets
|
|
105
137
|
|
|
106
138
|
```bash
|
|
107
|
-
npx create-ai-context -t python-fastapi
|
|
108
|
-
npx create-ai-context -t python-django
|
|
109
|
-
npx create-ai-context -t node-express
|
|
110
|
-
npx create-ai-context -t node-nestjs
|
|
111
|
-
npx create-ai-context -t typescript-nextjs
|
|
112
|
-
npx create-ai-context -t go-gin
|
|
113
|
-
npx create-ai-context -t rust-actix
|
|
114
|
-
npx create-ai-context -t ruby-rails
|
|
139
|
+
npx create-universal-ai-context -t python-fastapi
|
|
140
|
+
npx create-universal-ai-context -t python-django
|
|
141
|
+
npx create-universal-ai-context -t node-express
|
|
142
|
+
npx create-universal-ai-context -t node-nestjs
|
|
143
|
+
npx create-universal-ai-context -t typescript-nextjs
|
|
144
|
+
npx create-universal-ai-context -t go-gin
|
|
145
|
+
npx create-universal-ai-context -t rust-actix
|
|
146
|
+
npx create-universal-ai-context -t ruby-rails
|
|
115
147
|
```
|
|
116
148
|
|
|
117
149
|
## Features
|
|
@@ -134,7 +166,7 @@ npx create-ai-context -t ruby-rails
|
|
|
134
166
|
If you have an existing `.claude/` directory:
|
|
135
167
|
|
|
136
168
|
```bash
|
|
137
|
-
npx create-ai-context migrate
|
|
169
|
+
npx create-universal-ai-context migrate
|
|
138
170
|
```
|
|
139
171
|
|
|
140
172
|
This will:
|
package/bin/create-ai-context.js
CHANGED
|
@@ -25,6 +25,12 @@ const { migrateV1ToV2, getMigrationStatus } = require('../lib/migrate');
|
|
|
25
25
|
const { detectTechStack } = require('../lib/detector');
|
|
26
26
|
const { analyzeCodebase } = require('../lib/static-analyzer');
|
|
27
27
|
const { createSpinner } = require('../lib/spinner');
|
|
28
|
+
const {
|
|
29
|
+
findDocumentationFiles,
|
|
30
|
+
generateDriftReport,
|
|
31
|
+
checkDocumentDrift,
|
|
32
|
+
formatDriftReportConsole
|
|
33
|
+
} = require('../lib/drift-checker');
|
|
28
34
|
const packageJson = require('../package.json');
|
|
29
35
|
|
|
30
36
|
// ASCII Banner
|
|
@@ -70,6 +76,10 @@ program
|
|
|
70
76
|
.option('--analyze-only', 'Run codebase analysis without installation')
|
|
71
77
|
.option('--monorepo', 'Initialize in monorepo mode with federation support')
|
|
72
78
|
.option('--federate', 'Run federation to generate context for subprojects')
|
|
79
|
+
.option('--mode <mode>', 'How to handle existing docs: merge, overwrite, interactive', 'merge')
|
|
80
|
+
.option('--preserve-custom', 'Keep user customizations when merging (default: true)', true)
|
|
81
|
+
.option('--update-refs', 'Auto-fix drifted line references')
|
|
82
|
+
.option('--backup', 'Create backup before modifying existing files')
|
|
73
83
|
.action(async (projectName, options) => {
|
|
74
84
|
console.log(banner);
|
|
75
85
|
|
|
@@ -94,7 +104,12 @@ program
|
|
|
94
104
|
forceStatic: options.static,
|
|
95
105
|
analyzeOnly: options.analyzeOnly,
|
|
96
106
|
monorepo: options.monorepo,
|
|
97
|
-
federate: options.federate
|
|
107
|
+
federate: options.federate,
|
|
108
|
+
// Merge options
|
|
109
|
+
mode: options.mode,
|
|
110
|
+
preserveCustom: options.preserveCustom,
|
|
111
|
+
updateRefs: options.updateRefs,
|
|
112
|
+
backup: options.backup
|
|
98
113
|
});
|
|
99
114
|
} catch (error) {
|
|
100
115
|
console.error(chalk.red('\n✖ Error:'), error.message);
|
|
@@ -334,4 +349,147 @@ program
|
|
|
334
349
|
}
|
|
335
350
|
});
|
|
336
351
|
|
|
352
|
+
// Drift subcommand - check documentation drift
|
|
353
|
+
program
|
|
354
|
+
.command('drift')
|
|
355
|
+
.description('Check documentation drift against codebase')
|
|
356
|
+
.option('-f, --file <path>', 'Check specific documentation file')
|
|
357
|
+
.option('-a, --all', 'Check all documentation files')
|
|
358
|
+
.option('--fix', 'Show suggested fixes for issues')
|
|
359
|
+
.option('--strict', 'Exit with error if drift detected')
|
|
360
|
+
.option('-o, --output <format>', 'Output format: console, json, markdown', 'console')
|
|
361
|
+
.option('-t, --threshold <percent>', 'Health score threshold for --strict', '70')
|
|
362
|
+
.option('-p, --path <dir>', 'Project directory (defaults to current)', '.')
|
|
363
|
+
.option('-v, --verbose', 'Show detailed output')
|
|
364
|
+
.action(async (options) => {
|
|
365
|
+
console.log(banner);
|
|
366
|
+
|
|
367
|
+
const projectRoot = path.resolve(options.path);
|
|
368
|
+
const spinner = createSpinner();
|
|
369
|
+
|
|
370
|
+
try {
|
|
371
|
+
// Determine which files to check
|
|
372
|
+
let filesToCheck = [];
|
|
373
|
+
|
|
374
|
+
if (options.file) {
|
|
375
|
+
// Single file mode
|
|
376
|
+
filesToCheck = [options.file];
|
|
377
|
+
} else if (options.all) {
|
|
378
|
+
// All documentation files
|
|
379
|
+
spinner.start('Finding documentation files...');
|
|
380
|
+
filesToCheck = await findDocumentationFiles(projectRoot);
|
|
381
|
+
spinner.succeed(`Found ${filesToCheck.length} documentation files`);
|
|
382
|
+
} else {
|
|
383
|
+
// Default: check main context files
|
|
384
|
+
const defaultFiles = ['CLAUDE.md', 'AI_CONTEXT.md', 'README.md'];
|
|
385
|
+
filesToCheck = defaultFiles.filter(f =>
|
|
386
|
+
fs.existsSync(path.join(projectRoot, f))
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
if (filesToCheck.length === 0) {
|
|
390
|
+
console.log(chalk.yellow('\nNo documentation files found.'));
|
|
391
|
+
console.log(chalk.gray('Use --all to scan for all markdown files, or --file to check a specific file.'));
|
|
392
|
+
process.exit(0);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Generate drift report
|
|
397
|
+
spinner.start('Checking documentation drift...');
|
|
398
|
+
const report = generateDriftReport(filesToCheck, projectRoot);
|
|
399
|
+
spinner.succeed(`Checked ${report.summary.totalDocuments} documents`);
|
|
400
|
+
|
|
401
|
+
// Output results
|
|
402
|
+
if (options.output === 'json') {
|
|
403
|
+
console.log(JSON.stringify(report, null, 2));
|
|
404
|
+
} else if (options.output === 'markdown') {
|
|
405
|
+
console.log(formatDriftReportMarkdown(report));
|
|
406
|
+
} else {
|
|
407
|
+
// Console output
|
|
408
|
+
console.log(formatDriftReportConsole(report));
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Show suggested fixes if requested
|
|
412
|
+
if (options.fix && report.suggestedFixes.length > 0) {
|
|
413
|
+
console.log(chalk.bold('\nSuggested Fixes:'));
|
|
414
|
+
for (const fix of report.suggestedFixes) {
|
|
415
|
+
console.log(chalk.cyan(`\n ${fix.document}:`));
|
|
416
|
+
console.log(chalk.red(` - ${fix.original}`));
|
|
417
|
+
console.log(chalk.green(` + ${fix.suggestion}`));
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Strict mode - exit with error if below threshold
|
|
422
|
+
if (options.strict) {
|
|
423
|
+
const threshold = parseInt(options.threshold, 10);
|
|
424
|
+
if (report.summary.overallHealthScore < threshold) {
|
|
425
|
+
console.log(chalk.red(`\n✖ Health score ${report.summary.overallHealthScore}% is below threshold ${threshold}%`));
|
|
426
|
+
process.exit(1);
|
|
427
|
+
} else {
|
|
428
|
+
console.log(chalk.green(`\n✓ Health score ${report.summary.overallHealthScore}% meets threshold ${threshold}%`));
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
} catch (error) {
|
|
433
|
+
spinner.fail('Drift check failed');
|
|
434
|
+
console.error(chalk.red('\n✖ Error:'), error.message);
|
|
435
|
+
if (options.verbose) {
|
|
436
|
+
console.error(chalk.gray(error.stack));
|
|
437
|
+
}
|
|
438
|
+
process.exit(1);
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Format drift report as markdown
|
|
444
|
+
*/
|
|
445
|
+
function formatDriftReportMarkdown(report) {
|
|
446
|
+
const lines = [
|
|
447
|
+
'# Documentation Drift Report',
|
|
448
|
+
'',
|
|
449
|
+
`**Generated:** ${report.generatedAt}`,
|
|
450
|
+
`**Overall Health:** ${report.summary.overallHealthScore}%`,
|
|
451
|
+
'',
|
|
452
|
+
'## Summary',
|
|
453
|
+
'',
|
|
454
|
+
'| Metric | Value |',
|
|
455
|
+
'|--------|-------|',
|
|
456
|
+
`| Documents Analyzed | ${report.summary.totalDocuments} |`,
|
|
457
|
+
`| Healthy | ${report.summary.healthyDocuments} |`,
|
|
458
|
+
`| With Issues | ${report.summary.documentsWithIssues} |`,
|
|
459
|
+
`| References Valid | ${report.summary.validReferences}/${report.summary.totalReferences} |`,
|
|
460
|
+
''
|
|
461
|
+
];
|
|
462
|
+
|
|
463
|
+
if (report.documents.length > 0) {
|
|
464
|
+
lines.push('## Documents', '');
|
|
465
|
+
for (const doc of report.documents) {
|
|
466
|
+
const emoji = doc.status === 'healthy' ? '✓' :
|
|
467
|
+
doc.status === 'needs_update' ? '⚠' : '✗';
|
|
468
|
+
lines.push(`### ${doc.document} (${doc.healthScore}% ${emoji})`);
|
|
469
|
+
lines.push('');
|
|
470
|
+
|
|
471
|
+
if (doc.references.invalid.length > 0) {
|
|
472
|
+
lines.push('**Issues:**', '');
|
|
473
|
+
for (const issue of doc.references.invalid) {
|
|
474
|
+
lines.push(`- \`${issue.original}\` - ${issue.issue}`);
|
|
475
|
+
if (issue.suggestion) {
|
|
476
|
+
lines.push(` - Suggestion: ${issue.suggestion}`);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
lines.push('');
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (report.suggestedFixes.length > 0) {
|
|
485
|
+
lines.push('## Suggested Fixes', '');
|
|
486
|
+
for (const fix of report.suggestedFixes) {
|
|
487
|
+
lines.push(`- **${fix.document}**: \`${fix.original}\``);
|
|
488
|
+
lines.push(` - → ${fix.suggestion}`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return lines.join('\n');
|
|
493
|
+
}
|
|
494
|
+
|
|
337
495
|
program.parse();
|
package/lib/adapters/claude.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Claude Adapter
|
|
3
3
|
*
|
|
4
|
-
* Generates AI_CONTEXT.md and .
|
|
4
|
+
* Generates AI_CONTEXT.md and .claude/ directory structure.
|
|
5
5
|
* This is the primary/universal format.
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -16,8 +16,8 @@ const adapter = {
|
|
|
16
16
|
name: 'claude',
|
|
17
17
|
displayName: 'Claude Code',
|
|
18
18
|
description: 'Universal AI context format for Claude Code and other AI assistants',
|
|
19
|
-
outputType: '
|
|
20
|
-
outputPath: '
|
|
19
|
+
outputType: 'multi-file',
|
|
20
|
+
outputPath: '.claude/'
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
/**
|
|
@@ -35,11 +35,13 @@ function getOutputPath(projectRoot) {
|
|
|
35
35
|
* @returns {boolean}
|
|
36
36
|
*/
|
|
37
37
|
function exists(projectRoot) {
|
|
38
|
-
|
|
38
|
+
const aiContextPath = getOutputPath(projectRoot);
|
|
39
|
+
const claudeDir = path.join(projectRoot, '.claude');
|
|
40
|
+
return fs.existsSync(aiContextPath) || fs.existsSync(claudeDir);
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
/**
|
|
42
|
-
* Generate Claude context file
|
|
44
|
+
* Generate Claude context file and .claude/ directory structure
|
|
43
45
|
* @param {object} analysis - Analysis results from static analyzer
|
|
44
46
|
* @param {object} config - Configuration from CLI
|
|
45
47
|
* @param {string} projectRoot - Project root directory
|
|
@@ -54,22 +56,24 @@ async function generate(analysis, config, projectRoot) {
|
|
|
54
56
|
};
|
|
55
57
|
|
|
56
58
|
try {
|
|
57
|
-
//
|
|
59
|
+
// 1. Generate AI_CONTEXT.md at project root (existing behavior)
|
|
58
60
|
const context = buildContext(analysis, config);
|
|
59
|
-
|
|
60
|
-
// Render template
|
|
61
61
|
const content = renderTemplateByName('claude', context);
|
|
62
|
-
|
|
63
|
-
// Write output file
|
|
64
62
|
const outputPath = getOutputPath(projectRoot);
|
|
65
63
|
fs.writeFileSync(outputPath, content, 'utf-8');
|
|
66
|
-
|
|
67
|
-
result.success = true;
|
|
68
64
|
result.files.push({
|
|
69
65
|
path: outputPath,
|
|
70
66
|
relativePath: 'AI_CONTEXT.md',
|
|
71
67
|
size: content.length
|
|
72
68
|
});
|
|
69
|
+
|
|
70
|
+
// 2. Generate .claude/ directory structure (NEW)
|
|
71
|
+
const claudeDirResult = await generateClaudeDirectory(projectRoot, context, result);
|
|
72
|
+
if (claudeDirResult) {
|
|
73
|
+
result.files.push(...claudeDirResult);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
result.success = result.errors.length === 0 || result.errors.some(e => e.code === 'EXISTS');
|
|
73
77
|
} catch (error) {
|
|
74
78
|
result.errors.push({
|
|
75
79
|
message: error.message,
|
|
@@ -80,36 +84,183 @@ async function generate(analysis, config, projectRoot) {
|
|
|
80
84
|
return result;
|
|
81
85
|
}
|
|
82
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Generate .claude/ directory with full structure
|
|
89
|
+
* @param {string} projectRoot - Project root directory
|
|
90
|
+
* @param {object} context - Template context
|
|
91
|
+
* @param {object} result - Result object to track files/errors
|
|
92
|
+
* @returns {Array} List of generated files
|
|
93
|
+
*/
|
|
94
|
+
async function generateClaudeDirectory(projectRoot, context, result) {
|
|
95
|
+
const { copyDirectory } = require('../installer');
|
|
96
|
+
const templatesDir = path.join(__dirname, '..', '..', 'templates', 'base');
|
|
97
|
+
const claudeDir = path.join(projectRoot, '.claude');
|
|
98
|
+
|
|
99
|
+
// Don't overwrite existing .claude/ directory
|
|
100
|
+
if (fs.existsSync(claudeDir)) {
|
|
101
|
+
result.errors.push({
|
|
102
|
+
message: '.claude/ directory already exists, skipping structure generation',
|
|
103
|
+
code: 'EXISTS',
|
|
104
|
+
severity: 'warning'
|
|
105
|
+
});
|
|
106
|
+
return [{
|
|
107
|
+
path: claudeDir,
|
|
108
|
+
relativePath: '.claude/',
|
|
109
|
+
size: 0,
|
|
110
|
+
skipped: true
|
|
111
|
+
}];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
// Create .claude/ directory
|
|
116
|
+
fs.mkdirSync(claudeDir, { recursive: true });
|
|
117
|
+
|
|
118
|
+
// Copy relevant subdirectories from templates/base to .claude/
|
|
119
|
+
const subdirsToCopy = [
|
|
120
|
+
'agents',
|
|
121
|
+
'commands',
|
|
122
|
+
'indexes',
|
|
123
|
+
'context',
|
|
124
|
+
'schemas',
|
|
125
|
+
'standards'
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
// Only copy tools if explicitly enabled
|
|
129
|
+
if (context.features?.tools !== false) {
|
|
130
|
+
subdirsToCopy.push('tools');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let filesCopied = 0;
|
|
134
|
+
for (const subdir of subdirsToCopy) {
|
|
135
|
+
const srcPath = path.join(templatesDir, subdir);
|
|
136
|
+
if (fs.existsSync(srcPath)) {
|
|
137
|
+
const destPath = path.join(claudeDir, subdir);
|
|
138
|
+
fs.mkdirSync(destPath, { recursive: true });
|
|
139
|
+
const count = await copyDirectory(srcPath, destPath);
|
|
140
|
+
filesCopied += count;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Create minimal .claude/settings.json
|
|
145
|
+
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
146
|
+
const settings = {
|
|
147
|
+
'$schema': './schemas/settings.schema.json',
|
|
148
|
+
version: '2.1.0',
|
|
149
|
+
project: {
|
|
150
|
+
name: context.project?.name || 'Project',
|
|
151
|
+
tech_stack: context.project?.tech_stack || 'Not detected'
|
|
152
|
+
},
|
|
153
|
+
agents: {
|
|
154
|
+
context_engineer: 'enabled',
|
|
155
|
+
core_architect: 'enabled',
|
|
156
|
+
api_developer: 'enabled',
|
|
157
|
+
database_ops: 'enabled',
|
|
158
|
+
integration_hub: 'enabled',
|
|
159
|
+
deployment_ops: 'enabled'
|
|
160
|
+
},
|
|
161
|
+
commands: {
|
|
162
|
+
rpi_workflow: 'enabled',
|
|
163
|
+
validation: 'enabled'
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
167
|
+
filesCopied++;
|
|
168
|
+
|
|
169
|
+
// Create .claude/README.md
|
|
170
|
+
const readmePath = path.join(claudeDir, 'README.md');
|
|
171
|
+
const readme = `# .claude Configuration - ${context.project?.name || 'Project'}
|
|
172
|
+
|
|
173
|
+
This directory contains Claude Code-specific context engineering files.
|
|
174
|
+
|
|
175
|
+
## Quick Start
|
|
176
|
+
|
|
177
|
+
1. Load agents: \`@context-engineer\`
|
|
178
|
+
2. Use commands: \`/rpi-research\`, \`/rpi-plan\`, \`/rpi-implement\`
|
|
179
|
+
3. Validate: \`/verify-docs-current\`
|
|
180
|
+
|
|
181
|
+
## Universal Context
|
|
182
|
+
|
|
183
|
+
See \`AI_CONTEXT.md\` at project root for universal AI context (works with all tools).
|
|
184
|
+
|
|
185
|
+
## Claude-Specific Files
|
|
186
|
+
|
|
187
|
+
- **agents/** - Specialized agents for different tasks
|
|
188
|
+
- **commands/** - Custom slash commands
|
|
189
|
+
- **indexes/** - 3-level navigation system
|
|
190
|
+
- **context/** - Workflow documentation
|
|
191
|
+
|
|
192
|
+
*Generated by create-universal-ai-context v${context.version || '2.1.0'}*
|
|
193
|
+
`;
|
|
194
|
+
fs.writeFileSync(readmePath, readme);
|
|
195
|
+
filesCopied++;
|
|
196
|
+
|
|
197
|
+
return [{
|
|
198
|
+
path: claudeDir,
|
|
199
|
+
relativePath: '.claude/',
|
|
200
|
+
size: filesCopied
|
|
201
|
+
}];
|
|
202
|
+
|
|
203
|
+
} catch (error) {
|
|
204
|
+
result.errors.push({
|
|
205
|
+
message: `Failed to generate .claude/ directory: ${error.message}`,
|
|
206
|
+
stack: error.stack
|
|
207
|
+
});
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
83
212
|
/**
|
|
84
213
|
* Validate Claude output
|
|
85
214
|
* @param {string} projectRoot - Project root directory
|
|
86
215
|
* @returns {object} Validation result
|
|
87
216
|
*/
|
|
88
217
|
function validate(projectRoot) {
|
|
89
|
-
const
|
|
218
|
+
const issues = [];
|
|
90
219
|
|
|
220
|
+
// 1. Validate AI_CONTEXT.md
|
|
221
|
+
const outputPath = getOutputPath(projectRoot);
|
|
91
222
|
if (!fs.existsSync(outputPath)) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
};
|
|
223
|
+
issues.push({ file: 'AI_CONTEXT.md', error: 'not found' });
|
|
224
|
+
} else {
|
|
225
|
+
const content = fs.readFileSync(outputPath, 'utf-8');
|
|
226
|
+
const placeholderMatch = content.match(/\{\{[A-Z_]+\}\}/g);
|
|
227
|
+
if (placeholderMatch && placeholderMatch.length > 0) {
|
|
228
|
+
issues.push({
|
|
229
|
+
file: 'AI_CONTEXT.md',
|
|
230
|
+
error: `Found ${placeholderMatch.length} unreplaced placeholders`
|
|
231
|
+
});
|
|
232
|
+
}
|
|
96
233
|
}
|
|
97
234
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
235
|
+
// 2. Validate .claude/ directory (optional, warn if missing)
|
|
236
|
+
const claudeDir = path.join(projectRoot, '.claude');
|
|
237
|
+
if (!fs.existsSync(claudeDir)) {
|
|
238
|
+
issues.push({
|
|
239
|
+
file: '.claude/',
|
|
240
|
+
error: 'directory not found (optional but recommended)',
|
|
241
|
+
severity: 'warning'
|
|
242
|
+
});
|
|
243
|
+
} else {
|
|
244
|
+
// Check for critical files
|
|
245
|
+
const criticalFiles = [
|
|
246
|
+
'settings.json',
|
|
247
|
+
'README.md'
|
|
248
|
+
];
|
|
249
|
+
for (const file of criticalFiles) {
|
|
250
|
+
if (!fs.existsSync(path.join(claudeDir, file))) {
|
|
251
|
+
issues.push({
|
|
252
|
+
file: `.claude/${file}`,
|
|
253
|
+
error: 'missing',
|
|
254
|
+
severity: 'warning'
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
108
258
|
}
|
|
109
259
|
|
|
110
260
|
return {
|
|
111
|
-
valid:
|
|
112
|
-
|
|
261
|
+
valid: issues.filter(i => i.severity !== 'warning').length === 0,
|
|
262
|
+
issues,
|
|
263
|
+
warnings: issues.filter(i => i.severity === 'warning').length
|
|
113
264
|
};
|
|
114
265
|
}
|
|
115
266
|
|