create-universal-ai-context 2.1.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/lib/adapters/claude.js +180 -29
- package/lib/installer.js +1 -0
- package/package.json +1 -1
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
|
|
package/lib/installer.js
CHANGED
package/package.json
CHANGED