ccsetup 1.1.1 → 1.2.0

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.
Files changed (88) hide show
  1. package/README.md +96 -363
  2. package/bin/create-project.js +1616 -60
  3. package/bin/lib/claudeInterface.js +209 -0
  4. package/bin/lib/contextGenerator.js +287 -0
  5. package/bin/lib/scanner/index.js +28 -0
  6. package/bin/scan.js +367 -0
  7. package/lib/aiAgentSelector.js +155 -0
  8. package/lib/aiMergeHelper.js +112 -0
  9. package/lib/contextGenerator.js +574 -0
  10. package/lib/contextMerger.js +812 -0
  11. package/lib/progressReporter.js +88 -0
  12. package/lib/scanConfig.js +200 -0
  13. package/lib/scanner/fileAnalyzer.js +605 -0
  14. package/lib/scanner/index.js +164 -0
  15. package/lib/scanner/patterns.js +277 -0
  16. package/lib/scanner/projectDetector.js +147 -0
  17. package/lib/templates/README.md +176 -0
  18. package/lib/templates/catalog.js +230 -0
  19. package/lib/templates/filter.js +257 -0
  20. package/lib/templates/index.js +45 -0
  21. package/lib/templates/metadata/agents.json +413 -0
  22. package/lib/templates/metadata-extractor.js +329 -0
  23. package/lib/templates/search.js +356 -0
  24. package/package.json +11 -4
  25. package/template/{agents → .claude/agents}/checker.md +29 -0
  26. package/template/.claude/settings.json +15 -0
  27. package/template/.claude/skills/prd/SKILL.md +343 -0
  28. package/template/.claude/skills/ralph/SKILL.md +339 -0
  29. package/template/CLAUDE.md +39 -21
  30. package/template/CONTRIBUTING.md +37 -0
  31. package/template/agents/README.md +15 -171
  32. package/template/docs/ROADMAP.md +0 -36
  33. package/template/docs/agent-orchestration.md +24 -141
  34. package/template/hooks/workflow-selector/index.js +398 -0
  35. package/template/scripts/ralph/CLAUDE.md +174 -0
  36. package/template/scripts/ralph/ralph.sh +127 -0
  37. package/template/tickets/ticket-list.md +17 -68
  38. package/template/agents/ai-engineer.md +0 -31
  39. package/template/agents/api-documenter.md +0 -31
  40. package/template/agents/architect-review.md +0 -42
  41. package/template/agents/backend-architect.md +0 -29
  42. package/template/agents/business-analyst.md +0 -34
  43. package/template/agents/c-pro.md +0 -34
  44. package/template/agents/cloud-architect.md +0 -31
  45. package/template/agents/code-reviewer.md +0 -28
  46. package/template/agents/content-marketer.md +0 -34
  47. package/template/agents/context-manager.md +0 -63
  48. package/template/agents/cpp-pro.md +0 -37
  49. package/template/agents/customer-support.md +0 -34
  50. package/template/agents/data-engineer.md +0 -31
  51. package/template/agents/data-scientist.md +0 -28
  52. package/template/agents/database-admin.md +0 -31
  53. package/template/agents/database-optimizer.md +0 -31
  54. package/template/agents/debugger.md +0 -29
  55. package/template/agents/deployment-engineer.md +0 -31
  56. package/template/agents/devops-troubleshooter.md +0 -31
  57. package/template/agents/dx-optimizer.md +0 -62
  58. package/template/agents/error-detective.md +0 -31
  59. package/template/agents/frontend-developer.md +0 -30
  60. package/template/agents/golang-pro.md +0 -31
  61. package/template/agents/graphql-architect.md +0 -31
  62. package/template/agents/incident-responder.md +0 -73
  63. package/template/agents/javascript-pro.md +0 -34
  64. package/template/agents/legacy-modernizer.md +0 -31
  65. package/template/agents/ml-engineer.md +0 -31
  66. package/template/agents/mlops-engineer.md +0 -56
  67. package/template/agents/mobile-developer.md +0 -31
  68. package/template/agents/network-engineer.md +0 -31
  69. package/template/agents/payment-integration.md +0 -31
  70. package/template/agents/performance-engineer.md +0 -31
  71. package/template/agents/prompt-engineer.md +0 -58
  72. package/template/agents/python-pro.md +0 -31
  73. package/template/agents/quant-analyst.md +0 -31
  74. package/template/agents/risk-manager.md +0 -40
  75. package/template/agents/rust-pro.md +0 -34
  76. package/template/agents/sales-automator.md +0 -34
  77. package/template/agents/search-specialist.md +0 -58
  78. package/template/agents/security-auditor.md +0 -31
  79. package/template/agents/sql-pro.md +0 -34
  80. package/template/agents/terraform-specialist.md +0 -34
  81. package/template/agents/test-automator.md +0 -31
  82. /package/template/{agents → .claude/agents}/backend.md +0 -0
  83. /package/template/{agents → .claude/agents}/blockchain.md +0 -0
  84. /package/template/{agents → .claude/agents}/coder.md +0 -0
  85. /package/template/{agents → .claude/agents}/frontend.md +0 -0
  86. /package/template/{agents → .claude/agents}/planner.md +0 -0
  87. /package/template/{agents → .claude/agents}/researcher.md +0 -0
  88. /package/template/{agents → .claude/agents}/shadcn.md +0 -0
@@ -3,9 +3,23 @@
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
+ const TemplateCatalog = require('../lib/templates/catalog');
11
+ const TemplateFilter = require('../lib/templates/filter');
12
+ const TemplateSearch = require('../lib/templates/search');
6
13
 
7
14
  // Parse CLI arguments
8
15
  const args = process.argv.slice(2);
