ai-development-framework 0.1.0 → 0.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/.claude/commands/create-prd.md +23 -2
- package/.claude/commands/execute.md +14 -0
- package/.claude/commands/plan-project.md +18 -0
- package/.claude/commands/prime.md +14 -0
- package/.claude/commands/ship.md +45 -0
- package/.claude/commands/start.md +10 -1
- package/.claude/references/knowledge-base-templates.md +124 -0
- package/CLAUDE.md +38 -0
- package/cli/init.js +308 -73
- package/cli/protected-files.js +65 -0
- package/cli/update.js +145 -55
- package/docs/customization.md +56 -0
- package/docs/plans/2026-04-04-knowledge-base-integration-design.md +209 -0
- package/docs/superpowers/plans/2026-04-04-knowledge-base-integration.md +526 -0
- package/package.json +1 -1
package/cli/init.js
CHANGED
|
@@ -2,6 +2,7 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { execFileSync } = require('child_process');
|
|
4
4
|
const readline = require('readline');
|
|
5
|
+
const { PROTECTED_FILES, PROTECTED_DIRS, CUSTOMIZABLE_FILES, toProjectRelative } = require('./protected-files');
|
|
5
6
|
|
|
6
7
|
const REPO = 'cristian-robert/AIDevelopmentFramework';
|
|
7
8
|
const BRANCH = 'main';
|
|
@@ -18,66 +19,193 @@ function ask(question) {
|
|
|
18
19
|
});
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
var entries = fs.readdirSync(src, { withFileTypes: true });
|
|
26
|
-
for (var i = 0; i < entries.length; i++) {
|
|
27
|
-
var entry = entries[i];
|
|
28
|
-
var srcPath = path.join(src, entry.name);
|
|
29
|
-
var destPath = path.join(dest, entry.name);
|
|
30
|
-
if (entry.isDirectory()) {
|
|
31
|
-
copyDirRecursive(srcPath, destPath);
|
|
32
|
-
} else {
|
|
33
|
-
fs.copyFileSync(srcPath, destPath);
|
|
34
|
-
}
|
|
22
|
+
function copyFileSimple(srcPath, destPath) {
|
|
23
|
+
var destDir = path.dirname(destPath);
|
|
24
|
+
if (!fs.existsSync(destDir)) {
|
|
25
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
35
26
|
}
|
|
27
|
+
fs.copyFileSync(srcPath, destPath);
|
|
36
28
|
}
|
|
37
29
|
|
|
38
|
-
function
|
|
39
|
-
var tmpDir = path.join(require('os').tmpdir(), 'ai-framework-' + Date.now());
|
|
40
|
-
fs.mkdirSync(tmpDir, { recursive: true });
|
|
41
|
-
|
|
30
|
+
function downloadAndExtract(tmpDir) {
|
|
42
31
|
console.log('Downloading latest framework from GitHub...');
|
|
43
|
-
|
|
44
32
|
try {
|
|
45
|
-
// Download and extract tarball
|
|
46
33
|
execFileSync('curl', ['-sL', TARBALL_URL, '-o', path.join(tmpDir, 'framework.tar.gz')]);
|
|
47
34
|
execFileSync('tar', ['-xzf', path.join(tmpDir, 'framework.tar.gz'), '-C', tmpDir, '--strip-components=1']);
|
|
35
|
+
return true;
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.error('Download failed: ' + err.message);
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
48
41
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
42
|
+
function getLocalFallbackDir() {
|
|
43
|
+
var frameworkDir = path.join(__dirname, '..');
|
|
44
|
+
if (fs.existsSync(path.join(frameworkDir, '.claude'))) {
|
|
45
|
+
return frameworkDir;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
function isTemplateContent(filePath) {
|
|
51
|
+
try {
|
|
52
|
+
var content = fs.readFileSync(filePath, 'utf-8');
|
|
53
|
+
if (content.includes('> Populated by /create-rules')) return true;
|
|
54
|
+
if (content.includes('> Populated when /create-rules')) return true;
|
|
55
|
+
if (content.includes('> No decisions recorded yet')) return true;
|
|
56
|
+
if (content.includes('Run `/create-rules` to populate')) return true;
|
|
57
|
+
if (content.includes('> This section is populated as the project grows')) return true;
|
|
58
|
+
return false;
|
|
59
|
+
} catch (e) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function cleanupTmpDir(tmpDir) {
|
|
65
|
+
try {
|
|
66
|
+
if (fs.existsSync(tmpDir)) {
|
|
67
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
68
|
+
}
|
|
69
|
+
} catch (e) {
|
|
70
|
+
// Best-effort cleanup
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Collect all files from source, compute project-root-relative paths, classify them
|
|
75
|
+
function collectSourceFiles(sourceDir, projectRoot) {
|
|
76
|
+
var files = [];
|
|
77
|
+
|
|
78
|
+
function walk(dir) {
|
|
79
|
+
var entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
80
|
+
for (var i = 0; i < entries.length; i++) {
|
|
81
|
+
var entry = entries[i];
|
|
82
|
+
var srcPath = path.join(dir, entry.name);
|
|
83
|
+
if (entry.isDirectory()) {
|
|
84
|
+
walk(srcPath);
|
|
85
|
+
} else {
|
|
86
|
+
// Compute the project-root-relative path this file WOULD have
|
|
87
|
+
var relFromSource = path.relative(sourceDir, srcPath).split(path.sep).join('/');
|
|
88
|
+
files.push({ srcPath: srcPath, relPath: relFromSource });
|
|
89
|
+
}
|
|
56
90
|
}
|
|
91
|
+
}
|
|
57
92
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
93
|
+
walk(sourceDir);
|
|
94
|
+
return files;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function detectConflicts(sourceDir, targetDir, projectRoot) {
|
|
98
|
+
var conflicts = { protected: [], customized: [], safe: [] };
|
|
99
|
+
var files = collectSourceFiles(sourceDir, projectRoot);
|
|
100
|
+
|
|
101
|
+
for (var i = 0; i < files.length; i++) {
|
|
102
|
+
var relPath = files[i].relPath;
|
|
103
|
+
var srcPath = files[i].srcPath;
|
|
104
|
+
var destPath = path.join(targetDir, relPath);
|
|
61
105
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
106
|
+
// Compute project-root-relative path for matching against protection lists
|
|
107
|
+
var projectRelPath = toProjectRelative(destPath, projectRoot);
|
|
108
|
+
|
|
109
|
+
if (!fs.existsSync(destPath)) {
|
|
110
|
+
conflicts.safe.push(relPath);
|
|
111
|
+
} else if (PROTECTED_FILES.indexOf(projectRelPath) !== -1) {
|
|
112
|
+
if (!isTemplateContent(destPath)) {
|
|
113
|
+
conflicts.protected.push(relPath);
|
|
114
|
+
} else {
|
|
115
|
+
conflicts.safe.push(relPath);
|
|
116
|
+
}
|
|
117
|
+
} else if (CUSTOMIZABLE_FILES.indexOf(projectRelPath) !== -1) {
|
|
118
|
+
var srcContent = fs.readFileSync(srcPath, 'utf-8');
|
|
119
|
+
var destContent = fs.readFileSync(destPath, 'utf-8');
|
|
120
|
+
if (srcContent !== destContent) {
|
|
121
|
+
conflicts.customized.push(relPath);
|
|
122
|
+
} else {
|
|
123
|
+
conflicts.safe.push(relPath);
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
conflicts.safe.push(relPath);
|
|
65
127
|
}
|
|
128
|
+
}
|
|
66
129
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
130
|
+
return conflicts;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function smartCopy(sourceDir, targetDir, projectRoot, strategy, customizedAction) {
|
|
134
|
+
var stats = { created: 0, updated: 0, skipped: 0, backedUp: 0 };
|
|
135
|
+
|
|
136
|
+
function copy(src, dest) {
|
|
137
|
+
if (!fs.existsSync(dest)) {
|
|
138
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
139
|
+
}
|
|
140
|
+
var entries = fs.readdirSync(src, { withFileTypes: true });
|
|
141
|
+
for (var i = 0; i < entries.length; i++) {
|
|
142
|
+
var entry = entries[i];
|
|
143
|
+
var srcPath = path.join(src, entry.name);
|
|
144
|
+
var destPath = path.join(dest, entry.name);
|
|
145
|
+
var projectRelPath = toProjectRelative(destPath, projectRoot);
|
|
146
|
+
|
|
147
|
+
if (entry.isDirectory()) {
|
|
148
|
+
if (strategy === 'smart') {
|
|
149
|
+
var isProtectedDir = PROTECTED_DIRS.some(function (d) {
|
|
150
|
+
return projectRelPath === d || projectRelPath.startsWith(d + '/');
|
|
151
|
+
});
|
|
152
|
+
if (isProtectedDir && fs.existsSync(destPath)) {
|
|
153
|
+
// Count actual files in the directory for accurate reporting
|
|
154
|
+
try {
|
|
155
|
+
var dirFiles = fs.readdirSync(destPath, { recursive: true });
|
|
156
|
+
stats.skipped += dirFiles.length || 1;
|
|
157
|
+
} catch (e) {
|
|
158
|
+
stats.skipped++;
|
|
159
|
+
}
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
copy(srcPath, destPath);
|
|
164
|
+
} else {
|
|
165
|
+
var destExists = fs.existsSync(destPath);
|
|
166
|
+
|
|
167
|
+
if (strategy === 'smart' && destExists) {
|
|
168
|
+
// Protected file with real content — never overwrite
|
|
169
|
+
if (PROTECTED_FILES.indexOf(projectRelPath) !== -1 && !isTemplateContent(destPath)) {
|
|
170
|
+
stats.skipped++;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Customized file — apply user's chosen action
|
|
175
|
+
if (CUSTOMIZABLE_FILES.indexOf(projectRelPath) !== -1) {
|
|
176
|
+
var srcContent = fs.readFileSync(srcPath, 'utf-8');
|
|
177
|
+
var destContent = fs.readFileSync(destPath, 'utf-8');
|
|
178
|
+
if (srcContent !== destContent) {
|
|
179
|
+
if (customizedAction === 'keep') {
|
|
180
|
+
stats.skipped++;
|
|
181
|
+
continue;
|
|
182
|
+
} else if (customizedAction === 'backup') {
|
|
183
|
+
// Use timestamped backup name to avoid overwriting previous backups
|
|
184
|
+
var timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
185
|
+
var backupPath = destPath + '.' + timestamp + '.backup';
|
|
186
|
+
fs.copyFileSync(destPath, backupPath);
|
|
187
|
+
fs.copyFileSync(srcPath, destPath);
|
|
188
|
+
stats.backedUp++;
|
|
189
|
+
stats.updated++;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
// 'overwrite' falls through to normal copy
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
fs.copyFileSync(srcPath, destPath);
|
|
198
|
+
if (destExists) {
|
|
199
|
+
stats.updated++;
|
|
200
|
+
} else {
|
|
201
|
+
stats.created++;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
79
204
|
}
|
|
80
205
|
}
|
|
206
|
+
|
|
207
|
+
copy(sourceDir, targetDir);
|
|
208
|
+
return stats;
|
|
81
209
|
}
|
|
82
210
|
|
|
83
211
|
function detectTechStack() {
|
|
@@ -115,7 +243,7 @@ function detectTechStack() {
|
|
|
115
243
|
if (reqContent.includes('django')) detected.push('Django');
|
|
116
244
|
if (reqContent.includes('flask')) detected.push('Flask');
|
|
117
245
|
} catch (e) {
|
|
118
|
-
// ignore
|
|
246
|
+
// ignore
|
|
119
247
|
}
|
|
120
248
|
}
|
|
121
249
|
if (fs.existsSync('go.mod')) detected.push('Go');
|
|
@@ -133,45 +261,143 @@ async function main() {
|
|
|
133
261
|
var targetDir = process.cwd();
|
|
134
262
|
var hasGit = fs.existsSync('.git');
|
|
135
263
|
var hasClaudeDir = fs.existsSync('.claude');
|
|
136
|
-
|
|
137
|
-
// Check if .claude/ already exists
|
|
138
|
-
if (hasClaudeDir) {
|
|
139
|
-
var overwrite = await ask('.claude/ already exists. Overwrite? (yes/no): ');
|
|
140
|
-
if (overwrite.toLowerCase() !== 'yes' && overwrite.toLowerCase() !== 'y') {
|
|
141
|
-
console.log('Aborted. Use "npx ai-framework update" to update existing installation.');
|
|
142
|
-
rl.close();
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
264
|
+
var hasCLAUDEmd = fs.existsSync('CLAUDE.md');
|
|
146
265
|
|
|
147
266
|
// Detect tech stack
|
|
148
267
|
var stack = detectTechStack();
|
|
149
268
|
if (stack.length > 0) {
|
|
150
|
-
console.log('Detected tech stack: ' + stack.join(', '));
|
|
269
|
+
console.log(' Detected tech stack: ' + stack.join(', '));
|
|
270
|
+
console.log('');
|
|
151
271
|
}
|
|
152
272
|
|
|
153
|
-
// Download
|
|
154
|
-
var
|
|
155
|
-
|
|
156
|
-
// Fall back to local copy if download fails (when installed via npm)
|
|
157
|
-
if (!downloaded) {
|
|
158
|
-
var frameworkDir = path.join(__dirname, '..');
|
|
159
|
-
var localClaudeDir = path.join(frameworkDir, '.claude');
|
|
160
|
-
if (fs.existsSync(localClaudeDir)) {
|
|
161
|
-
console.log('Copying from local package...');
|
|
162
|
-
copyDirRecursive(localClaudeDir, path.join(targetDir, '.claude'));
|
|
273
|
+
// Download framework to temp dir
|
|
274
|
+
var tmpDir = path.join(require('os').tmpdir(), 'ai-framework-' + Date.now());
|
|
275
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
163
276
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
277
|
+
var sourceDir = null;
|
|
278
|
+
var downloaded = downloadAndExtract(tmpDir);
|
|
279
|
+
if (downloaded) {
|
|
280
|
+
sourceDir = tmpDir;
|
|
281
|
+
} else {
|
|
282
|
+
var fallback = getLocalFallbackDir();
|
|
283
|
+
if (fallback) {
|
|
284
|
+
console.log('Using local package as fallback...');
|
|
285
|
+
sourceDir = fallback;
|
|
168
286
|
} else {
|
|
169
|
-
console.error('No
|
|
287
|
+
console.error('No framework source available. Check your internet connection.');
|
|
288
|
+
cleanupTmpDir(tmpDir);
|
|
170
289
|
rl.close();
|
|
171
290
|
process.exit(1);
|
|
172
291
|
}
|
|
173
292
|
}
|
|
174
293
|
|
|
294
|
+
var strategy = 'fresh';
|
|
295
|
+
var customizedAction = 'overwrite';
|
|
296
|
+
|
|
297
|
+
if (hasClaudeDir || hasCLAUDEmd) {
|
|
298
|
+
console.log('Existing configuration detected. Scanning for conflicts...');
|
|
299
|
+
console.log('');
|
|
300
|
+
|
|
301
|
+
// Scan .claude/ directory for conflicts
|
|
302
|
+
var claudeConflicts = { protected: [], customized: [], safe: [] };
|
|
303
|
+
var sourceClaudeDir = path.join(sourceDir, '.claude');
|
|
304
|
+
if (fs.existsSync(sourceClaudeDir)) {
|
|
305
|
+
claudeConflicts = detectConflicts(sourceClaudeDir, path.join(targetDir, '.claude'), targetDir);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Check CLAUDE.md separately
|
|
309
|
+
if (hasCLAUDEmd && !isTemplateContent(path.join(targetDir, 'CLAUDE.md'))) {
|
|
310
|
+
claudeConflicts.protected.push('CLAUDE.md');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (claudeConflicts.protected.length > 0) {
|
|
314
|
+
console.log(' Project-specific files (will be preserved):');
|
|
315
|
+
for (var i = 0; i < claudeConflicts.protected.length; i++) {
|
|
316
|
+
console.log(' ' + claudeConflicts.protected[i]);
|
|
317
|
+
}
|
|
318
|
+
console.log('');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (claudeConflicts.customized.length > 0) {
|
|
322
|
+
console.log(' Modified files (differ from framework defaults):');
|
|
323
|
+
for (var j = 0; j < claudeConflicts.customized.length; j++) {
|
|
324
|
+
console.log(' ' + claudeConflicts.customized[j]);
|
|
325
|
+
}
|
|
326
|
+
console.log('');
|
|
327
|
+
|
|
328
|
+
var choice = await ask(
|
|
329
|
+
' How should modified files be handled?\n' +
|
|
330
|
+
' 1. Keep mine — preserve your customizations, skip framework updates\n' +
|
|
331
|
+
' 2. Use framework — overwrite with latest framework versions\n' +
|
|
332
|
+
' 3. Backup + update — save yours as .backup, install framework versions\n' +
|
|
333
|
+
' Choice (1/2/3): '
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
if (choice === '1') customizedAction = 'keep';
|
|
337
|
+
else if (choice === '2') customizedAction = 'overwrite';
|
|
338
|
+
else if (choice === '3') customizedAction = 'backup';
|
|
339
|
+
else customizedAction = 'keep'; // default to safe option
|
|
340
|
+
|
|
341
|
+
console.log('');
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (claudeConflicts.safe.length > 0) {
|
|
345
|
+
console.log(' New/unchanged files: ' + claudeConflicts.safe.length + ' (will be installed)');
|
|
346
|
+
console.log('');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
strategy = 'smart';
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Install .claude/
|
|
353
|
+
console.log('Installing framework...');
|
|
354
|
+
var stats = smartCopy(
|
|
355
|
+
path.join(sourceDir, '.claude'),
|
|
356
|
+
path.join(targetDir, '.claude'),
|
|
357
|
+
targetDir,
|
|
358
|
+
strategy,
|
|
359
|
+
customizedAction
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
// Install CLAUDE.md from framework source (if not protected)
|
|
363
|
+
var claudeMdSource = path.join(sourceDir, 'CLAUDE.md');
|
|
364
|
+
if (fs.existsSync(claudeMdSource)) {
|
|
365
|
+
var claudeMdDest = path.join(targetDir, 'CLAUDE.md');
|
|
366
|
+
if (!fs.existsSync(claudeMdDest)) {
|
|
367
|
+
copyFileSimple(claudeMdSource, claudeMdDest);
|
|
368
|
+
stats.created++;
|
|
369
|
+
} else if (strategy === 'fresh') {
|
|
370
|
+
copyFileSimple(claudeMdSource, claudeMdDest);
|
|
371
|
+
stats.updated++;
|
|
372
|
+
}
|
|
373
|
+
// In 'smart' mode, CLAUDE.md is handled by the protection logic above
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Install docs/ (methodology guides only, not project-specific plans)
|
|
377
|
+
var docsSource = path.join(sourceDir, 'docs');
|
|
378
|
+
if (fs.existsSync(docsSource)) {
|
|
379
|
+
var docsTarget = path.join(targetDir, 'docs');
|
|
380
|
+
if (!fs.existsSync(docsTarget)) {
|
|
381
|
+
fs.mkdirSync(docsTarget, { recursive: true });
|
|
382
|
+
}
|
|
383
|
+
var docsEntries = fs.readdirSync(docsSource, { withFileTypes: true });
|
|
384
|
+
for (var k = 0; k < docsEntries.length; k++) {
|
|
385
|
+
var entry = docsEntries[k];
|
|
386
|
+
if (entry.isFile()) {
|
|
387
|
+
var srcPath = path.join(docsSource, entry.name);
|
|
388
|
+
var destPath = path.join(docsTarget, entry.name);
|
|
389
|
+
var existed = fs.existsSync(destPath);
|
|
390
|
+
fs.copyFileSync(srcPath, destPath);
|
|
391
|
+
if (existed) {
|
|
392
|
+
stats.updated++;
|
|
393
|
+
} else {
|
|
394
|
+
stats.created++;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
// Skip docs/plans/ and docs/superpowers/ — project-specific
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
175
401
|
// Create docs/plans directory
|
|
176
402
|
var plansDir = path.join(targetDir, 'docs', 'plans');
|
|
177
403
|
if (!fs.existsSync(plansDir)) {
|
|
@@ -180,6 +406,7 @@ async function main() {
|
|
|
180
406
|
|
|
181
407
|
// Init git if needed
|
|
182
408
|
if (!hasGit) {
|
|
409
|
+
console.log('');
|
|
183
410
|
var initGit = await ask('No git repo found. Initialize one? (yes/no): ');
|
|
184
411
|
if (initGit.toLowerCase() === 'yes' || initGit.toLowerCase() === 'y') {
|
|
185
412
|
try {
|
|
@@ -192,15 +419,22 @@ async function main() {
|
|
|
192
419
|
}
|
|
193
420
|
}
|
|
194
421
|
|
|
422
|
+
// Summary
|
|
195
423
|
console.log('');
|
|
196
424
|
console.log('Setup complete!');
|
|
197
425
|
console.log('');
|
|
198
|
-
console.log('
|
|
426
|
+
console.log(' Created: ' + stats.created + ' files');
|
|
427
|
+
console.log(' Updated: ' + stats.updated + ' files');
|
|
428
|
+
console.log(' Skipped: ' + stats.skipped + ' files (preserved your customizations)');
|
|
429
|
+
if (stats.backedUp > 0) {
|
|
430
|
+
console.log(' Backed up: ' + stats.backedUp + ' files (saved as .backup)');
|
|
431
|
+
}
|
|
432
|
+
console.log('');
|
|
199
433
|
console.log(' .claude/commands/ 10 pipeline commands');
|
|
200
434
|
console.log(' .claude/agents/ 4 specialist agents + template');
|
|
201
435
|
console.log(' .claude/skills/ 2 framework skills');
|
|
202
436
|
console.log(' .claude/rules/ 6 domain rules + template');
|
|
203
|
-
console.log(' .claude/references/
|
|
437
|
+
console.log(' .claude/references/ 6 templates');
|
|
204
438
|
console.log(' .claude/hooks/ 5 guardrails');
|
|
205
439
|
console.log(' docs/ methodology + guides');
|
|
206
440
|
console.log('');
|
|
@@ -210,6 +444,7 @@ async function main() {
|
|
|
210
444
|
console.log(' 3. Run /start to begin');
|
|
211
445
|
console.log('');
|
|
212
446
|
|
|
447
|
+
cleanupTmpDir(tmpDir);
|
|
213
448
|
rl.close();
|
|
214
449
|
}
|
|
215
450
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Files that contain project-specific content and should never be blindly overwritten.
|
|
2
|
+
// All paths are relative to the PROJECT ROOT (not .claude/).
|
|
3
|
+
var PROTECTED_FILES = [
|
|
4
|
+
// Project CLAUDE.md (user may have their own)
|
|
5
|
+
'CLAUDE.md',
|
|
6
|
+
|
|
7
|
+
// Agent knowledge bases (populated per-project by /evolve and architect-agent RECORD)
|
|
8
|
+
'.claude/agents/architect-agent/index.md',
|
|
9
|
+
'.claude/agents/architect-agent/shared/patterns.md',
|
|
10
|
+
'.claude/agents/architect-agent/decisions/log.md',
|
|
11
|
+
|
|
12
|
+
// Tester agent project-specific config
|
|
13
|
+
'.claude/agents/tester-agent/test-patterns.md',
|
|
14
|
+
'.claude/agents/tester-agent/auth-state.md',
|
|
15
|
+
|
|
16
|
+
// Mobile tester project-specific config
|
|
17
|
+
'.claude/agents/mobile-tester-agent/screen-patterns.md',
|
|
18
|
+
|
|
19
|
+
// Project-specific code patterns (generated by /create-rules)
|
|
20
|
+
'.claude/references/code-patterns.md',
|
|
21
|
+
|
|
22
|
+
// Project-specific settings and permissions
|
|
23
|
+
'.claude/settings.local.json',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
// Directories with project-specific content (architect-agent module knowledge).
|
|
27
|
+
// All paths are relative to the PROJECT ROOT.
|
|
28
|
+
var PROTECTED_DIRS = [
|
|
29
|
+
'.claude/agents/architect-agent/modules',
|
|
30
|
+
'.claude/agents/architect-agent/frontend',
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
// Files that users commonly customize (rules, hooks).
|
|
34
|
+
// These get special "merge" treatment — ask user what to do.
|
|
35
|
+
// All paths are relative to the PROJECT ROOT.
|
|
36
|
+
var CUSTOMIZABLE_FILES = [
|
|
37
|
+
'.claude/rules/_global.md',
|
|
38
|
+
'.claude/rules/backend.md',
|
|
39
|
+
'.claude/rules/frontend.md',
|
|
40
|
+
'.claude/rules/mobile.md',
|
|
41
|
+
'.claude/rules/database.md',
|
|
42
|
+
'.claude/rules/testing.md',
|
|
43
|
+
'.claude/hooks/branch-guard.sh',
|
|
44
|
+
'.claude/hooks/plan-required.sh',
|
|
45
|
+
'.claude/hooks/architect-sync.sh',
|
|
46
|
+
'.claude/hooks/evolve-reminder.sh',
|
|
47
|
+
'.claude/hooks/session-primer.sh',
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
// Helper: normalize a path to project-root-relative format.
|
|
51
|
+
// Call sites that compare against these lists MUST use this function
|
|
52
|
+
// to ensure the path prefix matches (e.g., '.claude/rules/backend.md').
|
|
53
|
+
function toProjectRelative(filePath, rootDir) {
|
|
54
|
+
var path = require('path');
|
|
55
|
+
var abs = path.resolve(filePath);
|
|
56
|
+
var root = path.resolve(rootDir);
|
|
57
|
+
return path.relative(root, abs).split(path.sep).join('/');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = {
|
|
61
|
+
PROTECTED_FILES: PROTECTED_FILES,
|
|
62
|
+
PROTECTED_DIRS: PROTECTED_DIRS,
|
|
63
|
+
CUSTOMIZABLE_FILES: CUSTOMIZABLE_FILES,
|
|
64
|
+
toProjectRelative: toProjectRelative,
|
|
65
|
+
};
|