ccsetup 1.2.0 → 1.2.1
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 +93 -24
- package/bin/create-project.js +373 -773
- package/lib/templates/README.md +2 -2
- package/lib/templates/metadata/agents.json +1 -1
- package/package.json +3 -2
- package/template/.claude/settings.json +18 -1
- package/template/.claude/skills/codex-review/SKILL.md +139 -0
- package/template/.claude/skills/secops/SKILL.md +259 -0
- package/template/.codex/skills/codex-review/SKILL.md +139 -0
- package/template/.codex/skills/prd/SKILL.md +343 -0
- package/template/.codex/skills/ralph/SKILL.md +339 -0
- package/template/AGENTS.md +43 -0
- package/template/CLAUDE.md +106 -4
- package/template/docs/codex-setup.md +32 -0
- package/template/hooks/codex-review/index.js +105 -0
- package/template/scripts/codex-review/codex-review.sh +266 -0
- package/template/scripts/ralph/CODEX.md +76 -0
- package/template/scripts/ralph/ralph.sh +32 -9
- package/bin/lib/contextGenerator.js +0 -287
- package/bin/lib/scanner/index.js +0 -28
- package/bin/scan.js +0 -367
- package/lib/aiMergeHelper.js +0 -112
- package/lib/contextGenerator.js +0 -574
- package/lib/contextMerger.js +0 -812
- package/lib/progressReporter.js +0 -88
- package/lib/scanConfig.js +0 -200
- package/lib/scanner/fileAnalyzer.js +0 -605
- package/lib/scanner/index.js +0 -164
- package/lib/scanner/patterns.js +0 -277
- package/lib/scanner/projectDetector.js +0 -147
package/bin/create-project.js
CHANGED
|
@@ -3,10 +3,6 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const readline = require('readline');
|
|
6
|
-
const RepositoryScanner = require('../lib/scanner');
|
|
7
|
-
const ContextGenerator = require('../lib/contextGenerator');
|
|
8
|
-
const ContextMerger = require('../lib/contextMerger');
|
|
9
|
-
const ProgressReporter = require('../lib/progressReporter');
|
|
10
6
|
const TemplateCatalog = require('../lib/templates/catalog');
|
|
11
7
|
const TemplateFilter = require('../lib/templates/filter');
|
|
12
8
|
const TemplateSearch = require('../lib/templates/search');
|
|
@@ -14,12 +10,6 @@ const TemplateSearch = require('../lib/templates/search');
|
|
|
14
10
|
// Parse CLI arguments
|
|
15
11
|
const args = process.argv.slice(2);
|
|
16
12
|
|
|
17
|
-
// Handle scan subcommand
|
|
18
|
-
if (args[0] === 'scan') {
|
|
19
|
-
require('./scan.js');
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
13
|
const flags = {
|
|
24
14
|
force: false,
|
|
25
15
|
dryRun: false,
|
|
@@ -29,8 +19,6 @@ const flags = {
|
|
|
29
19
|
agents: false,
|
|
30
20
|
browseAgents: false,
|
|
31
21
|
browse: false,
|
|
32
|
-
scanContext: false,
|
|
33
|
-
scanOnly: false,
|
|
34
22
|
prompt: null
|
|
35
23
|
};
|
|
36
24
|
|
|
@@ -55,10 +43,6 @@ for (let i = 0; i < args.length; i++) {
|
|
|
55
43
|
flags.browseAgents = true;
|
|
56
44
|
} else if (arg === '--browse') {
|
|
57
45
|
flags.browse = true;
|
|
58
|
-
} else if (arg === '--scan-context') {
|
|
59
|
-
flags.scanContext = true;
|
|
60
|
-
} else if (arg === '--scan-only') {
|
|
61
|
-
flags.scanOnly = true;
|
|
62
46
|
} else if (arg === '--install-hooks') {
|
|
63
47
|
flags.installHooks = true;
|
|
64
48
|
} else if (!arg.startsWith('-')) {
|
|
@@ -70,15 +54,12 @@ for (let i = 0; i < args.length; i++) {
|
|
|
70
54
|
if (flags.help) {
|
|
71
55
|
console.log(`
|
|
72
56
|
Usage: ccsetup [project-name] [options]
|
|
73
|
-
ccsetup scan [path] [options]
|
|
74
57
|
|
|
75
58
|
Commands:
|
|
76
|
-
ccsetup Interactive mode
|
|
59
|
+
ccsetup Interactive mode
|
|
77
60
|
ccsetup <name> Create a new Claude Code project
|
|
78
|
-
ccsetup scan Advanced repository scanning (see 'ccsetup scan --help')
|
|
79
61
|
|
|
80
62
|
Options:
|
|
81
|
-
--scan-only Skip project setup, only scan and create/update CLAUDE.md ⭐
|
|
82
63
|
--force, -f Skip all prompts and overwrite existing files
|
|
83
64
|
--dry-run, -d Show what would be done without making changes
|
|
84
65
|
--agents Interactive agent selection mode
|
|
@@ -86,32 +67,15 @@ Options:
|
|
|
86
67
|
--no-agents Skip agent selection entirely
|
|
87
68
|
--browse-agents Copy all agents to /agents folder for browsing
|
|
88
69
|
--browse Enhanced template browsing and selection interface
|
|
89
|
-
--scan-context Scan repository and add context to CLAUDE.md
|
|
90
70
|
--help, -h Show this help message
|
|
91
71
|
|
|
92
72
|
Advanced:
|
|
93
73
|
--install-hooks Install workflow selection hook to .claude/hooks (optional, power users only)
|
|
94
74
|
|
|
95
|
-
Quick Start:
|
|
96
|
-
npx ccsetup # Interactive mode - choose what to do
|
|
97
|
-
npx ccsetup --scan-only # Just scan and create CLAUDE.md ⭐
|
|
98
|
-
npx ccsetup my-project # Full project setup
|
|
99
|
-
|
|
100
|
-
Scan-Only Mode ⭐:
|
|
101
|
-
Perfect for existing projects! Analyzes your codebase and creates/updates
|
|
102
|
-
CLAUDE.md with project context without modifying your project structure.
|
|
103
|
-
|
|
104
75
|
Examples:
|
|
105
|
-
ccsetup
|
|
106
|
-
ccsetup
|
|
107
|
-
ccsetup
|
|
108
|
-
ccsetup --scan-only --dry-run # Preview what would happen
|
|
109
|
-
|
|
110
|
-
Full Setup Examples:
|
|
111
|
-
ccsetup # Interactive setup in current directory
|
|
112
|
-
ccsetup my-project # Create in new directory
|
|
113
|
-
ccsetup . --scan-context # Full setup with context scanning
|
|
114
|
-
ccsetup my-app --all-agents # Include all agents automatically
|
|
76
|
+
npx ccsetup # Interactive setup in current directory
|
|
77
|
+
npx ccsetup my-project # Create in new directory
|
|
78
|
+
npx ccsetup my-app --all-agents # Include all agents automatically
|
|
115
79
|
`);
|
|
116
80
|
process.exit(0);
|
|
117
81
|
}
|
|
@@ -132,202 +96,8 @@ function validateProjectName(name) {
|
|
|
132
96
|
return true;
|
|
133
97
|
}
|
|
134
98
|
|
|
135
|
-
async function scanRepositoryForContext(projectPath) {
|
|
136
|
-
try {
|
|
137
|
-
const progressReporter = new ProgressReporter();
|
|
138
|
-
console.log('🔍 Scanning repository for project context...');
|
|
139
|
-
|
|
140
|
-
const scanner = new RepositoryScanner(projectPath);
|
|
141
|
-
const scanResults = await scanner.scan(progressReporter);
|
|
142
|
-
|
|
143
|
-
const contextGenerator = new ContextGenerator(scanResults);
|
|
144
|
-
const context = contextGenerator.generate();
|
|
145
|
-
const structuredSections = contextGenerator.generateStructuredSections();
|
|
146
|
-
|
|
147
|
-
console.log('\n📊 Detected project details:');
|
|
148
|
-
if (context.overview) {
|
|
149
|
-
console.log(` ${context.overview}`);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (context.techStack && context.techStack.frameworks && context.techStack.frameworks.length > 0) {
|
|
153
|
-
console.log(` Framework: ${context.techStack.frameworks.join(', ')}`);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (context.commands && Object.keys(context.commands).length > 0) {
|
|
157
|
-
const totalCommands = Object.values(context.commands).reduce((sum, cmds) => sum + cmds.length, 0);
|
|
158
|
-
console.log(` Commands: ${totalCommands} available scripts`);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (context.patterns && Object.keys(context.patterns).length > 0) {
|
|
162
|
-
const totalPatterns = Object.values(context.patterns).reduce((sum, patterns) => sum + patterns.length, 0);
|
|
163
|
-
console.log(` Patterns: ${totalPatterns} detected architectural patterns`);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return {
|
|
167
|
-
scanResults,
|
|
168
|
-
contextGenerator,
|
|
169
|
-
structuredSections,
|
|
170
|
-
formattedContext: contextGenerator.formatForClaude()
|
|
171
|
-
};
|
|
172
|
-
} catch (error) {
|
|
173
|
-
console.warn(`⚠️ Repository scanning failed: ${error.message}`);
|
|
174
|
-
console.log(' Continuing with standard setup...\n');
|
|
175
|
-
return null;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
async function previewAndConfirmContext(formattedContext) {
|
|
180
|
-
console.log('\n📝 Generated context preview:');
|
|
181
|
-
console.log('━'.repeat(60));
|
|
182
|
-
console.log(formattedContext);
|
|
183
|
-
console.log('━'.repeat(60));
|
|
184
|
-
|
|
185
|
-
// Use inquirer instead of readline prompt
|
|
186
|
-
const selectModule = await import('@inquirer/select');
|
|
187
|
-
const select = selectModule.default;
|
|
188
|
-
const response = await select({
|
|
189
|
-
message: 'Would you like to add this context to CLAUDE.md?',
|
|
190
|
-
choices: [
|
|
191
|
-
{ name: 'Yes', value: 'y' },
|
|
192
|
-
{ name: 'No', value: 'n' },
|
|
193
|
-
{ name: 'Edit', value: 'edit' }
|
|
194
|
-
],
|
|
195
|
-
default: 'y'
|
|
196
|
-
});
|
|
197
|
-
return response;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
async function shouldScanRepository() {
|
|
201
|
-
console.log('\n🔍 Existing project files detected.');
|
|
202
|
-
// Use inquirer instead of readline prompt
|
|
203
|
-
const confirmModule = await import('@inquirer/confirm');
|
|
204
|
-
const confirm = confirmModule.default;
|
|
205
|
-
const response = await confirm({
|
|
206
|
-
message: 'Would you like to scan the repository to add project context to CLAUDE.md?',
|
|
207
|
-
default: true
|
|
208
|
-
});
|
|
209
|
-
return response;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
async function mergeContextIntelligently(existingContent, repositoryContext, strategy = 'smart') {
|
|
213
|
-
try {
|
|
214
|
-
// Handle replace strategy
|
|
215
|
-
if (strategy === 'replace') {
|
|
216
|
-
console.log(' Replacing with new scan results...');
|
|
217
|
-
// Get template and apply context
|
|
218
|
-
const templatePath = path.join(__dirname, '..', 'template', 'CLAUDE.md');
|
|
219
|
-
let template = '';
|
|
220
|
-
if (fs.existsSync(templatePath)) {
|
|
221
|
-
template = fs.readFileSync(templatePath, 'utf8');
|
|
222
|
-
} else {
|
|
223
|
-
template = `# Claude Code Project Instructions
|
|
224
|
-
|
|
225
|
-
## Project Overview
|
|
226
|
-
[Project description will be added here]
|
|
227
|
-
|
|
228
|
-
## Key Objectives
|
|
229
|
-
[Project objectives will be added here]
|
|
230
|
-
|
|
231
|
-
## Additional Notes
|
|
232
|
-
[Any other important information for Claude to know about this project]
|
|
233
|
-
`;
|
|
234
|
-
}
|
|
235
|
-
return await applyContextToTemplate(template, repositoryContext, 'append');
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Use structured sections for intelligent merge, fall back to formatted context
|
|
239
|
-
let contextToMerge = repositoryContext.formattedContext;
|
|
240
|
-
|
|
241
|
-
if (repositoryContext.structuredSections &&
|
|
242
|
-
typeof repositoryContext.structuredSections === 'object' &&
|
|
243
|
-
Object.keys(repositoryContext.structuredSections).length > 0) {
|
|
244
|
-
contextToMerge = repositoryContext.structuredSections;
|
|
245
|
-
console.log(' 📊 Using intelligent merge with structured sections');
|
|
246
|
-
} else {
|
|
247
|
-
console.log(' 📝 Using fallback merge with formatted content');
|
|
248
|
-
}
|
|
249
|
-
const merger = new ContextMerger(existingContent, contextToMerge);
|
|
250
|
-
const changes = merger.getChangesSummary();
|
|
251
|
-
|
|
252
|
-
if (!changes.hasChanges) {
|
|
253
|
-
console.log(' ✅ Context is already up to date');
|
|
254
|
-
return existingContent;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
console.log('\n 📊 Merge Analysis:');
|
|
258
|
-
if (changes.added > 0) console.log(` + ${changes.added} new sections`);
|
|
259
|
-
if (changes.modified > 0) console.log(` ~ ${changes.modified} updated sections`);
|
|
260
|
-
if (changes.unchanged > 0) console.log(` ✓ ${changes.unchanged} preserved sections`);
|
|
261
|
-
|
|
262
|
-
const mergedContent = await merger.merge(strategy);
|
|
263
|
-
|
|
264
|
-
if (typeof mergedContent === 'string') {
|
|
265
|
-
const existingHeader = merger.extractHeaderContent();
|
|
266
|
-
return existingHeader + '\n\n' + mergedContent;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return mergedContent;
|
|
270
|
-
} catch (error) {
|
|
271
|
-
// Re-throw specific errors that should stop the process
|
|
272
|
-
if (error.message === 'Interactive merge cancelled' ||
|
|
273
|
-
error.message === 'Missing required dependency') {
|
|
274
|
-
throw error;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
console.warn(` ⚠️ Merge failed: ${error.message}`);
|
|
278
|
-
console.log(' 📝 Falling back to simple append...');
|
|
279
|
-
|
|
280
|
-
// Fall back to formatted context for simple append
|
|
281
|
-
const newContext = repositoryContext.formattedContext;
|
|
282
|
-
const additionalNotesMarker = '## Additional Notes';
|
|
283
|
-
const additionalNotesIdx = existingContent.indexOf(additionalNotesMarker);
|
|
284
|
-
|
|
285
|
-
if (additionalNotesIdx === -1) {
|
|
286
|
-
return existingContent.trimEnd() + '\n\n' + additionalNotesMarker + '\n\n' + newContext.trim() + '\n';
|
|
287
|
-
} else {
|
|
288
|
-
const insertIdx = existingContent.indexOf('\n', additionalNotesIdx) + 1;
|
|
289
|
-
return existingContent.substring(0, insertIdx) + '\n' + newContext.trim() + '\n' + existingContent.substring(insertIdx);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
async function applyContextToTemplate(templateContent, repositoryContext, conflictStrategy) {
|
|
295
|
-
const contextContent = repositoryContext.formattedContext;
|
|
296
|
-
const additionalNotesMarker = '## Additional Notes';
|
|
297
|
-
const placeholderContent = '[Any other important information for Claude to know about this project]';
|
|
298
|
-
|
|
299
|
-
if (templateContent.includes(additionalNotesMarker)) {
|
|
300
|
-
if (templateContent.includes(placeholderContent)) {
|
|
301
|
-
return templateContent.replace(placeholderContent, contextContent.trim());
|
|
302
|
-
} else {
|
|
303
|
-
const sections = templateContent.split(additionalNotesMarker);
|
|
304
|
-
if (sections.length >= 2) {
|
|
305
|
-
return sections[0] + additionalNotesMarker + contextContent + '\n';
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
return templateContent + contextContent;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
99
|
// Validate conflicting flags
|
|
314
100
|
function validateFlags() {
|
|
315
|
-
// Scan-only conflicts
|
|
316
|
-
if (flags.scanOnly) {
|
|
317
|
-
const conflictingFlags = [];
|
|
318
|
-
if (flags.allAgents) conflictingFlags.push('--all-agents');
|
|
319
|
-
if (flags.noAgents) conflictingFlags.push('--no-agents');
|
|
320
|
-
if (flags.browseAgents) conflictingFlags.push('--browse-agents');
|
|
321
|
-
if (flags.browse) conflictingFlags.push('--browse');
|
|
322
|
-
if (flags.agents) conflictingFlags.push('--agents');
|
|
323
|
-
|
|
324
|
-
if (conflictingFlags.length > 0) {
|
|
325
|
-
console.error(`Error: --scan-only cannot be used with ${conflictingFlags.join(', ')}`);
|
|
326
|
-
console.log('Tip: --scan-only skips all agent-related operations');
|
|
327
|
-
process.exit(1);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
101
|
// Existing validations
|
|
332
102
|
if (flags.allAgents && flags.noAgents) {
|
|
333
103
|
console.error('Error: Cannot use --all-agents and --no-agents together');
|
|
@@ -359,6 +129,7 @@ if (projectName !== '.') {
|
|
|
359
129
|
|
|
360
130
|
const targetDir = path.resolve(process.cwd(), projectName);
|
|
361
131
|
const templateDir = path.join(__dirname, '..', 'template');
|
|
132
|
+
const VALID_AI_PROVIDERS = new Set(['claude', 'codex', 'both']);
|
|
362
133
|
|
|
363
134
|
// Create readline interface only if needed
|
|
364
135
|
let rl = null;
|
|
@@ -409,6 +180,72 @@ function validateAgentFile(file) {
|
|
|
409
180
|
return true;
|
|
410
181
|
}
|
|
411
182
|
|
|
183
|
+
function includesClaude(provider) {
|
|
184
|
+
return provider === 'claude' || provider === 'both';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function includesCodex(provider) {
|
|
188
|
+
return provider === 'codex' || provider === 'both';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function getEnvBoolean(name) {
|
|
192
|
+
const value = process.env[name];
|
|
193
|
+
if (value == null || value === '') return null;
|
|
194
|
+
|
|
195
|
+
const normalized = value.toLowerCase().trim();
|
|
196
|
+
if (['1', 'true', 'yes', 'y'].includes(normalized)) return true;
|
|
197
|
+
if (['0', 'false', 'no', 'n'].includes(normalized)) return false;
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function getConfiguredAiProvider() {
|
|
202
|
+
const envValue = process.env.CCSETUP_AI_PROVIDER;
|
|
203
|
+
if (!envValue) return null;
|
|
204
|
+
|
|
205
|
+
const normalized = envValue.toLowerCase().trim();
|
|
206
|
+
if (!VALID_AI_PROVIDERS.has(normalized)) {
|
|
207
|
+
throw new Error(`Invalid CCSETUP_AI_PROVIDER value: ${envValue}`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return normalized;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function selectAiProvider() {
|
|
214
|
+
const envProvider = getConfiguredAiProvider();
|
|
215
|
+
if (envProvider) {
|
|
216
|
+
return envProvider;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (flags.force || flags.dryRun) {
|
|
220
|
+
return 'claude';
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const selectModule = await import('@inquirer/select');
|
|
224
|
+
const select = selectModule.default;
|
|
225
|
+
|
|
226
|
+
return select({
|
|
227
|
+
message: 'Which AI setup would you like to generate for this project?',
|
|
228
|
+
choices: [
|
|
229
|
+
{
|
|
230
|
+
name: 'Claude Code',
|
|
231
|
+
value: 'claude',
|
|
232
|
+
description: 'Copy CLAUDE.md and the .claude project setup'
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: 'Codex CLI',
|
|
236
|
+
value: 'codex',
|
|
237
|
+
description: 'Copy AGENTS.md plus project-local Codex skills'
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
name: 'Both',
|
|
241
|
+
value: 'both',
|
|
242
|
+
description: 'Copy both Claude Code and Codex project assets'
|
|
243
|
+
}
|
|
244
|
+
],
|
|
245
|
+
default: 'claude'
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
412
249
|
// Function to check if Claude Code is installed
|
|
413
250
|
function checkClaudeCode() {
|
|
414
251
|
const claudeDir = path.join(targetDir, '.claude');
|
|
@@ -604,6 +441,36 @@ async function initializeClaudeDirectory(selectedAgentFiles, conflictStrategy, d
|
|
|
604
441
|
}
|
|
605
442
|
}
|
|
606
443
|
|
|
444
|
+
// Copy template/hooks/ to .claude/hooks/ (all hook directories)
|
|
445
|
+
const templateHooksDir = path.join(templateDir, 'hooks');
|
|
446
|
+
if (fs.existsSync(templateHooksDir)) {
|
|
447
|
+
const claudeHooksDir = path.join(claudeDir, 'hooks');
|
|
448
|
+
const hookDirs = fs.readdirSync(templateHooksDir).filter(d => {
|
|
449
|
+
return fs.statSync(path.join(templateHooksDir, d)).isDirectory();
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
for (const hookName of hookDirs) {
|
|
453
|
+
const hookSrcDir = path.join(templateHooksDir, hookName);
|
|
454
|
+
const hookDestDir = path.join(claudeHooksDir, hookName);
|
|
455
|
+
const hookFile = path.join(hookSrcDir, 'index.js');
|
|
456
|
+
|
|
457
|
+
if (fs.existsSync(hookFile)) {
|
|
458
|
+
if (!fs.existsSync(path.join(hookDestDir, 'index.js'))) {
|
|
459
|
+
if (!dryRun) {
|
|
460
|
+
fs.mkdirSync(hookDestDir, { recursive: true });
|
|
461
|
+
fs.copyFileSync(hookFile, path.join(hookDestDir, 'index.js'));
|
|
462
|
+
}
|
|
463
|
+
createdItems.push(`.claude/hooks/${hookName}/index.js`);
|
|
464
|
+
if (dryRun) {
|
|
465
|
+
console.log(` ✨ Would copy: .claude/hooks/${hookName}/index.js`);
|
|
466
|
+
}
|
|
467
|
+
} else {
|
|
468
|
+
skippedItems.push(`.claude/hooks/${hookName}/index.js`);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
607
474
|
// Copy selected agents to .claude/agents
|
|
608
475
|
const templateAgentsDir = path.join(templateDir, '.claude', 'agents');
|
|
609
476
|
let copiedAgents = 0;
|
|
@@ -692,6 +559,126 @@ async function initializeClaudeDirectory(selectedAgentFiles, conflictStrategy, d
|
|
|
692
559
|
}
|
|
693
560
|
}
|
|
694
561
|
|
|
562
|
+
async function initializeCodexDirectory(conflictStrategy, dryRun) {
|
|
563
|
+
const codexDir = path.join(targetDir, '.codex');
|
|
564
|
+
const skillsDir = path.join(codexDir, 'skills');
|
|
565
|
+
const templateCodexDir = path.join(templateDir, '.codex');
|
|
566
|
+
const createdItems = [];
|
|
567
|
+
const skippedItems = [];
|
|
568
|
+
|
|
569
|
+
function copyCodexFile(src, dest, relativePath) {
|
|
570
|
+
if (!fs.existsSync(src)) {
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (!fs.existsSync(dest)) {
|
|
575
|
+
if (!dryRun) {
|
|
576
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
577
|
+
fs.copyFileSync(src, dest);
|
|
578
|
+
}
|
|
579
|
+
createdItems.push(relativePath);
|
|
580
|
+
if (dryRun) {
|
|
581
|
+
console.log(` ✨ Would copy: ${relativePath}`);
|
|
582
|
+
}
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (conflictStrategy === 'overwrite') {
|
|
587
|
+
if (!dryRun) {
|
|
588
|
+
fs.copyFileSync(src, dest);
|
|
589
|
+
}
|
|
590
|
+
if (dryRun) {
|
|
591
|
+
console.log(` ♻️ Would replace: ${relativePath}`);
|
|
592
|
+
}
|
|
593
|
+
createdItems.push(relativePath);
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
if (conflictStrategy === 'rename') {
|
|
598
|
+
const ext = path.extname(dest);
|
|
599
|
+
const baseName = path.basename(dest, ext);
|
|
600
|
+
const dirName = path.dirname(dest);
|
|
601
|
+
let newDest = path.join(dirName, `${baseName}-ccsetup${ext}`);
|
|
602
|
+
let counter = 1;
|
|
603
|
+
while (fs.existsSync(newDest)) {
|
|
604
|
+
newDest = path.join(dirName, `${baseName}-ccsetup-${counter}${ext}`);
|
|
605
|
+
counter++;
|
|
606
|
+
}
|
|
607
|
+
if (!dryRun) {
|
|
608
|
+
fs.copyFileSync(src, newDest);
|
|
609
|
+
}
|
|
610
|
+
createdItems.push(path.relative(targetDir, newDest));
|
|
611
|
+
if (dryRun) {
|
|
612
|
+
console.log(` 📄 Would create: ${path.relative(targetDir, newDest)}`);
|
|
613
|
+
}
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
skippedItems.push(relativePath);
|
|
618
|
+
if (dryRun) {
|
|
619
|
+
console.log(` ⏭️ Would skip: ${relativePath}`);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
try {
|
|
624
|
+
if (!fs.existsSync(codexDir)) {
|
|
625
|
+
if (!dryRun) {
|
|
626
|
+
fs.mkdirSync(codexDir, { recursive: true });
|
|
627
|
+
}
|
|
628
|
+
createdItems.push('.codex/');
|
|
629
|
+
if (dryRun) {
|
|
630
|
+
console.log(' 📁 Would create directory: .codex/');
|
|
631
|
+
}
|
|
632
|
+
} else {
|
|
633
|
+
skippedItems.push('.codex/');
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if (!fs.existsSync(skillsDir)) {
|
|
637
|
+
if (!dryRun) {
|
|
638
|
+
fs.mkdirSync(skillsDir, { recursive: true });
|
|
639
|
+
}
|
|
640
|
+
createdItems.push('.codex/skills/');
|
|
641
|
+
if (dryRun) {
|
|
642
|
+
console.log(' 📁 Would create directory: .codex/skills/');
|
|
643
|
+
}
|
|
644
|
+
} else {
|
|
645
|
+
skippedItems.push('.codex/skills/');
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
const templateSkillsDir = path.join(templateCodexDir, 'skills');
|
|
649
|
+
if (fs.existsSync(templateSkillsDir)) {
|
|
650
|
+
const skillDirs = fs.readdirSync(templateSkillsDir, { withFileTypes: true }).filter(entry => entry.isDirectory());
|
|
651
|
+
for (const entry of skillDirs) {
|
|
652
|
+
const srcDir = path.join(templateSkillsDir, entry.name);
|
|
653
|
+
const skillFileSrc = path.join(srcDir, 'SKILL.md');
|
|
654
|
+
|
|
655
|
+
if (!fs.existsSync(skillFileSrc)) {
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
const destDir = path.join(skillsDir, entry.name);
|
|
660
|
+
const skillFileDest = path.join(destDir, 'SKILL.md');
|
|
661
|
+
|
|
662
|
+
if (!fs.existsSync(destDir)) {
|
|
663
|
+
if (!dryRun) {
|
|
664
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
665
|
+
}
|
|
666
|
+
createdItems.push(`.codex/skills/${entry.name}/`);
|
|
667
|
+
if (dryRun) {
|
|
668
|
+
console.log(` 📁 Would create directory: .codex/skills/${entry.name}/`);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
copyCodexFile(skillFileSrc, skillFileDest, `.codex/skills/${entry.name}/SKILL.md`);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
return { createdItems, skippedItems };
|
|
677
|
+
} catch (error) {
|
|
678
|
+
throw new Error(`Failed to initialize .codex directory: ${error.message}`);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
695
682
|
// Function to parse agent frontmatter
|
|
696
683
|
function parseAgentFrontmatter(filePath) {
|
|
697
684
|
try {
|
|
@@ -1014,441 +1001,11 @@ async function installClaudeHooks() {
|
|
|
1014
1001
|
|
|
1015
1002
|
// Setup mode selection function
|
|
1016
1003
|
async function selectSetupMode() {
|
|
1017
|
-
// Direct flag handling
|
|
1018
1004
|
if (flags.installHooks) return 'install-hooks';
|
|
1019
|
-
if (flags.scanOnly) return 'scan-only';
|
|
1020
1005
|
if (flags.agents) return 'agents-only';
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
// Skip selection if already in a specific mode
|
|
1024
|
-
if (projectName !== '.' || flags.scanContext) return 'full';
|
|
1025
|
-
|
|
1026
|
-
// Interactive selection for bare 'npx ccsetup'
|
|
1027
|
-
console.log('Welcome to ccsetup! 🎉\n');
|
|
1028
|
-
console.log('This tool helps you set up and maintain your Claude Code project.');
|
|
1029
|
-
console.log('Let\'s analyze your repository and create the perfect CLAUDE.md file.\n');
|
|
1030
|
-
|
|
1031
|
-
// Check if CLAUDE.md exists to provide context
|
|
1032
|
-
const claudeMdExists = fs.existsSync(path.join(process.cwd(), 'CLAUDE.md'));
|
|
1033
|
-
if (claudeMdExists) {
|
|
1034
|
-
console.log('📋 Existing CLAUDE.md detected in this directory.\n');
|
|
1035
|
-
} else {
|
|
1036
|
-
console.log('📄 No CLAUDE.md found. Let\'s create one!\n');
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
// Dynamic import for ESM module
|
|
1040
|
-
const selectModule = await import('@inquirer/select');
|
|
1041
|
-
const select = selectModule.default;
|
|
1042
|
-
|
|
1043
|
-
const mode = await select({
|
|
1044
|
-
message: claudeMdExists ?
|
|
1045
|
-
'CLAUDE.md exists. How would you like to proceed?' :
|
|
1046
|
-
'What would you like to do?',
|
|
1047
|
-
choices: claudeMdExists ? [
|
|
1048
|
-
{
|
|
1049
|
-
name: 'Smart Merge - Keep my content, add new findings',
|
|
1050
|
-
value: 'scan-smart',
|
|
1051
|
-
description: 'Preserves your customizations while adding newly detected information'
|
|
1052
|
-
},
|
|
1053
|
-
{
|
|
1054
|
-
name: 'Replace - Fresh scan, replace existing',
|
|
1055
|
-
value: 'scan-replace',
|
|
1056
|
-
description: 'Creates a brand new CLAUDE.md from your current codebase'
|
|
1057
|
-
},
|
|
1058
|
-
{
|
|
1059
|
-
name: 'Full Setup - Add agents and project structure',
|
|
1060
|
-
value: 'full',
|
|
1061
|
-
description: 'Creates the complete Claude Code boilerplate structure with agents, docs, tickets, and plans'
|
|
1062
|
-
}
|
|
1063
|
-
] : [
|
|
1064
|
-
{
|
|
1065
|
-
name: '🚀 Quick Start - Just create CLAUDE.md',
|
|
1066
|
-
value: 'scan-smart',
|
|
1067
|
-
description: 'Scans your code and creates CLAUDE.md with project context'
|
|
1068
|
-
},
|
|
1069
|
-
{
|
|
1070
|
-
name: '🏗️ Full Setup - Complete Claude Code structure',
|
|
1071
|
-
value: 'full',
|
|
1072
|
-
description: 'Creates CLAUDE.md plus agents, docs, tickets, and plans'
|
|
1073
|
-
}
|
|
1074
|
-
]
|
|
1075
|
-
});
|
|
1076
|
-
|
|
1077
|
-
return mode;
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
// Validate scan-only environment
|
|
1081
|
-
async function validateScanOnlyEnvironment() {
|
|
1082
|
-
// Check directory exists and is accessible
|
|
1083
|
-
try {
|
|
1084
|
-
await fs.promises.access(targetDir, fs.constants.R_OK | fs.constants.W_OK);
|
|
1085
|
-
} catch (error) {
|
|
1086
|
-
let message = 'Cannot access target directory';
|
|
1087
|
-
let suggestion = 'Check directory permissions';
|
|
1088
|
-
|
|
1089
|
-
if (error.code === 'ENOENT') {
|
|
1090
|
-
message = 'Target directory does not exist';
|
|
1091
|
-
suggestion = 'Create the directory first or check the path';
|
|
1092
|
-
} else if (error.code === 'EACCES') {
|
|
1093
|
-
message = 'Permission denied accessing target directory';
|
|
1094
|
-
suggestion = 'Run with appropriate permissions or use sudo';
|
|
1095
|
-
} else if (error.code === 'ENOTDIR') {
|
|
1096
|
-
message = 'Target path is not a directory';
|
|
1097
|
-
suggestion = 'Specify a valid directory path';
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
return {
|
|
1101
|
-
valid: false,
|
|
1102
|
-
message,
|
|
1103
|
-
suggestion
|
|
1104
|
-
};
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
// Check if CLAUDE.md exists and is writable
|
|
1108
|
-
const claudeMdPath = path.join(targetDir, 'CLAUDE.md');
|
|
1109
|
-
if (fs.existsSync(claudeMdPath)) {
|
|
1110
|
-
try {
|
|
1111
|
-
await fs.promises.access(claudeMdPath, fs.constants.R_OK | fs.constants.W_OK);
|
|
1112
|
-
} catch (error) {
|
|
1113
|
-
return {
|
|
1114
|
-
valid: false,
|
|
1115
|
-
message: 'Cannot access existing CLAUDE.md file',
|
|
1116
|
-
suggestion: 'Check file permissions or run with appropriate access rights'
|
|
1117
|
-
};
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
// Check if directory is empty (warning only)
|
|
1122
|
-
const files = await fs.promises.readdir(targetDir);
|
|
1123
|
-
const hasFiles = files.some(f => !f.startsWith('.') && f !== 'node_modules');
|
|
1124
|
-
|
|
1125
|
-
if (!hasFiles && projectName === '.') {
|
|
1126
|
-
console.log('⚠️ Warning: Current directory appears to be empty.');
|
|
1127
|
-
console.log(' Scan results may be limited.\n');
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
return { valid: true };
|
|
1006
|
+
return 'full';
|
|
1131
1007
|
}
|
|
1132
1008
|
|
|
1133
|
-
// Get CLAUDE.md template
|
|
1134
|
-
async function getClaudeMdTemplate() {
|
|
1135
|
-
const templatePath = path.join(__dirname, '..', 'template', 'CLAUDE.md');
|
|
1136
|
-
try {
|
|
1137
|
-
return await fs.promises.readFile(templatePath, 'utf8');
|
|
1138
|
-
} catch (error) {
|
|
1139
|
-
// Fallback to minimal template
|
|
1140
|
-
return `# Claude Code Project Instructions
|
|
1141
|
-
|
|
1142
|
-
## Project Overview
|
|
1143
|
-
[Project description will be added here]
|
|
1144
|
-
|
|
1145
|
-
## Key Objectives
|
|
1146
|
-
[Project objectives will be added here]
|
|
1147
|
-
|
|
1148
|
-
## Additional Notes
|
|
1149
|
-
[Any other important information for Claude to know about this project]
|
|
1150
|
-
`;
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
// Scan-only mode implementation
|
|
1155
|
-
async function scanOnlyMode(defaultMergeStrategy = 'smart') {
|
|
1156
|
-
console.log('\n🔍 Repository Scan Mode\n');
|
|
1157
|
-
|
|
1158
|
-
// Provide context based on merge strategy
|
|
1159
|
-
switch (defaultMergeStrategy) {
|
|
1160
|
-
case 'smart':
|
|
1161
|
-
console.log('Smart Merge Mode');
|
|
1162
|
-
console.log('Intelligently merges new findings with your existing CLAUDE.md');
|
|
1163
|
-
console.log('Preserves your customizations while adding newly detected information\n');
|
|
1164
|
-
break;
|
|
1165
|
-
case 'replace':
|
|
1166
|
-
console.log('Replace Mode');
|
|
1167
|
-
console.log('Creates a brand new CLAUDE.md from your current codebase');
|
|
1168
|
-
console.log('Perfect for: Starting fresh or major project restructuring\n');
|
|
1169
|
-
break;
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
console.log('This will analyze your repository and update CLAUDE.md with:');
|
|
1173
|
-
console.log(' • Project structure and organization');
|
|
1174
|
-
console.log(' • Technology stack and dependencies');
|
|
1175
|
-
console.log(' • Available commands and scripts');
|
|
1176
|
-
console.log(' • Architectural patterns detected');
|
|
1177
|
-
console.log(' • Important context for Claude\n');
|
|
1178
|
-
|
|
1179
|
-
// Validate environment
|
|
1180
|
-
const validation = await validateScanOnlyEnvironment();
|
|
1181
|
-
if (!validation.valid) {
|
|
1182
|
-
console.error(`❌ ${validation.message}`);
|
|
1183
|
-
if (validation.suggestion) {
|
|
1184
|
-
console.log(`💡 ${validation.suggestion}`);
|
|
1185
|
-
}
|
|
1186
|
-
return;
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
// Confirmation prompt (unless --force)
|
|
1190
|
-
if (!flags.force && !flags.dryRun) {
|
|
1191
|
-
// Create readline interface if needed
|
|
1192
|
-
if (!rl) {
|
|
1193
|
-
rl = readline.createInterface({
|
|
1194
|
-
input: process.stdin,
|
|
1195
|
-
output: process.stdout
|
|
1196
|
-
});
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
// Use inquirer instead of readline prompt
|
|
1200
|
-
const confirmModule = await import('@inquirer/confirm');
|
|
1201
|
-
const confirm = confirmModule.default;
|
|
1202
|
-
const shouldContinue = await confirm({
|
|
1203
|
-
message: 'Continue?',
|
|
1204
|
-
default: true
|
|
1205
|
-
});
|
|
1206
|
-
if (!shouldContinue) {
|
|
1207
|
-
console.log('Setup cancelled.');
|
|
1208
|
-
if (rl) rl.close();
|
|
1209
|
-
return;
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
// Check if directory has meaningful files
|
|
1214
|
-
const files = await fs.promises.readdir(targetDir);
|
|
1215
|
-
const hasFiles = files.some(f => !f.startsWith('.') && f !== 'node_modules');
|
|
1216
|
-
|
|
1217
|
-
// Perform repository scan
|
|
1218
|
-
let repositoryContext = null;
|
|
1219
|
-
try {
|
|
1220
|
-
repositoryContext = await scanRepositoryForContext(targetDir);
|
|
1221
|
-
} catch (error) {
|
|
1222
|
-
console.error('❌ Repository scanning failed:', error.message);
|
|
1223
|
-
if (rl) rl.close();
|
|
1224
|
-
return;
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
if (!repositoryContext) {
|
|
1228
|
-
// Handle empty directory case
|
|
1229
|
-
if (!hasFiles && projectName === '.') {
|
|
1230
|
-
console.log('📝 Since the directory is empty, would you like to:');
|
|
1231
|
-
console.log('1) Create a minimal CLAUDE.md with basic template');
|
|
1232
|
-
console.log('2) Cancel and run full setup instead');
|
|
1233
|
-
|
|
1234
|
-
// Use inquirer instead of readline prompt
|
|
1235
|
-
const selectModule = await import('@inquirer/select');
|
|
1236
|
-
const select = selectModule.default;
|
|
1237
|
-
const choice = await select({
|
|
1238
|
-
message: 'Choose an option:',
|
|
1239
|
-
choices: [
|
|
1240
|
-
{ name: '1) Continue with minimal context', value: '1' },
|
|
1241
|
-
{ name: '2) Cancel and run full setup instead', value: '2' }
|
|
1242
|
-
]
|
|
1243
|
-
});
|
|
1244
|
-
|
|
1245
|
-
if (choice === '2') {
|
|
1246
|
-
console.log('\n💡 Run `npx ccsetup` without --scan-only for full project setup.');
|
|
1247
|
-
if (rl) rl.close();
|
|
1248
|
-
return;
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
// Continue with minimal context
|
|
1252
|
-
repositoryContext = {
|
|
1253
|
-
formattedContext: `
|
|
1254
|
-
## Additional Notes
|
|
1255
|
-
|
|
1256
|
-
This project directory was empty when scanned.
|
|
1257
|
-
Please update this file with relevant project information as you develop.
|
|
1258
|
-
|
|
1259
|
-
### Getting Started
|
|
1260
|
-
- Add project description above
|
|
1261
|
-
- Define key objectives
|
|
1262
|
-
- Document important conventions
|
|
1263
|
-
- Update as the project evolves
|
|
1264
|
-
`
|
|
1265
|
-
};
|
|
1266
|
-
} else {
|
|
1267
|
-
console.log('❌ Unable to generate context from repository.');
|
|
1268
|
-
console.log('💡 Tip: Make sure you\'re in a valid project directory.');
|
|
1269
|
-
if (rl) rl.close();
|
|
1270
|
-
return;
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
// Preview and confirm context
|
|
1275
|
-
if (!flags.force && !flags.dryRun) {
|
|
1276
|
-
const confirmResult = await previewAndConfirmContext(repositoryContext.formattedContext);
|
|
1277
|
-
if (confirmResult === 'n' || confirmResult === 'no') {
|
|
1278
|
-
console.log('✅ Setup cancelled.');
|
|
1279
|
-
if (rl) rl.close();
|
|
1280
|
-
return;
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
// Handle CLAUDE.md creation/update
|
|
1285
|
-
const claudeMdPath = path.join(targetDir, 'CLAUDE.md');
|
|
1286
|
-
const exists = fs.existsSync(claudeMdPath);
|
|
1287
|
-
let backupPath = null;
|
|
1288
|
-
let mergeStrategy = 'smart'; // Default merge strategy
|
|
1289
|
-
|
|
1290
|
-
try {
|
|
1291
|
-
if (exists) {
|
|
1292
|
-
// Update existing CLAUDE.md
|
|
1293
|
-
if (!flags.force && !flags.dryRun) {
|
|
1294
|
-
console.log('\n📋 Existing CLAUDE.md detected!');
|
|
1295
|
-
|
|
1296
|
-
// Use the default merge strategy if provided
|
|
1297
|
-
if (defaultMergeStrategy === 'replace') {
|
|
1298
|
-
console.log('This will replace your existing CLAUDE.md with a fresh scan.');
|
|
1299
|
-
console.log(' Your current content will be backed up first.');
|
|
1300
|
-
const confirmModule = await import('@inquirer/confirm');
|
|
1301
|
-
const confirm = confirmModule.default;
|
|
1302
|
-
const shouldProceed = await confirm({
|
|
1303
|
-
message: 'Proceed with replacement?',
|
|
1304
|
-
default: false
|
|
1305
|
-
});
|
|
1306
|
-
if (!shouldProceed) {
|
|
1307
|
-
console.log('Cancelled.');
|
|
1308
|
-
if (rl) rl.close();
|
|
1309
|
-
return;
|
|
1310
|
-
}
|
|
1311
|
-
mergeStrategy = 'replace';
|
|
1312
|
-
} else {
|
|
1313
|
-
console.log('The scan will:');
|
|
1314
|
-
console.log(' • Preserve all your existing content');
|
|
1315
|
-
console.log(' • Add new findings from the scan');
|
|
1316
|
-
console.log(' • Create an automatic backup');
|
|
1317
|
-
|
|
1318
|
-
const confirmModule = await import('@inquirer/confirm');
|
|
1319
|
-
const confirm = confirmModule.default;
|
|
1320
|
-
const shouldContinue = await confirm({
|
|
1321
|
-
message: 'Continue with smart merge?',
|
|
1322
|
-
default: true
|
|
1323
|
-
});
|
|
1324
|
-
if (!shouldContinue) {
|
|
1325
|
-
console.log('Update cancelled.');
|
|
1326
|
-
if (rl) rl.close();
|
|
1327
|
-
return;
|
|
1328
|
-
}
|
|
1329
|
-
mergeStrategy = 'smart';
|
|
1330
|
-
}
|
|
1331
|
-
} else {
|
|
1332
|
-
mergeStrategy = defaultMergeStrategy;
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
console.log('\n📄 Updating existing CLAUDE.md...');
|
|
1336
|
-
|
|
1337
|
-
if (!flags.dryRun) {
|
|
1338
|
-
// Check file size before processing
|
|
1339
|
-
const stats = fs.statSync(claudeMdPath);
|
|
1340
|
-
if (stats.size > 1024 * 1024) { // 1MB
|
|
1341
|
-
console.warn('⚠️ Warning: CLAUDE.md is large (>1MB). Processing may take longer.');
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
let existingContent;
|
|
1345
|
-
try {
|
|
1346
|
-
existingContent = fs.readFileSync(claudeMdPath, 'utf8');
|
|
1347
|
-
} catch (error) {
|
|
1348
|
-
throw new Error(`Failed to read existing CLAUDE.md: ${error.message}`);
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
// Validate content
|
|
1352
|
-
if (!existingContent) {
|
|
1353
|
-
console.warn('⚠️ Warning: Existing CLAUDE.md appears to be empty.');
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
|
-
// Create backup first
|
|
1357
|
-
backupPath = `${claudeMdPath}.backup.${Date.now()}`;
|
|
1358
|
-
try {
|
|
1359
|
-
fs.writeFileSync(backupPath, existingContent);
|
|
1360
|
-
console.log(`📦 Backup created: ${path.basename(backupPath)}`);
|
|
1361
|
-
} catch (error) {
|
|
1362
|
-
throw new Error(`Failed to create backup: ${error.message}`);
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1365
|
-
// Use intelligent merge with backup
|
|
1366
|
-
let updatedContent;
|
|
1367
|
-
try {
|
|
1368
|
-
updatedContent = await mergeContextIntelligently(existingContent, repositoryContext, mergeStrategy);
|
|
1369
|
-
} catch (mergeError) {
|
|
1370
|
-
if (mergeError.message === 'Interactive merge cancelled') {
|
|
1371
|
-
console.log('💾 Your original CLAUDE.md is unchanged.');
|
|
1372
|
-
console.log(`📦 Scan results backup: ${path.basename(backupPath)}`);
|
|
1373
|
-
if (rl) rl.close();
|
|
1374
|
-
return;
|
|
1375
|
-
} else if (mergeError.message === 'Missing required dependency') {
|
|
1376
|
-
console.error('Please install missing dependencies and try again.');
|
|
1377
|
-
if (rl) rl.close();
|
|
1378
|
-
return;
|
|
1379
|
-
}
|
|
1380
|
-
throw mergeError;
|
|
1381
|
-
}
|
|
1382
|
-
|
|
1383
|
-
try {
|
|
1384
|
-
fs.writeFileSync(claudeMdPath, updatedContent, 'utf8');
|
|
1385
|
-
} catch (error) {
|
|
1386
|
-
// Restore from backup if write fails
|
|
1387
|
-
try {
|
|
1388
|
-
fs.writeFileSync(claudeMdPath, existingContent, 'utf8');
|
|
1389
|
-
console.error('❌ Failed to update CLAUDE.md. Original content restored.');
|
|
1390
|
-
} catch (restoreError) {
|
|
1391
|
-
console.error('❌ Critical: Failed to update AND restore CLAUDE.md!');
|
|
1392
|
-
console.error(`💾 Your content is safe in: ${path.basename(backupPath)}`);
|
|
1393
|
-
}
|
|
1394
|
-
throw new Error(`Failed to write updated content: ${error.message}`);
|
|
1395
|
-
}
|
|
1396
|
-
|
|
1397
|
-
console.log('✅ CLAUDE.md updated successfully!\n');
|
|
1398
|
-
|
|
1399
|
-
// Provide feedback based on merge strategy used
|
|
1400
|
-
if (mergeStrategy === 'smart') {
|
|
1401
|
-
console.log(' Smart merge completed');
|
|
1402
|
-
console.log(' - Your customizations preserved');
|
|
1403
|
-
console.log(' - New findings integrated');
|
|
1404
|
-
} else if (mergeStrategy === 'replace') {
|
|
1405
|
-
console.log(' Replace completed');
|
|
1406
|
-
console.log(' - New CLAUDE.md generated from current codebase');
|
|
1407
|
-
}
|
|
1408
|
-
|
|
1409
|
-
console.log(` 📦 Backup saved: ${path.basename(backupPath)}`);
|
|
1410
|
-
console.log(' 💡 Review the updated content and delete backup when satisfied');
|
|
1411
|
-
} else {
|
|
1412
|
-
console.log(' Would update: CLAUDE.md (dry-run mode)');
|
|
1413
|
-
}
|
|
1414
|
-
} else {
|
|
1415
|
-
// Create new CLAUDE.md
|
|
1416
|
-
console.log('\n📄 Creating CLAUDE.md...');
|
|
1417
|
-
|
|
1418
|
-
if (!flags.dryRun) {
|
|
1419
|
-
const template = await getClaudeMdTemplate();
|
|
1420
|
-
const enhanced = await applyContextToTemplate(template, repositoryContext, 'append');
|
|
1421
|
-
fs.writeFileSync(claudeMdPath, enhanced, 'utf8');
|
|
1422
|
-
console.log('✅ CLAUDE.md created with project context!');
|
|
1423
|
-
} else {
|
|
1424
|
-
console.log(' Would create: CLAUDE.md (dry-run mode)');
|
|
1425
|
-
}
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
// Show next steps
|
|
1429
|
-
console.log('\nNext steps:');
|
|
1430
|
-
console.log('1. Review the updated Additional Notes section in CLAUDE.md');
|
|
1431
|
-
console.log('2. Move any important context to appropriate sections');
|
|
1432
|
-
if (exists && backupPath && !flags.dryRun) {
|
|
1433
|
-
console.log(`3. Delete the backup file once satisfied: ${path.basename(backupPath)}`);
|
|
1434
|
-
console.log('4. Run `ccsetup scan` anytime to refresh context');
|
|
1435
|
-
} else if (exists) {
|
|
1436
|
-
console.log('3. Run `ccsetup scan` anytime to refresh context');
|
|
1437
|
-
} else {
|
|
1438
|
-
console.log('3. Add project-specific instructions and guidelines');
|
|
1439
|
-
console.log('4. Run `npx ccsetup` for full setup if you need agents and project structure');
|
|
1440
|
-
console.log('5. Run `ccsetup scan` anytime to refresh context');
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
|
-
} catch (error) {
|
|
1444
|
-
console.error('❌ Error handling CLAUDE.md:', error.message);
|
|
1445
|
-
if (exists && !flags.dryRun) {
|
|
1446
|
-
console.log('💡 Your original CLAUDE.md is safe. Check for backup files if needed.');
|
|
1447
|
-
}
|
|
1448
|
-
} finally {
|
|
1449
|
-
if (rl) rl.close();
|
|
1450
|
-
}
|
|
1451
|
-
}
|
|
1452
1009
|
|
|
1453
1010
|
// Dynamic import for ESM module
|
|
1454
1011
|
async function importCheckbox() {
|
|
@@ -1862,29 +1419,27 @@ async function main() {
|
|
|
1862
1419
|
await installClaudeHooks();
|
|
1863
1420
|
return;
|
|
1864
1421
|
}
|
|
1865
|
-
|
|
1866
|
-
if (setupMode === 'scan-only' || setupMode === 'scan-smart' || setupMode === 'scan-replace') {
|
|
1867
|
-
// Close readline if open
|
|
1868
|
-
if (rl) {
|
|
1869
|
-
rl.close();
|
|
1870
|
-
rl = null;
|
|
1871
|
-
}
|
|
1872
1422
|
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
flags.mergeStrategy = 'replace';
|
|
1877
|
-
} else {
|
|
1878
|
-
flags.mergeStrategy = 'smart';
|
|
1879
|
-
}
|
|
1880
|
-
|
|
1881
|
-
await scanOnlyMode(flags.mergeStrategy);
|
|
1882
|
-
return;
|
|
1423
|
+
if (!flags.force && !flags.dryRun && rl) {
|
|
1424
|
+
rl.close();
|
|
1425
|
+
rl = null;
|
|
1883
1426
|
}
|
|
1884
1427
|
|
|
1428
|
+
const aiProvider = await selectAiProvider();
|
|
1429
|
+
|
|
1885
1430
|
// Handle --agents and --browse flags for agent selection only
|
|
1886
1431
|
if (flags.agents || flags.browse) {
|
|
1432
|
+
if (!includesClaude(aiProvider)) {
|
|
1433
|
+
console.log('Claude agent selection is only available for Claude Code setup.');
|
|
1434
|
+
console.log('Re-run full setup to generate Codex assets.\n');
|
|
1435
|
+
process.exit(0);
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1887
1438
|
console.log('🤖 Interactive Agent Selection\n');
|
|
1439
|
+
|
|
1440
|
+
if (aiProvider === 'both') {
|
|
1441
|
+
console.log('This step configures Claude Code agents only. Codex assets are created during full setup.\n');
|
|
1442
|
+
}
|
|
1888
1443
|
|
|
1889
1444
|
let selectedAgentFiles = [];
|
|
1890
1445
|
|
|
@@ -1943,6 +1498,13 @@ async function main() {
|
|
|
1943
1498
|
process.exit(0);
|
|
1944
1499
|
}
|
|
1945
1500
|
|
|
1501
|
+
if (!flags.force && !flags.dryRun && !rl) {
|
|
1502
|
+
rl = readline.createInterface({
|
|
1503
|
+
input: process.stdin,
|
|
1504
|
+
output: process.stdout
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1946
1508
|
// Additional path validation
|
|
1947
1509
|
const normalizedTarget = path.normalize(targetDir);
|
|
1948
1510
|
const normalizedCwd = path.normalize(process.cwd());
|
|
@@ -1957,8 +1519,12 @@ async function main() {
|
|
|
1957
1519
|
}
|
|
1958
1520
|
|
|
1959
1521
|
// Check for Claude Code installation
|
|
1960
|
-
const claudeStatus = checkClaudeCode()
|
|
1961
|
-
|
|
1522
|
+
const claudeStatus = includesClaude(aiProvider) ? checkClaudeCode() : {
|
|
1523
|
+
isInstalled: false,
|
|
1524
|
+
hasClaudeDir: false,
|
|
1525
|
+
hasClaudeCodeFile: false
|
|
1526
|
+
};
|
|
1527
|
+
if (includesClaude(aiProvider) && projectName === '.') {
|
|
1962
1528
|
if (flags.dryRun) {
|
|
1963
1529
|
console.log('⚠️ Note: Claude Code detection skipped in dry-run mode for current directory\n');
|
|
1964
1530
|
} else if (!claudeStatus.isInstalled && !claudeStatus.hasClaudeDir) {
|
|
@@ -1992,7 +1558,12 @@ async function main() {
|
|
|
1992
1558
|
|
|
1993
1559
|
// Escape targetDir for safe display
|
|
1994
1560
|
const safeTargetDir = targetDir.replace(/[^\w\s\-./\\:]/g, '');
|
|
1995
|
-
|
|
1561
|
+
const projectLabel = aiProvider === 'both'
|
|
1562
|
+
? 'Claude Code + Codex'
|
|
1563
|
+
: aiProvider === 'codex'
|
|
1564
|
+
? 'Codex'
|
|
1565
|
+
: 'Claude Code';
|
|
1566
|
+
console.log(`${flags.dryRun ? 'Would create' : 'Creating'} ${projectLabel} project in ${safeTargetDir}...`);
|
|
1996
1567
|
|
|
1997
1568
|
if (projectName !== '.') {
|
|
1998
1569
|
if (!fs.existsSync(targetDir)) {
|
|
@@ -2013,8 +1584,8 @@ async function main() {
|
|
|
2013
1584
|
}
|
|
2014
1585
|
|
|
2015
1586
|
// Check Claude Code in new directory after creation
|
|
2016
|
-
const newDirClaudeStatus = checkClaudeCode();
|
|
2017
|
-
if (!flags.dryRun && !newDirClaudeStatus.isInstalled) {
|
|
1587
|
+
const newDirClaudeStatus = includesClaude(aiProvider) ? checkClaudeCode() : { isInstalled: true };
|
|
1588
|
+
if (includesClaude(aiProvider) && !flags.dryRun && !newDirClaudeStatus.isInstalled) {
|
|
2018
1589
|
console.log('\n⚠️ Note: Claude Code is not initialized in the new project directory.');
|
|
2019
1590
|
console.log('After setup, remember to:');
|
|
2020
1591
|
console.log(`1. cd ${JSON.stringify(projectName)}`);
|
|
@@ -2029,7 +1600,9 @@ async function main() {
|
|
|
2029
1600
|
// Group conflicts by category
|
|
2030
1601
|
const conflictsByCategory = {
|
|
2031
1602
|
'CLAUDE.md': [],
|
|
1603
|
+
'AGENTS.md': [],
|
|
2032
1604
|
'agents': [],
|
|
1605
|
+
'codex': [],
|
|
2033
1606
|
'docs': [],
|
|
2034
1607
|
'plans': [],
|
|
2035
1608
|
'tickets': []
|
|
@@ -2038,7 +1611,9 @@ async function main() {
|
|
|
2038
1611
|
// Store conflict strategies per category
|
|
2039
1612
|
const conflictStrategies = {
|
|
2040
1613
|
'CLAUDE.md': 'skip',
|
|
1614
|
+
'AGENTS.md': 'skip',
|
|
2041
1615
|
'agents': 'skip',
|
|
1616
|
+
'codex': 'skip',
|
|
2042
1617
|
'docs': 'skip',
|
|
2043
1618
|
'plans': 'skip',
|
|
2044
1619
|
'tickets': 'skip'
|
|
@@ -2050,7 +1625,10 @@ async function main() {
|
|
|
2050
1625
|
|
|
2051
1626
|
|
|
2052
1627
|
// Determine which agents to include
|
|
2053
|
-
if (
|
|
1628
|
+
if (!includesClaude(aiProvider)) {
|
|
1629
|
+
selectedAgentFiles = [];
|
|
1630
|
+
console.log('\n⏭️ Skipping Claude agents for Codex-only setup');
|
|
1631
|
+
} else if (flags.noAgents) {
|
|
2054
1632
|
selectedAgentFiles = [];
|
|
2055
1633
|
console.log('\n⏭️ Skipping agent selection (--no-agents flag)');
|
|
2056
1634
|
} else if (flags.browseAgents) {
|
|
@@ -2089,7 +1667,7 @@ async function main() {
|
|
|
2089
1667
|
description: 'Use the enhanced interface with categories, tags, and search'
|
|
2090
1668
|
},
|
|
2091
1669
|
{
|
|
2092
|
-
name: '📚 Copy All Agents - Get all
|
|
1670
|
+
name: '📚 Copy All Agents - Get all 8 agents to explore',
|
|
2093
1671
|
value: 'browse',
|
|
2094
1672
|
description: 'Copies all agents to /agents folder for manual review'
|
|
2095
1673
|
},
|
|
@@ -2131,7 +1709,7 @@ async function main() {
|
|
|
2131
1709
|
// In dry-run mode, show what would happen
|
|
2132
1710
|
console.log(`\nWould prompt for agent selection mode`);
|
|
2133
1711
|
console.log(`Would then prompt for agent selection from ${availableAgents.length} available agents`);
|
|
2134
|
-
selectedAgentFiles = availableAgents.map(a => a.file).filter(validateAgentFile); // Include all for
|
|
1712
|
+
selectedAgentFiles = availableAgents.map(a => a.file).filter(validateAgentFile); // Include all for dry-run preview
|
|
2135
1713
|
}
|
|
2136
1714
|
|
|
2137
1715
|
function scanTemplate(src, dest, relativePath = '', skipAgents = false) {
|
|
@@ -2157,6 +1735,10 @@ async function main() {
|
|
|
2157
1735
|
if (path.basename(src) === '.claude') {
|
|
2158
1736
|
return; // Don't process .claude directory in regular template scan
|
|
2159
1737
|
}
|
|
1738
|
+
|
|
1739
|
+
if (path.basename(src) === '.codex') {
|
|
1740
|
+
return; // Don't process .codex directory in regular template scan
|
|
1741
|
+
}
|
|
2160
1742
|
|
|
2161
1743
|
// Skip hooks directory - hooks should only be installed to .claude/hooks via --install-hooks
|
|
2162
1744
|
if (path.basename(src) === 'hooks') {
|
|
@@ -2264,6 +1846,14 @@ async function main() {
|
|
|
2264
1846
|
);
|
|
2265
1847
|
});
|
|
2266
1848
|
} else {
|
|
1849
|
+
if (
|
|
1850
|
+
(!includesClaude(aiProvider) && relativePath === 'CLAUDE.md') ||
|
|
1851
|
+
(!includesCodex(aiProvider) && relativePath === 'AGENTS.md') ||
|
|
1852
|
+
(!includesCodex(aiProvider) && relativePath === path.join('docs', 'codex-setup.md'))
|
|
1853
|
+
) {
|
|
1854
|
+
return;
|
|
1855
|
+
}
|
|
1856
|
+
|
|
2267
1857
|
allItems.push({
|
|
2268
1858
|
src,
|
|
2269
1859
|
dest,
|
|
@@ -2278,8 +1868,12 @@ async function main() {
|
|
|
2278
1868
|
// Categorize the conflict
|
|
2279
1869
|
if (relativePath === 'CLAUDE.md') {
|
|
2280
1870
|
conflictsByCategory['CLAUDE.md'].push(relativePath);
|
|
1871
|
+
} else if (relativePath === 'AGENTS.md') {
|
|
1872
|
+
conflictsByCategory['AGENTS.md'].push(relativePath);
|
|
2281
1873
|
} else if (relativePath.startsWith('agents/')) {
|
|
2282
1874
|
conflictsByCategory['agents'].push(relativePath);
|
|
1875
|
+
} else if (relativePath.startsWith('.codex/')) {
|
|
1876
|
+
conflictsByCategory['codex'].push(relativePath);
|
|
2283
1877
|
} else if (relativePath.startsWith('docs/')) {
|
|
2284
1878
|
conflictsByCategory['docs'].push(relativePath);
|
|
2285
1879
|
} else if (relativePath.startsWith('plans/')) {
|
|
@@ -2296,29 +1890,40 @@ async function main() {
|
|
|
2296
1890
|
|
|
2297
1891
|
scanTemplate(templateDir, targetDir, '', true);
|
|
2298
1892
|
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
1893
|
+
function scanCodexConflicts() {
|
|
1894
|
+
if (!includesCodex(aiProvider)) {
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
const templateCodexDir = path.join(templateDir, '.codex');
|
|
1899
|
+
if (!fs.existsSync(templateCodexDir)) {
|
|
1900
|
+
return;
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
function walk(srcDir, relBase = '.codex') {
|
|
1904
|
+
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
1905
|
+
for (const entry of entries) {
|
|
1906
|
+
const src = path.join(srcDir, entry.name);
|
|
1907
|
+
const relativePath = path.join(relBase, entry.name);
|
|
1908
|
+
const dest = path.join(targetDir, relativePath);
|
|
1909
|
+
|
|
1910
|
+
if (entry.isDirectory()) {
|
|
1911
|
+
walk(src, relativePath);
|
|
1912
|
+
continue;
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
if (fs.existsSync(dest)) {
|
|
1916
|
+
fileConflicts.push(relativePath);
|
|
1917
|
+
conflictsByCategory['codex'].push(relativePath);
|
|
2317
1918
|
}
|
|
2318
1919
|
}
|
|
2319
1920
|
}
|
|
1921
|
+
|
|
1922
|
+
walk(templateCodexDir);
|
|
2320
1923
|
}
|
|
2321
1924
|
|
|
1925
|
+
scanCodexConflicts();
|
|
1926
|
+
|
|
2322
1927
|
// Handle force flag
|
|
2323
1928
|
if (flags.force) {
|
|
2324
1929
|
// Set all strategies to overwrite
|
|
@@ -2342,7 +1947,9 @@ async function main() {
|
|
|
2342
1947
|
// Ask for resolution strategy for each category with conflicts
|
|
2343
1948
|
const categories = [
|
|
2344
1949
|
{ key: 'CLAUDE.md', name: 'CLAUDE.md', emoji: '📄' },
|
|
1950
|
+
{ key: 'AGENTS.md', name: 'AGENTS.md', emoji: '🧭' },
|
|
2345
1951
|
{ key: 'agents', name: 'Agents', emoji: '🤖' },
|
|
1952
|
+
{ key: 'codex', name: 'Codex', emoji: '🧠' },
|
|
2346
1953
|
{ key: 'docs', name: 'Documentation', emoji: '📚' },
|
|
2347
1954
|
{ key: 'plans', name: 'Plans', emoji: '📋' },
|
|
2348
1955
|
{ key: 'tickets', name: 'Tickets', emoji: '🎫' }
|
|
@@ -2373,17 +1980,17 @@ async function main() {
|
|
|
2373
1980
|
|
|
2374
1981
|
// Strategy will always be valid when using select, no need for validation
|
|
2375
1982
|
|
|
2376
|
-
if (strategy === 'overwrite' && category.key === 'CLAUDE.md') {
|
|
1983
|
+
if (strategy === 'overwrite' && (category.key === 'CLAUDE.md' || category.key === 'AGENTS.md')) {
|
|
2377
1984
|
// Use inquirer instead of readline prompt
|
|
2378
1985
|
const confirmModule = await import('@inquirer/confirm');
|
|
2379
1986
|
const confirm = confirmModule.default;
|
|
2380
1987
|
const shouldOverwrite = await confirm({
|
|
2381
|
-
message:
|
|
1988
|
+
message: `⚠️ Are you sure you want to overwrite ${category.key}? This will lose your project instructions!`,
|
|
2382
1989
|
default: false
|
|
2383
1990
|
});
|
|
2384
1991
|
if (!shouldOverwrite) {
|
|
2385
1992
|
conflictStrategies[category.key] = 'skip';
|
|
2386
|
-
console.log(
|
|
1993
|
+
console.log(`Keeping existing ${category.key}`);
|
|
2387
1994
|
continue;
|
|
2388
1995
|
}
|
|
2389
1996
|
}
|
|
@@ -2418,8 +2025,12 @@ async function main() {
|
|
|
2418
2025
|
let strategy = 'skip'; // default
|
|
2419
2026
|
if (item.relativePath === 'CLAUDE.md') {
|
|
2420
2027
|
strategy = conflictStrategies['CLAUDE.md'];
|
|
2028
|
+
} else if (item.relativePath === 'AGENTS.md') {
|
|
2029
|
+
strategy = conflictStrategies['AGENTS.md'];
|
|
2421
2030
|
} else if (item.relativePath.startsWith('agents/')) {
|
|
2422
2031
|
strategy = conflictStrategies['agents'];
|
|
2032
|
+
} else if (item.relativePath.startsWith('.codex/')) {
|
|
2033
|
+
strategy = conflictStrategies['codex'];
|
|
2423
2034
|
} else if (item.relativePath.startsWith('docs/')) {
|
|
2424
2035
|
strategy = conflictStrategies['docs'];
|
|
2425
2036
|
} else if (item.relativePath.startsWith('plans/')) {
|
|
@@ -2451,26 +2062,10 @@ async function main() {
|
|
|
2451
2062
|
console.log(` 📄 ${flags.dryRun ? 'Would create' : 'Created'}: ${path.relative(targetDir, newDest)}`);
|
|
2452
2063
|
} else if (strategy === 'overwrite') {
|
|
2453
2064
|
if (!flags.dryRun) {
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
const templateContent = fs.readFileSync(item.src, 'utf8');
|
|
2457
|
-
const enhancedContent = await applyContextToTemplate(
|
|
2458
|
-
templateContent,
|
|
2459
|
-
repositoryContext,
|
|
2460
|
-
'append'
|
|
2461
|
-
);
|
|
2462
|
-
fs.writeFileSync(item.dest, enhancedContent, 'utf8');
|
|
2463
|
-
console.log(` ♻️ Replaced CLAUDE.md with project context`);
|
|
2464
|
-
} else {
|
|
2465
|
-
fs.copyFileSync(item.src, item.dest);
|
|
2466
|
-
console.log(` ♻️ Replaced: ${item.relativePath}`);
|
|
2467
|
-
}
|
|
2065
|
+
fs.copyFileSync(item.src, item.dest);
|
|
2066
|
+
console.log(` ♻️ Replaced: ${item.relativePath}`);
|
|
2468
2067
|
} else {
|
|
2469
|
-
|
|
2470
|
-
console.log(` ♻️ Would replace: ${item.relativePath} (with project context)`);
|
|
2471
|
-
} else {
|
|
2472
|
-
console.log(` ♻️ Would replace: ${item.relativePath}`);
|
|
2473
|
-
}
|
|
2068
|
+
console.log(` ♻️ Would replace: ${item.relativePath}`);
|
|
2474
2069
|
}
|
|
2475
2070
|
overwrittenCount++;
|
|
2476
2071
|
}
|
|
@@ -2482,25 +2077,9 @@ async function main() {
|
|
|
2482
2077
|
fs.mkdirSync(destDir, { recursive: true });
|
|
2483
2078
|
}
|
|
2484
2079
|
|
|
2485
|
-
|
|
2486
|
-
if (item.relativePath === 'CLAUDE.md' && repositoryContext) {
|
|
2487
|
-
const templateContent = fs.readFileSync(item.src, 'utf8');
|
|
2488
|
-
const enhancedContent = await applyContextToTemplate(
|
|
2489
|
-
templateContent,
|
|
2490
|
-
repositoryContext,
|
|
2491
|
-
'append'
|
|
2492
|
-
);
|
|
2493
|
-
fs.writeFileSync(item.dest, enhancedContent, 'utf8');
|
|
2494
|
-
console.log(` ✨ Created CLAUDE.md with project context`);
|
|
2495
|
-
} else {
|
|
2496
|
-
fs.copyFileSync(item.src, item.dest);
|
|
2497
|
-
}
|
|
2080
|
+
fs.copyFileSync(item.src, item.dest);
|
|
2498
2081
|
} else {
|
|
2499
|
-
|
|
2500
|
-
console.log(` ✨ Would copy: ${item.relativePath} (with project context)`);
|
|
2501
|
-
} else {
|
|
2502
|
-
console.log(` ✨ Would copy: ${item.relativePath}`);
|
|
2503
|
-
}
|
|
2082
|
+
console.log(` ✨ Would copy: ${item.relativePath}`);
|
|
2504
2083
|
}
|
|
2505
2084
|
copiedCount++;
|
|
2506
2085
|
}
|
|
@@ -2517,32 +2096,42 @@ async function main() {
|
|
|
2517
2096
|
}
|
|
2518
2097
|
}
|
|
2519
2098
|
|
|
2520
|
-
// Initialize
|
|
2099
|
+
// Initialize provider-specific directories
|
|
2521
2100
|
let claudeInitResult = null;
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
}
|
|
2529
|
-
|
|
2530
|
-
|
|
2101
|
+
let codexInitResult = null;
|
|
2102
|
+
if (includesClaude(aiProvider)) {
|
|
2103
|
+
console.log(`\n🔧 ${claudeStatus.hasClaudeDir ? 'Updating' : 'Initializing'} .claude directory structure...`);
|
|
2104
|
+
claudeInitResult = await initializeClaudeDirectory(selectedAgentFiles, conflictStrategies['agents'], flags.dryRun);
|
|
2105
|
+
|
|
2106
|
+
if (claudeInitResult.createdItems.length > 0) {
|
|
2107
|
+
console.log(` ✅ Created ${claudeInitResult.createdItems.length} items in .claude directory`);
|
|
2108
|
+
}
|
|
2109
|
+
if (claudeInitResult.copiedAgents > 0) {
|
|
2110
|
+
console.log(` 🤖 Copied ${claudeInitResult.copiedAgents} agents to .claude/agents`);
|
|
2111
|
+
}
|
|
2112
|
+
if (claudeInitResult.skippedAgents > 0) {
|
|
2113
|
+
console.log(` ⏭️ Skipped ${claudeInitResult.skippedAgents} existing agents in .claude/agents`);
|
|
2114
|
+
}
|
|
2531
2115
|
}
|
|
2532
|
-
|
|
2533
|
-
|
|
2116
|
+
|
|
2117
|
+
if (includesCodex(aiProvider)) {
|
|
2118
|
+
console.log(`\n🔧 ${fs.existsSync(path.join(targetDir, '.codex')) ? 'Updating' : 'Initializing'} .codex directory structure...`);
|
|
2119
|
+
codexInitResult = await initializeCodexDirectory(conflictStrategies['codex'], flags.dryRun);
|
|
2120
|
+
if (codexInitResult.createdItems.length > 0) {
|
|
2121
|
+
console.log(` ✅ Created ${codexInitResult.createdItems.length} items in .codex directory`);
|
|
2122
|
+
}
|
|
2534
2123
|
}
|
|
2535
2124
|
|
|
2536
|
-
console.log(`\n✅
|
|
2125
|
+
console.log(`\n✅ ${projectLabel} project ${flags.dryRun ? 'would be ' : ''}created successfully!`);
|
|
2537
2126
|
|
|
2538
2127
|
// Show summary of what happened
|
|
2539
|
-
if (fileConflicts.length > 0 || copiedCount > 0 || selectedAgentFiles.length > 0 || claudeInitResult) {
|
|
2128
|
+
if (fileConflicts.length > 0 || copiedCount > 0 || selectedAgentFiles.length > 0 || claudeInitResult || codexInitResult) {
|
|
2540
2129
|
console.log('\n📊 Summary:');
|
|
2541
2130
|
if (copiedCount > 0) console.log(` ✨ ${copiedCount} new files ${flags.dryRun ? 'would be' : ''} copied`);
|
|
2542
2131
|
if (skippedCount > 0) console.log(` ⏭️ ${skippedCount} existing files ${flags.dryRun ? 'would be' : ''} kept unchanged`);
|
|
2543
2132
|
if (renamedCount > 0) console.log(` 📄 ${renamedCount} template files ${flags.dryRun ? 'would be' : ''} saved with -ccsetup suffix`);
|
|
2544
2133
|
if (overwrittenCount > 0) console.log(` ♻️ ${overwrittenCount} files ${flags.dryRun ? 'would be' : ''} replaced with template versions`);
|
|
2545
|
-
if (!flags.noAgents && !flags.dryRun) {
|
|
2134
|
+
if (includesClaude(aiProvider) && !flags.noAgents && !flags.dryRun) {
|
|
2546
2135
|
if (flags.browseAgents) {
|
|
2547
2136
|
const agentCount = availableAgents.length;
|
|
2548
2137
|
console.log(` 📚 ${agentCount} agent${agentCount === 1 ? '' : 's'} ${flags.dryRun ? 'would be' : ''} copied to /agents for browsing`);
|
|
@@ -2554,8 +2143,8 @@ async function main() {
|
|
|
2554
2143
|
if (claudeInitResult && claudeInitResult.createdItems.length > 0) {
|
|
2555
2144
|
console.log(` 📁 ${claudeInitResult.createdItems.length} items created in .claude directory`);
|
|
2556
2145
|
}
|
|
2557
|
-
if (
|
|
2558
|
-
console.log(`
|
|
2146
|
+
if (codexInitResult && codexInitResult.createdItems.length > 0) {
|
|
2147
|
+
console.log(` 🧠 ${codexInitResult.createdItems.length} items created in .codex directory`);
|
|
2559
2148
|
}
|
|
2560
2149
|
}
|
|
2561
2150
|
|
|
@@ -2565,15 +2154,21 @@ async function main() {
|
|
|
2565
2154
|
// Escape project name to prevent command injection
|
|
2566
2155
|
const escapedProjectName = JSON.stringify(projectName);
|
|
2567
2156
|
console.log(` cd ${escapedProjectName}`);
|
|
2568
|
-
const finalClaudeStatus = checkClaudeCode();
|
|
2569
|
-
if (!finalClaudeStatus.isInstalled) {
|
|
2157
|
+
const finalClaudeStatus = includesClaude(aiProvider) ? checkClaudeCode() : { isInstalled: true };
|
|
2158
|
+
if (includesClaude(aiProvider) && !finalClaudeStatus.isInstalled) {
|
|
2570
2159
|
console.log(' claude init # Initialize Claude Code in the project');
|
|
2571
2160
|
}
|
|
2572
2161
|
}
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2162
|
+
if (includesClaude(aiProvider)) {
|
|
2163
|
+
console.log(' 1. Edit CLAUDE.md to add your project-specific instructions');
|
|
2164
|
+
}
|
|
2165
|
+
if (includesCodex(aiProvider)) {
|
|
2166
|
+
console.log(` ${includesClaude(aiProvider) ? '2' : '1'}. Edit AGENTS.md to add your Codex project instructions`);
|
|
2167
|
+
console.log(` ${includesClaude(aiProvider) ? '3' : '2'}. Review docs/codex-setup.md for Codex setup guidance`);
|
|
2168
|
+
}
|
|
2169
|
+
console.log(` ${includesClaude(aiProvider) && includesCodex(aiProvider) ? '4' : includesCodex(aiProvider) ? '3' : '2'}. Update docs/ROADMAP.md with your project goals`);
|
|
2170
|
+
console.log(` ${includesClaude(aiProvider) && includesCodex(aiProvider) ? '5' : includesCodex(aiProvider) ? '4' : '3'}. Update docs/agent-orchestration.md to define agent workflows`);
|
|
2171
|
+
console.log(` ${includesClaude(aiProvider) && includesCodex(aiProvider) ? '6' : includesCodex(aiProvider) ? '5' : '4'}. Start creating tickets in the tickets/ directory`);
|
|
2577
2172
|
|
|
2578
2173
|
if (flags.browseAgents) {
|
|
2579
2174
|
console.log('\n📚 Agent Browse Mode:');
|
|
@@ -2589,8 +2184,13 @@ async function main() {
|
|
|
2589
2184
|
console.log(' You can compare them with your existing files or copy sections you need');
|
|
2590
2185
|
}
|
|
2591
2186
|
|
|
2187
|
+
if (includesCodex(aiProvider)) {
|
|
2188
|
+
console.log('\n🧠 Codex skills copied to .codex/skills/');
|
|
2189
|
+
console.log(' Use them from this project alongside AGENTS.md and docs/codex-setup.md');
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2592
2192
|
// Ask user if they want the workflow selector hook
|
|
2593
|
-
if (setupMode === 'full' && !flags.dryRun) {
|
|
2193
|
+
if (includesClaude(aiProvider) && setupMode === 'full' && !flags.dryRun) {
|
|
2594
2194
|
const claudeDir = path.join(targetDir, '.claude');
|
|
2595
2195
|
if (fs.existsSync(claudeDir)) {
|
|
2596
2196
|
try {
|
|
@@ -2688,4 +2288,4 @@ main().catch(err => {
|
|
|
2688
2288
|
rl.close();
|
|
2689
2289
|
}
|
|
2690
2290
|
process.exit(1);
|
|
2691
|
-
});
|
|
2291
|
+
});
|