16
+
17
+ // Handle scan subcommand
18
+ if (args[0] === 'scan') {
19
+ require('./scan.js');
20
+ return;
21
+ }
22
+
9
23
  const flags = {
10
24
  force: false,
11
25
  dryRun: false,
@@ -13,7 +27,11 @@ const flags = {
13
27
  allAgents: false,
14
28
  noAgents: false,
15
29
  agents: false,
16
- browseAgents: false
30
+ browseAgents: false,
31
+ browse: false,
32
+ scanContext: false,
33
+ scanOnly: false,
34
+ prompt: null
17
35
  };
18
36
 
19
37
  let projectName = '.';
@@ -35,6 +53,14 @@ for (let i = 0; i < args.length; i++) {
35
53
  flags.agents = true;
36
54
  } else if (arg === '--browse-agents') {
37
55
  flags.browseAgents = true;
56
+ } else if (arg === '--browse') {
57
+ flags.browse = true;
58
+ } else if (arg === '--scan-context') {
59
+ flags.scanContext = true;
60
+ } else if (arg === '--scan-only') {
61
+ flags.scanOnly = true;
62
+ } else if (arg === '--install-hooks') {
63
+ flags.installHooks = true;
38
64
  } else if (!arg.startsWith('-')) {
39
65
  projectName = arg;
40
66
  }
@@ -44,24 +70,48 @@ for (let i = 0; i < args.length; i++) {
44
70
  if (flags.help) {
45
71
  console.log(`
46
72
  Usage: ccsetup [project-name] [options]
73
+ ccsetup scan [path] [options]
74
+
75
+ Commands:
76
+ ccsetup Interactive mode - choose full setup or scan-only
77
+ ccsetup <name> Create a new Claude Code project
78
+ ccsetup scan Advanced repository scanning (see 'ccsetup scan --help')
47
79
 
48
80
  Options:
49
- --force, -f Skip all prompts and overwrite existing files
50
- --dry-run, -d Show what would be done without making changes
51
- --agents Interactive agent selection mode
52
- --all-agents Include all agents without prompting
53
- --no-agents Skip agent selection entirely
54
- --browse-agents Copy all agents to /agents folder for browsing
55
- --help, -h Show this help message
81
+ --scan-only Skip project setup, only scan and create/update CLAUDE.md
82
+ --force, -f Skip all prompts and overwrite existing files
83
+ --dry-run, -d Show what would be done without making changes
84
+ --agents Interactive agent selection mode
85
+ --all-agents Include all agents without prompting
86
+ --no-agents Skip agent selection entirely
87
+ --browse-agents Copy all agents to /agents folder for browsing
88
+ --browse Enhanced template browsing and selection interface
89
+ --scan-context Scan repository and add context to CLAUDE.md
90
+ --help, -h Show this help message
56
91
 
92
+ Advanced:
93
+ --install-hooks Install workflow selection hook to .claude/hooks (optional, power users only)
94
+
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
+
57
104
  Examples:
58
- ccsetup # Create in current directory
105
+ ccsetup --scan-only # Scan current directory only
106
+ ccsetup . --scan-only # Same as above
107
+ ccsetup --scan-only --force # Skip confirmation prompts
108
+ ccsetup --scan-only --dry-run # Preview what would happen
109
+
110
+ Full Setup Examples:
111
+ ccsetup # Interactive setup in current directory
59
112
  ccsetup my-project # Create in new directory
60
- ccsetup . --force # Overwrite files in current directory
61
- ccsetup my-app --dry-run # Preview changes without creating files
62
- ccsetup --agents # Interactive agent selection only
113
+ ccsetup . --scan-context # Full setup with context scanning
63
114
  ccsetup my-app --all-agents # Include all agents automatically
64
- ccsetup --browse-agents # Copy all agents for manual selection
65
115
  `);
66
116
  process.exit(0);
67
117
  }
@@ -82,17 +132,221 @@ function validateProjectName(name) {
82
132
  return true;
83
133
  }
84
134
 
85
- // Validate conflicting flags
86
- if (flags.allAgents && flags.noAgents) {
87
- console.error('Error: Cannot use --all-agents and --no-agents together');
88
- process.exit(1);
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
+ }
89
177
  }
90
178
 
91
- if (flags.browseAgents && (flags.allAgents || flags.noAgents || flags.agents)) {
92
- console.error('Error: --browse-agents cannot be used with other agent flags');
93
- process.exit(1);
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
+ // Validate conflicting flags
314
+ 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
+ // Existing validations
332
+ if (flags.allAgents && flags.noAgents) {
333
+ console.error('Error: Cannot use --all-agents and --no-agents together');
334
+ process.exit(1);
335
+ }
336
+
337
+ if (flags.browseAgents && (flags.allAgents || flags.noAgents || flags.agents || flags.browse)) {
338
+ console.error('Error: --browse-agents cannot be used with other agent flags');
339
+ process.exit(1);
340
+ }
341
+
342
+ if (flags.browse && (flags.allAgents || flags.noAgents || flags.browseAgents)) {
343
+ console.error('Error: --browse cannot be used with other agent flags except --agents');
344
+ process.exit(1);
345
+ }
94
346
  }
95
347
 
348
+ validateFlags();
349
+
96
350
  // Validate the project name
97
351
  if (projectName !== '.') {
98
352
  try {
@@ -254,6 +508,23 @@ async function initializeClaudeDirectory(selectedAgentFiles, conflictStrategy, d
254
508
  }
255
509
  }
256
510
 
511
+ // Copy .claude/settings.json
512
+ const settingsSrc = path.join(templateClaudeDir, 'settings.json');
513
+ const settingsDest = path.join(claudeDir, 'settings.json');
514
+ if (fs.existsSync(settingsSrc)) {
515
+ if (!fs.existsSync(settingsDest)) {
516
+ if (!dryRun) {
517
+ fs.copyFileSync(settingsSrc, settingsDest);
518
+ }
519
+ createdItems.push('.claude/settings.json');
520
+ if (dryRun) {
521
+ console.log(' ✨ Would copy: .claude/settings.json');
522
+ }
523
+ } else {
524
+ skippedItems.push('.claude/settings.json');
525
+ }
526
+ }
527
+
257
528
  // Copy .claude/agents/README.md
258
529
  const agentsReadmeSrc = path.join(templateClaudeDir, 'agents', 'README.md');
259
530
  const agentsReadmeDest = path.join(claudeAgentsDir, 'README.md');
@@ -303,8 +574,38 @@ async function initializeClaudeDirectory(selectedAgentFiles, conflictStrategy, d
303
574
  }
304
575
  }
305
576
 
577
+ // Copy .claude/skills/ directory (recursively)
578
+ const templateSkillsDir = path.join(templateClaudeDir, 'skills');
579
+ if (fs.existsSync(templateSkillsDir)) {
580
+ const claudeSkillsDir = path.join(claudeDir, 'skills');
581
+ const skillDirs = fs.readdirSync(templateSkillsDir).filter(d => {
582
+ return fs.statSync(path.join(templateSkillsDir, d)).isDirectory();
583
+ });
584
+
585
+ for (const skillName of skillDirs) {
586
+ const skillSrcDir = path.join(templateSkillsDir, skillName);
587
+ const skillDestDir = path.join(claudeSkillsDir, skillName);
588
+ const skillFile = path.join(skillSrcDir, 'SKILL.md');
589
+
590
+ if (fs.existsSync(skillFile)) {
591
+ if (!fs.existsSync(path.join(skillDestDir, 'SKILL.md'))) {
592
+ if (!dryRun) {
593
+ fs.mkdirSync(skillDestDir, { recursive: true });
594
+ fs.copyFileSync(skillFile, path.join(skillDestDir, 'SKILL.md'));
595
+ }
596
+ createdItems.push(`.claude/skills/${skillName}/SKILL.md`);
597
+ if (dryRun) {
598
+ console.log(` ✨ Would copy: .claude/skills/${skillName}/SKILL.md`);
599
+ }
600
+ } else {
601
+ skippedItems.push(`.claude/skills/${skillName}/SKILL.md`);
602
+ }
603
+ }
604
+ }
605
+ }
606
+
306
607
  // Copy selected agents to .claude/agents
307
- const templateAgentsDir = path.join(templateDir, 'agents');
608
+ const templateAgentsDir = path.join(templateDir, '.claude', 'agents');
308
609
  let copiedAgents = 0;
309
610
  let skippedAgents = 0;
310
611
 
@@ -426,7 +727,7 @@ function parseAgentFrontmatter(filePath) {
426
727
 
427
728
  // Function to get available agents
428
729
  function getAvailableAgents() {
429
- const agentsDir = path.join(templateDir, 'agents');
730
+ const agentsDir = path.join(templateDir, '.claude', 'agents');
430
731
  const agents = [];
431
732
 
432
733
  try {
@@ -451,6 +752,704 @@ function getAvailableAgents() {
451
752
  return agents.sort((a, b) => a.name.localeCompare(b.name));
452
753
  }
453
754
 
755
+ // Install Claude Code hooks
756
+ async function installClaudeHooks() {
757
+ console.log('\n🪝 Installing Claude Code Workflow Selection Hook...\n');
758
+
759
+ const claudeDir = path.join(process.cwd(), '.claude');
760
+ const hooksDir = path.join(claudeDir, 'hooks');
761
+ const settingsFile = path.join(claudeDir, 'settings.json');
762
+
763
+ // Check if .claude directory exists
764
+ if (!fs.existsSync(claudeDir)) {
765
+ console.error('❌ Error: .claude directory not found.');
766
+ console.log('\n💡 Please run "claude init" first to initialize Claude Code in this directory.\n');
767
+ return;
768
+ }
769
+
770
+ try {
771
+ // Create hooks directory
772
+ if (!fs.existsSync(hooksDir)) {
773
+ fs.mkdirSync(hooksDir, { recursive: true });
774
+ console.log('✅ Created .claude/hooks directory');
775
+ }
776
+
777
+ // Copy workflow-selector hook
778
+ const hookSourceDir = path.join(templateDir, 'hooks', 'workflow-selector');
779
+ const hookDestDir = path.join(hooksDir, 'workflow-selector');
780
+
781
+ if (!fs.existsSync(hookSourceDir)) {
782
+ console.error('❌ Error: Hook source files not found in template.');
783
+ return;
784
+ }
785
+
786
+ // Check if hook already exists
787
+ const hookFile = path.join(hookSourceDir, 'index.js');
788
+ const destFile = path.join(hookDestDir, 'index.js');
789
+ let shouldCopyHook = true;
790
+
791
+ if (fs.existsSync(destFile)) {
792
+ console.log('⚠️ Workflow-selector hook already exists.');
793
+ const selectModule = await import('@inquirer/select');
794
+ const select = selectModule.default;
795
+
796
+ const action = await select({
797
+ message: 'How would you like to proceed?',
798
+ choices: [
799
+ {
800
+ name: '📋 Keep existing - Preserve your customizations',
801
+ value: 'keep',
802
+ description: 'Keep your existing hook file unchanged'
803
+ },
804
+ {
805
+ name: '🔄 Update - Replace with latest version',
806
+ value: 'replace',
807
+ description: 'Replace with the latest hook (backup will be created)'
808
+ },
809
+ {
810
+ name: '👀 Compare - View differences first',
811
+ value: 'compare',
812
+ description: 'Compare existing and new versions before deciding'
813
+ },
814
+ {
815
+ name: '❌ Skip - Cancel hook installation',
816
+ value: 'skip',
817
+ description: 'Skip installing the hook file'
818
+ }
819
+ ]
820
+ });
821
+
822
+ if (action === 'skip') {
823
+ console.log('⏭️ Skipped hook file installation');
824
+ shouldCopyHook = false;
825
+ } else if (action === 'keep') {
826
+ console.log('✅ Keeping existing hook file');
827
+ shouldCopyHook = false;
828
+ } else if (action === 'compare') {
829
+ // Show comparison
830
+ console.log('\n📄 Existing hook file summary:');
831
+ const existingContent = fs.readFileSync(destFile, 'utf8');
832
+ const existingLines = existingContent.split('\n').length;
833
+ console.log(` Lines: ${existingLines}`);
834
+ console.log(` Size: ${fs.statSync(destFile).size} bytes`);
835
+ console.log(` Modified: ${fs.statSync(destFile).mtime.toLocaleString()}`);
836
+
837
+ const confirmModule = await import('@inquirer/confirm');
838
+ const confirm = confirmModule.default;
839
+ const shouldReplace = await confirm({
840
+ message: 'Replace with new version?',
841
+ default: false
842
+ });
843
+
844
+ if (shouldReplace) {
845
+ // Create backup
846
+ const backupFile = destFile + '.backup-' + Date.now();
847
+ fs.copyFileSync(destFile, backupFile);
848
+ console.log(`✅ Created backup: ${path.basename(backupFile)}`);
849
+ shouldCopyHook = true;
850
+ } else {
851
+ shouldCopyHook = false;
852
+ }
853
+ } else if (action === 'replace') {
854
+ // Create backup
855
+ const backupFile = destFile + '.backup-' + Date.now();
856
+ fs.copyFileSync(destFile, backupFile);
857
+ console.log(`✅ Created backup: ${path.basename(backupFile)}`);
858
+ shouldCopyHook = true;
859
+ }
860
+ }
861
+
862
+ // Create workflow-selector directory if needed
863
+ if (!fs.existsSync(hookDestDir)) {
864
+ fs.mkdirSync(hookDestDir, { recursive: true });
865
+ }
866
+
867
+ // Copy hook file if needed
868
+ if (shouldCopyHook) {
869
+ fs.copyFileSync(hookFile, destFile);
870
+ console.log('✅ Installed workflow-selector hook');
871
+ }
872
+
873
+ // Update settings.json
874
+ let settings = {};
875
+ if (fs.existsSync(settingsFile)) {
876
+ const content = fs.readFileSync(settingsFile, 'utf8');
877
+ try {
878
+ settings = JSON.parse(content);
879
+ } catch (e) {
880
+ console.warn('⚠️ Warning: Could not parse existing settings.json, creating new one');
881
+ }
882
+ }
883
+
884
+ // Add hook configuration intelligently
885
+ if (!settings.hooks) {
886
+ settings.hooks = {};
887
+ }
888
+
889
+ // Check if UserPromptSubmit hooks already exist
890
+ const workflowHookCommand = "node $CLAUDE_PROJECT_DIR/.claude/hooks/workflow-selector/index.js";
891
+ let hookExists = false;
892
+
893
+ if (settings.hooks.UserPromptSubmit && Array.isArray(settings.hooks.UserPromptSubmit)) {
894
+ // Check if our hook is already configured
895
+ hookExists = settings.hooks.UserPromptSubmit.some(hookConfig =>
896
+ hookConfig.hooks && hookConfig.hooks.some(hook =>
897
+ hook.type === 'command' && hook.command === workflowHookCommand
898
+ )
899
+ );
900
+
901
+ if (hookExists) {
902
+ console.log('✅ Workflow hook already configured in settings.json');
903
+ } else {
904
+ // Ask user how to proceed
905
+ const selectModule = await import('@inquirer/select');
906
+ const select = selectModule.default;
907
+
908
+ console.log('\n⚠️ Existing UserPromptSubmit hooks detected in settings.json');
909
+ const action = await select({
910
+ message: 'How would you like to add the workflow hook?',
911
+ choices: [
912
+ {
913
+ name: '➕ Add to existing - Preserve current hooks and add workflow hook',
914
+ value: 'add',
915
+ description: 'Keep all existing hooks and add the workflow hook'
916
+ },
917
+ {
918
+ name: '🔄 Replace all - Replace existing hooks with workflow hook',
919
+ value: 'replace',
920
+ description: 'Replace all existing hooks (backup will be created)'
921
+ },
922
+ {
923
+ name: '❌ Skip - Don\'t modify hooks configuration',
924
+ value: 'skip',
925
+ description: 'Keep settings.json unchanged'
926
+ }
927
+ ]
928
+ });
929
+
930
+ if (action === 'add') {
931
+ // Add our hook to existing array
932
+ settings.hooks.UserPromptSubmit.push({
933
+ "matcher": ".*",
934
+ "hooks": [
935
+ {
936
+ "type": "command",
937
+ "command": workflowHookCommand
938
+ }
939
+ ]
940
+ });
941
+ console.log('✅ Added workflow hook to existing hooks');
942
+ } else if (action === 'replace') {
943
+ // Backup existing settings
944
+ const backupFile = settingsFile + '.backup-' + Date.now();
945
+ fs.writeFileSync(backupFile, JSON.stringify(settings, null, 2));
946
+ console.log(`✅ Created settings backup: ${path.basename(backupFile)}`);
947
+
948
+ // Replace with our hook
949
+ settings.hooks.UserPromptSubmit = [
950
+ {
951
+ "matcher": ".*",
952
+ "hooks": [
953
+ {
954
+ "type": "command",
955
+ "command": workflowHookCommand
956
+ }
957
+ ]
958
+ }
959
+ ];
960
+ console.log('✅ Replaced existing hooks with workflow hook');
961
+ } else {
962
+ console.log('⏭️ Skipped settings.json modification');
963
+ return;
964
+ }
965
+ }
966
+ } else {
967
+ // No existing UserPromptSubmit hooks, safe to add
968
+ settings.hooks.UserPromptSubmit = [
969
+ {
970
+ "matcher": ".*",
971
+ "hooks": [
972
+ {
973
+ "type": "command",
974
+ "command": workflowHookCommand
975
+ }
976
+ ]
977
+ }
978
+ ];
979
+ console.log('✅ Added workflow hook configuration');
980
+ }
981
+
982
+ // Write updated settings only if we made changes
983
+ if (!hookExists) {
984
+ fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
985
+ console.log('✅ Updated .claude/settings.json');
986
+ }
987
+
988
+ // Copy agent-orchestration.md if it doesn't exist
989
+ const orchestrationSource = path.join(templateDir, 'docs', 'agent-orchestration.md');
990
+ const orchestrationDest = path.join(process.cwd(), 'docs', 'agent-orchestration.md');
991
+
992
+ if (!fs.existsSync(orchestrationDest) && fs.existsSync(orchestrationSource)) {
993
+ const docsDir = path.join(process.cwd(), 'docs');
994
+ if (!fs.existsSync(docsDir)) {
995
+ fs.mkdirSync(docsDir, { recursive: true });
996
+ }
997
+ fs.copyFileSync(orchestrationSource, orchestrationDest);
998
+ console.log('✅ Copied agent-orchestration.md to docs/');
999
+ }
1000
+
1001
+ console.log('\n🎉 Workflow selection hook installed successfully!\n');
1002
+ console.log('The hook will:');
1003
+ console.log(' • Analyze your prompts to determine task type');
1004
+ console.log(' • Suggest appropriate workflows (Feature Development, Bug Fix, etc.)');
1005
+ console.log(' • Generate relevant todo items');
1006
+ console.log(' • Guide agent selection based on the task\n');
1007
+ console.log('📝 Note: The hook reads workflows from docs/agent-orchestration.md');
1008
+ console.log('💡 You can disable the hook by editing .claude/settings.json\n');
1009
+
1010
+ } catch (error) {
1011
+ console.error('❌ Error installing hooks:', error.message);
1012
+ }
1013
+ }
1014
+
1015
+ // Setup mode selection function
1016
+ async function selectSetupMode() {
1017
+ // Direct flag handling
1018
+ if (flags.installHooks) return 'install-hooks';
1019
+ if (flags.scanOnly) return 'scan-only';
1020
+ if (flags.agents) return 'agents-only';
1021
+ if (flags.force || flags.browseAgents || flags.allAgents || flags.noAgents) return 'full';
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 };
1131
+ }
1132
+
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
+
454
1453
  // Dynamic import for ESM module
455
1454
  async function importCheckbox() {
456
1455
  try {
@@ -512,6 +1511,341 @@ async function selectAgents(availableAgents) {
512
1511
  return validatedFiles;
513
1512
  }
514
1513
 
1514
+ async function enhancedAgentSelection() {
1515
+ try {
1516
+ console.log('\n🔍 Loading template catalog...');
1517
+ const catalog = TemplateCatalog.createInstance();
1518
+ const templates = await catalog.load();
1519
+
1520
+ if (!templates.agents || templates.agents.length === 0) {
1521
+ console.log('❌ No agents found in catalog. Falling back to basic selection.');
1522
+ return await selectAgents(getAvailableAgents());
1523
+ }
1524
+
1525
+ console.log(`✅ Found ${templates.agents.length} agents in catalog\n`);
1526
+
1527
+ // Import select for mode selection
1528
+ const selectModule = await import('@inquirer/select');
1529
+ const select = selectModule.default;
1530
+
1531
+ const mode = await select({
1532
+ message: 'How would you like to browse and select agents?',
1533
+ choices: [
1534
+ {
1535
+ name: '🔍 Search & Filter - Find agents by keywords and categories',
1536
+ value: 'search',
1537
+ description: 'Use search and filtering to find exactly what you need'
1538
+ },
1539
+ {
1540
+ name: '📂 Browse by Category - Explore agents organized by type',
1541
+ value: 'category',
1542
+ description: 'Browse agents grouped by their specialization'
1543
+ },
1544
+ {
1545
+ name: '🏷️ Browse by Tags - Find agents with specific capabilities',
1546
+ value: 'tags',
1547
+ description: 'Explore agents based on their tags and features'
1548
+ },
1549
+ {
1550
+ name: '📋 Simple List - Traditional selection from all agents',
1551
+ value: 'simple',
1552
+ description: 'Classic checkbox selection from complete agent list'
1553
+ }
1554
+ ]
1555
+ });
1556
+
1557
+ let selectedAgents = [];
1558
+
1559
+ switch (mode) {
1560
+ case 'search':
1561
+ selectedAgents = await searchAndFilterSelection(templates.agents);
1562
+ break;
1563
+ case 'category':
1564
+ selectedAgents = await categoryBrowseSelection(templates.agents);
1565
+ break;
1566
+ case 'tags':
1567
+ selectedAgents = await tagBrowseSelection(templates.agents);
1568
+ break;
1569
+ case 'simple':
1570
+ default:
1571
+ selectedAgents = await simpleListSelection(templates.agents);
1572
+ break;
1573
+ }
1574
+
1575
+ return selectedAgents.map(agent => agent.files[0]).filter(file => file && validateAgentFile(file));
1576
+
1577
+ } catch (error) {
1578
+ console.warn(`⚠️ Enhanced selection failed: ${error.message}`);
1579
+ console.log('Falling back to basic agent selection...\n');
1580
+ return await selectAgents(getAvailableAgents());
1581
+ }
1582
+ }
1583
+
1584
+ async function searchAndFilterSelection(agents) {
1585
+ const inputModule = await import('@inquirer/input');
1586
+ const input = inputModule.default;
1587
+ const selectModule = await import('@inquirer/select');
1588
+ const select = selectModule.default;
1589
+ const checkboxModule = await import('@inquirer/checkbox');
1590
+ const checkbox = checkboxModule.default;
1591
+
1592
+ console.log('\n🔍 Search & Filter Mode\n');
1593
+
1594
+ let currentResults = agents;
1595
+ const selectedAgents = [];
1596
+
1597
+ while (true) {
1598
+ console.log(`\n📊 Current results: ${currentResults.length} agents`);
1599
+
1600
+ const action = await select({
1601
+ message: 'What would you like to do?',
1602
+ choices: [
1603
+ { name: '🔍 Search by keyword', value: 'search' },
1604
+ { name: '📂 Filter by category', value: 'category' },
1605
+ { name: '🏷️ Filter by tags', value: 'tags' },
1606
+ { name: '📋 Select from current results', value: 'select' },
1607
+ { name: '🔄 Reset filters', value: 'reset' },
1608
+ { name: '✅ Finish selection', value: 'done' }
1609
+ ]
1610
+ });
1611
+
1612
+ if (action === 'done') break;
1613
+
1614
+ if (action === 'reset') {
1615
+ currentResults = agents;
1616
+ continue;
1617
+ }
1618
+
1619
+ if (action === 'search') {
1620
+ const query = await input({
1621
+ message: 'Enter search terms:',
1622
+ validate: (input) => input.trim().length > 0 ? true : 'Please enter a search term'
1623
+ });
1624
+
1625
+ const search = new TemplateSearch(currentResults);
1626
+ currentResults = search.search(query.trim()).getResults();
1627
+ console.log(`Found ${currentResults.length} agents matching "${query}"`);
1628
+ continue;
1629
+ }
1630
+
1631
+ if (action === 'category') {
1632
+ const filter = new TemplateFilter(currentResults);
1633
+ const categories = filter.getSubcategories();
1634
+
1635
+ if (categories.length === 0) {
1636
+ console.log('No categories available in current results.');
1637
+ continue;
1638
+ }
1639
+
1640
+ const category = await select({
1641
+ message: 'Select category:',
1642
+ choices: [
1643
+ { name: 'All categories', value: 'all' },
1644
+ ...categories.map(cat => ({ name: cat, value: cat }))
1645
+ ]
1646
+ });
1647
+
1648
+ currentResults = filter.byCategory(category).getResults();
1649
+ console.log(`Filtered to ${currentResults.length} agents in category "${category}"`);
1650
+ continue;
1651
+ }
1652
+
1653
+ if (action === 'tags') {
1654
+ const filter = new TemplateFilter(currentResults);
1655
+ const tagCloud = filter.getTagsByFrequency();
1656
+
1657
+ if (tagCloud.length === 0) {
1658
+ console.log('No tags available in current results.');
1659
+ continue;
1660
+ }
1661
+
1662
+ const tags = await checkbox({
1663
+ message: 'Select tags to filter by:',
1664
+ choices: tagCloud.slice(0, 15).map(({ tag, count }) => ({
1665
+ name: `${tag} (${count})`,
1666
+ value: tag
1667
+ })),
1668
+ pageSize: 10
1669
+ });
1670
+
1671
+ if (tags.length > 0) {
1672
+ currentResults = filter.byTags(tags).getResults();
1673
+ console.log(`Filtered to ${currentResults.length} agents with tags: ${tags.join(', ')}`);
1674
+ }
1675
+ continue;
1676
+ }
1677
+
1678
+ if (action === 'select') {
1679
+ if (currentResults.length === 0) {
1680
+ console.log('No agents in current results to select from.');
1681
+ continue;
1682
+ }
1683
+
1684
+ const choices = currentResults.map(agent => ({
1685
+ name: `${agent.name}\n ${agent.description}`,
1686
+ value: agent,
1687
+ checked: selectedAgents.some(selected => selected.id === agent.id)
1688
+ }));
1689
+
1690
+ const newSelections = await checkbox({
1691
+ message: `Select agents (${currentResults.length} available):`,
1692
+ choices,
1693
+ pageSize: 8
1694
+ });
1695
+
1696
+ // Update selected agents
1697
+ selectedAgents.length = 0;
1698
+ selectedAgents.push(...newSelections);
1699
+
1700
+ console.log(`Selected ${selectedAgents.length} agents`);
1701
+ }
1702
+ }
1703
+
1704
+ return selectedAgents;
1705
+ }
1706
+
1707
+ async function categoryBrowseSelection(agents) {
1708
+ const selectModule = await import('@inquirer/select');
1709
+ const select = selectModule.default;
1710
+ const checkboxModule = await import('@inquirer/checkbox');
1711
+ const checkbox = checkboxModule.default;
1712
+
1713
+ console.log('\n📂 Category Browse Mode\n');
1714
+
1715
+ const filter = new TemplateFilter(agents);
1716
+ const categoryCatalog = {};
1717
+
1718
+ // Group agents by subcategory
1719
+ agents.forEach(agent => {
1720
+ if (!categoryCatalog[agent.subcategory]) {
1721
+ categoryCatalog[agent.subcategory] = [];
1722
+ }
1723
+ categoryCatalog[agent.subcategory].push(agent);
1724
+ });
1725
+
1726
+ const categories = Object.keys(categoryCatalog).sort();
1727
+
1728
+ if (categories.length === 0) {
1729
+ console.log('No categories found. Using simple selection.');
1730
+ return await simpleListSelection(agents);
1731
+ }
1732
+
1733
+ console.log(`Found ${categories.length} categories:`);
1734
+ categories.forEach(cat => {
1735
+ console.log(` 📂 ${cat} (${categoryCatalog[cat].length} agents)`);
1736
+ });
1737
+
1738
+ const selectedAgents = [];
1739
+
1740
+ while (true) {
1741
+ const action = await select({
1742
+ message: `Select a category to browse (${selectedAgents.length} agents selected):`,
1743
+ choices: [
1744
+ ...categories.map(cat => ({
1745
+ name: `📂 ${cat} (${categoryCatalog[cat].length} agents)`,
1746
+ value: cat
1747
+ })),
1748
+ { name: '✅ Finish selection', value: 'done' }
1749
+ ]
1750
+ });
1751
+
1752
+ if (action === 'done') break;
1753
+
1754
+ const categoryAgents = categoryCatalog[action];
1755
+ const choices = categoryAgents.map(agent => ({
1756
+ name: `${agent.name}\n ${agent.description}`,
1757
+ value: agent,
1758
+ checked: selectedAgents.some(selected => selected.id === agent.id)
1759
+ }));
1760
+
1761
+ const selections = await checkbox({
1762
+ message: `Select agents from ${action}:`,
1763
+ choices,
1764
+ pageSize: 8
1765
+ });
1766
+
1767
+ // Update selected agents for this category
1768
+ selectedAgents = selectedAgents.filter(agent => agent.subcategory !== action);
1769
+ selectedAgents.push(...selections);
1770
+
1771
+ console.log(`Updated selection: ${selectedAgents.length} total agents selected`);
1772
+ }
1773
+
1774
+ return selectedAgents;
1775
+ }
1776
+
1777
+ async function tagBrowseSelection(agents) {
1778
+ const selectModule = await import('@inquirer/select');
1779
+ const select = selectModule.default;
1780
+ const checkboxModule = await import('@inquirer/checkbox');
1781
+ const checkbox = checkboxModule.default;
1782
+
1783
+ console.log('\n🏷️ Tag Browse Mode\n');
1784
+
1785
+ const filter = new TemplateFilter(agents);
1786
+ const tagCloud = filter.getTagsByFrequency();
1787
+
1788
+ if (tagCloud.length === 0) {
1789
+ console.log('No tags found. Using simple selection.');
1790
+ return await simpleListSelection(agents);
1791
+ }
1792
+
1793
+ console.log(`Found ${tagCloud.length} tags. Most popular:`);
1794
+ tagCloud.slice(0, 10).forEach(({ tag, count }) => {
1795
+ console.log(` 🏷️ ${tag} (${count} agents)`);
1796
+ });
1797
+
1798
+ const selectedTags = await checkbox({
1799
+ message: 'Select tags to filter agents:',
1800
+ choices: tagCloud.map(({ tag, count }) => ({
1801
+ name: `${tag} (${count} agents)`,
1802
+ value: tag
1803
+ })),
1804
+ pageSize: 12
1805
+ });
1806
+
1807
+ if (selectedTags.length === 0) {
1808
+ console.log('No tags selected. Using all agents.');
1809
+ return await simpleListSelection(agents);
1810
+ }
1811
+
1812
+ const filteredAgents = filter.byTags(selectedTags).getResults();
1813
+ console.log(`\nFiltered to ${filteredAgents.length} agents with selected tags`);
1814
+
1815
+ if (filteredAgents.length === 0) {
1816
+ console.log('No agents match the selected tags.');
1817
+ return [];
1818
+ }
1819
+
1820
+ return await simpleListSelection(filteredAgents);
1821
+ }
1822
+
1823
+ async function simpleListSelection(agents) {
1824
+ const checkboxModule = await import('@inquirer/checkbox');
1825
+ const checkbox = checkboxModule.default;
1826
+
1827
+ console.log('\n📋 Simple List Selection\n');
1828
+
1829
+ if (agents.length === 0) {
1830
+ console.log('No agents available for selection.');
1831
+ return [];
1832
+ }
1833
+
1834
+ const choices = agents.map(agent => ({
1835
+ name: `${agent.name}\n ${agent.description}`,
1836
+ value: agent,
1837
+ checked: false
1838
+ }));
1839
+
1840
+ const selectedAgents = await checkbox({
1841
+ message: `Select agents (${agents.length} available):`,
1842
+ choices,
1843
+ pageSize: 10
1844
+ });
1845
+
1846
+ return selectedAgents;
1847
+ }
1848
+
515
1849
  async function main() {
516
1850
  try {
517
1851
  // Ensure template directory exists
@@ -519,18 +1853,55 @@ async function main() {
519
1853
  throw new Error(`Template directory not found: ${templateDir}`);
520
1854
  }
521
1855
 
522
- // Handle --agents flag for agent selection only
523
- if (flags.agents) {
1856
+
1857
+ // Add mode selection early (before agent selection)
1858
+ const setupMode = await selectSetupMode();
1859
+
1860
+ if (setupMode === 'install-hooks') {
1861
+ // Install Claude Code hooks
1862
+ await installClaudeHooks();
1863
+ return;
1864
+ }
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
+
1873
+ // Set appropriate flags based on mode
1874
+ flags.scanOnly = true;
1875
+ if (setupMode === 'scan-replace') {
1876
+ flags.mergeStrategy = 'replace';
1877
+ } else {
1878
+ flags.mergeStrategy = 'smart';
1879
+ }
1880
+
1881
+ await scanOnlyMode(flags.mergeStrategy);
1882
+ return;
1883
+ }
1884
+
1885
+ // Handle --agents and --browse flags for agent selection only
1886
+ if (flags.agents || flags.browse) {
524
1887
  console.log('🤖 Interactive Agent Selection\n');
525
- const availableAgents = getAvailableAgents();
526
1888
 
527
- if (availableAgents.length === 0) {
528
- console.log('No agents available for selection.');
529
- process.exit(0);
530
- }
1889
+ let selectedAgentFiles = [];
531
1890
 
532
- // Perform interactive selection
533
- const selectedAgentFiles = await selectAgents(availableAgents);
1891
+ if (flags.browse) {
1892
+ // Use enhanced selection interface
1893
+ selectedAgentFiles = await enhancedAgentSelection();
1894
+ } else {
1895
+ // Use traditional selection
1896
+ const availableAgents = getAvailableAgents();
1897
+
1898
+ if (availableAgents.length === 0) {
1899
+ console.log('No agents available for selection.');
1900
+ process.exit(0);
1901
+ }
1902
+
1903
+ selectedAgentFiles = await selectAgents(availableAgents);
1904
+ }
534
1905
 
535
1906
  if (selectedAgentFiles.length === 0) {
536
1907
  console.log('\n❌ No agents selected.');
@@ -548,13 +1919,20 @@ async function main() {
548
1919
  console.log(`\n${colors.green}${colors.bold}✅ You selected ${selectedAgentFiles.length} agent${selectedAgentFiles.length === 1 ? '' : 's'}:${colors.reset}\n`);
549
1920
 
550
1921
  // Show selected agents with descriptions
551
- selectedAgentFiles.forEach(file => {
552
- const agent = availableAgents.find(a => a.file === file);
553
- if (agent) {
554
- console.log(` ${colors.cyan}${colors.bold}${agent.name}${colors.reset}`);
555
- console.log(` ${colors.dim}${agent.description}${colors.reset}\n`);
556
- }
557
- });
1922
+ if (flags.browse) {
1923
+ // For enhanced selection, we already have agent data
1924
+ console.log(`${colors.dim}Selected files: ${selectedAgentFiles.join(', ')}${colors.reset}\n`);
1925
+ } else {
1926
+ // For traditional selection, show agent details
1927
+ const availableAgents = getAvailableAgents();
1928
+ selectedAgentFiles.forEach(file => {
1929
+ const agent = availableAgents.find(a => a.file === file);
1930
+ if (agent) {
1931
+ console.log(` ${colors.cyan}${colors.bold}${agent.name}${colors.reset}`);
1932
+ console.log(` ${colors.dim}${agent.description}${colors.reset}\n`);
1933
+ }
1934
+ });
1935
+ }
558
1936
 
559
1937
  console.log(`${colors.yellow}${colors.bold}📝 Next Steps:${colors.reset}`);
560
1938
  console.log(`${colors.dim}1. Make sure Claude Code is initialized: ${colors.reset}${colors.cyan}claude init${colors.reset}`);
@@ -590,8 +1968,15 @@ async function main() {
590
1968
  } else {
591
1969
  console.log('⚠️ Claude Code not detected in this project.');
592
1970
  console.log('ccsetup can create the .claude directory structure for you.\n');
593
- const answer = await prompt('Would you like to create .claude directory? (yes/no): ');
594
- if (answer !== 'yes') {
1971
+
1972
+ // Use inquirer instead of readline prompt since rl might be closed
1973
+ const confirmModule = await import('@inquirer/confirm');
1974
+ const confirm = confirmModule.default;
1975
+ const shouldCreate = await confirm({
1976
+ message: 'Would you like to create .claude directory?',
1977
+ default: true
1978
+ });
1979
+ if (!shouldCreate) {
595
1980
  console.log('\nTo manually initialize Claude Code:');
596
1981
  console.log('1. Install Claude Code CLI: https://docs.anthropic.com/claude-code/quickstart');
597
1982
  console.log('2. Run \'claude init\' in your project directory');
@@ -691,19 +2076,27 @@ async function main() {
691
2076
  const select = selectModule.default;
692
2077
 
693
2078
  const agentMode = await select({
694
- message: 'Choose your setup mode:',
2079
+ message: 'Would you like to include AI agents in your project?',
695
2080
  choices: [
696
2081
  {
697
- name: 'Browse Mode - Copy all 50+ agents to /agents folder (explore later)',
698
- value: 'browse'
2082
+ name: ' Select Agents - Choose specific agents for your needs',
2083
+ value: 'select',
2084
+ description: 'Interactive selection of agents based on your project'
2085
+ },
2086
+ {
2087
+ name: '🔍 Enhanced Selection - Advanced browsing with search and filters',
2088
+ value: 'enhanced',
2089
+ description: 'Use the enhanced interface with categories, tags, and search'
699
2090
  },
700
2091
  {
701
- name: 'Select Agents - Choose specific agents to include now',
702
- value: 'select'
2092
+ name: '📚 Copy All Agents - Get all 50+ agents to explore',
2093
+ value: 'browse',
2094
+ description: 'Copies all agents to /agents folder for manual review'
703
2095
  },
704
2096
  {
705
- name: 'Skip - Don\'t include any agents',
706
- value: 'skip'
2097
+ name: '⏭️ Skip Agents - Just set up the basic structure',
2098
+ value: 'skip',
2099
+ description: 'You can always add agents later'
707
2100
  }
708
2101
  ]
709
2102
  });
@@ -721,6 +2114,10 @@ async function main() {
721
2114
  // Interactive selection
722
2115
  selectedAgentFiles = await selectAgents(availableAgents);
723
2116
  console.log(`\n✅ Selected ${selectedAgentFiles.length} agent${selectedAgentFiles.length === 1 ? '' : 's'}`);
2117
+ } else if (agentMode === 'enhanced') {
2118
+ // Enhanced selection with catalog system
2119
+ selectedAgentFiles = await enhancedAgentSelection();
2120
+ console.log(`\n✅ Selected ${selectedAgentFiles.length} agent${selectedAgentFiles.length === 1 ? '' : 's'}`);
724
2121
  }
725
2122
 
726
2123
  // Recreate readline interface after agent selection
@@ -761,6 +2158,11 @@ async function main() {
761
2158
  return; // Don't process .claude directory in regular template scan
762
2159
  }
763
2160
 
2161
+ // Skip hooks directory - hooks should only be installed to .claude/hooks via --install-hooks
2162
+ if (path.basename(src) === 'hooks') {
2163
+ return; // Don't copy hooks to project root
2164
+ }
2165
+
764
2166
  // Skip agents directory if we're handling it separately
765
2167
  if (skipAgents && path.basename(src) === 'agents') {
766
2168
  // Only scan selected agents
@@ -894,6 +2296,29 @@ async function main() {
894
2296
 
895
2297
  scanTemplate(templateDir, targetDir, '', true);
896
2298
 
2299
+ // Handle repository scanning for context
2300
+ let repositoryContext = null;
2301
+ if (flags.scanContext || (!flags.dryRun && projectName === '.' && !flags.force)) {
2302
+ const hasExistingFiles = allItems.some(item => item.exists);
2303
+
2304
+ if (hasExistingFiles || flags.scanContext) {
2305
+ if (flags.scanContext || (!flags.force && await shouldScanRepository())) {
2306
+ repositoryContext = await scanRepositoryForContext(targetDir);
2307
+
2308
+ if (repositoryContext && !flags.dryRun) {
2309
+ const confirmResult = await previewAndConfirmContext(repositoryContext.formattedContext);
2310
+
2311
+ if (confirmResult === 'n' || confirmResult === 'no') {
2312
+ repositoryContext = null;
2313
+ console.log('✅ Skipping context addition to CLAUDE.md');
2314
+ } else if (confirmResult === 'edit') {
2315
+ console.log('📝 Context editing not yet implemented. Using generated context as-is.');
2316
+ }
2317
+ }
2318
+ }
2319
+ }
2320
+ }
2321
+
897
2322
  // Handle force flag
898
2323
  if (flags.force) {
899
2324
  // Set all strategies to overwrite
@@ -933,18 +2358,30 @@ async function main() {
933
2358
  console.log(' 2) rename (r) - Save template files with -ccsetup suffix');
934
2359
  console.log(' 3) overwrite (o) - Replace with template versions');
935
2360
 
936
- const userInput = await prompt(`Your choice for ${category.name} [s/r/o]: `);
937
- const strategy = normalizeConflictStrategy(userInput);
2361
+ // Use inquirer instead of readline prompt
2362
+ const selectModule = await import('@inquirer/select');
2363
+ const select = selectModule.default;
2364
+ const strategy = await select({
2365
+ message: `Your choice for ${category.name}:`,
2366
+ choices: [
2367
+ { name: '(s)kip - Keep your existing files', value: 'skip' },
2368
+ { name: '(r)ename - Create backups and use template versions', value: 'rename' },
2369
+ { name: '(o)verwrite - Replace with template versions', value: 'overwrite' }
2370
+ ],
2371
+ default: 'skip'
2372
+ });
938
2373
 
939
- if (!strategy) {
940
- console.log(`\n❌ Invalid option: "${userInput}". Please use: skip/s/1, rename/r/2, or overwrite/o/3`);
941
- if (rl) rl.close();
942
- process.exit(1);
943
- }
2374
+ // Strategy will always be valid when using select, no need for validation
944
2375
 
945
2376
  if (strategy === 'overwrite' && category.key === 'CLAUDE.md') {
946
- const confirm = await prompt('⚠️ Are you sure you want to overwrite CLAUDE.md? This will lose your project instructions! (yes/no): ');
947
- if (confirm !== 'yes') {
2377
+ // Use inquirer instead of readline prompt
2378
+ const confirmModule = await import('@inquirer/confirm');
2379
+ const confirm = confirmModule.default;
2380
+ const shouldOverwrite = await confirm({
2381
+ message: '⚠️ Are you sure you want to overwrite CLAUDE.md? This will lose your project instructions!',
2382
+ default: false
2383
+ });
2384
+ if (!shouldOverwrite) {
948
2385
  conflictStrategies[category.key] = 'skip';
949
2386
  console.log('Keeping existing CLAUDE.md');
950
2387
  continue;
@@ -1014,10 +2451,28 @@ async function main() {
1014
2451
  console.log(` 📄 ${flags.dryRun ? 'Would create' : 'Created'}: ${path.relative(targetDir, newDest)}`);
1015
2452
  } else if (strategy === 'overwrite') {
1016
2453
  if (!flags.dryRun) {
1017
- fs.copyFileSync(item.src, item.dest);
2454
+ // Handle CLAUDE.md with context injection on overwrite
2455
+ if (item.relativePath === 'CLAUDE.md' && repositoryContext) {
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
+ }
2468
+ } else {
2469
+ if (item.relativePath === 'CLAUDE.md' && repositoryContext) {
2470
+ console.log(` ♻️ Would replace: ${item.relativePath} (with project context)`);
2471
+ } else {
2472
+ console.log(` ♻️ Would replace: ${item.relativePath}`);
2473
+ }
1018
2474
  }
1019
2475
  overwrittenCount++;
1020
- console.log(` ♻️ ${flags.dryRun ? 'Would replace' : 'Replaced'}: ${item.relativePath}`);
1021
2476
  }
1022
2477
  } else {
1023
2478
  if (!flags.dryRun) {
@@ -1026,9 +2481,26 @@ async function main() {
1026
2481
  if (!fs.existsSync(destDir)) {
1027
2482
  fs.mkdirSync(destDir, { recursive: true });
1028
2483
  }
1029
- fs.copyFileSync(item.src, item.dest);
2484
+
2485
+ // Handle CLAUDE.md with context injection
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
+ }
1030
2498
  } else {
1031
- console.log(` ✨ Would copy: ${item.relativePath}`);
2499
+ if (item.relativePath === 'CLAUDE.md' && repositoryContext) {
2500
+ console.log(` ✨ Would copy: ${item.relativePath} (with project context)`);
2501
+ } else {
2502
+ console.log(` ✨ Would copy: ${item.relativePath}`);
2503
+ }
1032
2504
  }
1033
2505
  copiedCount++;
1034
2506
  }
@@ -1082,6 +2554,9 @@ async function main() {
1082
2554
  if (claudeInitResult && claudeInitResult.createdItems.length > 0) {
1083
2555
  console.log(` 📁 ${claudeInitResult.createdItems.length} items created in .claude directory`);
1084
2556
  }
2557
+ if (repositoryContext && !flags.dryRun) {
2558
+ console.log(` 🔍 Project context automatically added to CLAUDE.md`);
2559
+ }
1085
2560
  }
1086
2561
 
1087
2562
  if (!flags.dryRun) {
@@ -1114,6 +2589,87 @@ async function main() {
1114
2589
  console.log(' You can compare them with your existing files or copy sections you need');
1115
2590
  }
1116
2591
 
2592
+ // Ask user if they want the workflow selector hook
2593
+ if (setupMode === 'full' && !flags.dryRun) {
2594
+ const claudeDir = path.join(targetDir, '.claude');
2595
+ if (fs.existsSync(claudeDir)) {
2596
+ try {
2597
+ let wantHook = false;
2598
+
2599
+ if (!flags.force) {
2600
+ const confirmModule = await import('@inquirer/confirm');
2601
+ const confirm = confirmModule.default;
2602
+
2603
+ wantHook = await confirm({
2604
+ message: 'Enable workflow selector hook? (suggests agent workflows per prompt, controlled via CCSETUP_WORKFLOW env var)',
2605
+ default: false
2606
+ });
2607
+ }
2608
+
2609
+ if (wantHook) {
2610
+ // Create hooks directory
2611
+ const hooksDir = path.join(claudeDir, 'hooks');
2612
+ if (!fs.existsSync(hooksDir)) {
2613
+ fs.mkdirSync(hooksDir, { recursive: true });
2614
+ }
2615
+
2616
+ // Copy workflow-selector hook
2617
+ const hookSourceDir = path.join(templateDir, 'hooks', 'workflow-selector');
2618
+ const hookDestDir = path.join(hooksDir, 'workflow-selector');
2619
+
2620
+ if (fs.existsSync(hookSourceDir)) {
2621
+ if (!fs.existsSync(hookDestDir)) {
2622
+ fs.mkdirSync(hookDestDir, { recursive: true });
2623
+ }
2624
+
2625
+ const hookFile = path.join(hookSourceDir, 'index.js');
2626
+ const destFile = path.join(hookDestDir, 'index.js');
2627
+ fs.copyFileSync(hookFile, destFile);
2628
+
2629
+ // Update settings.json
2630
+ const settingsFile = path.join(claudeDir, 'settings.json');
2631
+ let settings = {};
2632
+
2633
+ if (fs.existsSync(settingsFile)) {
2634
+ try {
2635
+ settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8'));
2636
+ } catch (e) {
2637
+ // Start with empty settings
2638
+ }
2639
+ }
2640
+
2641
+ if (!settings.hooks) {
2642
+ settings.hooks = {};
2643
+ }
2644
+
2645
+ settings.hooks.UserPromptSubmit = [
2646
+ {
2647
+ "matcher": ".*",
2648
+ "hooks": [
2649
+ {
2650
+ "type": "command",
2651
+ "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/workflow-selector/index.js"
2652
+ }
2653
+ ]
2654
+ }
2655
+ ];
2656
+
2657
+ fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
2658
+ console.log(' ✅ Workflow selection hook installed');
2659
+ console.log(' 📝 To activate, set the environment variable:');
2660
+ console.log(' export CCSETUP_WORKFLOW=1');
2661
+ console.log(' 💡 The hook suggests workflows and asks before applying them');
2662
+ }
2663
+ } else {
2664
+ console.log(' ⏭️ Skipped workflow selector hook');
2665
+ console.log(' 💡 You can install it later with: npx ccsetup --install-hooks');
2666
+ }
2667
+ } catch (error) {
2668
+ console.warn(' ⚠️ Could not install workflow hook:', error.message);
2669
+ }
2670
+ }
2671
+ }
2672
+
1117
2673
  console.log('\nHappy coding with Claude! 🎉');
1118
2674
  }
1119
2675