ccsetup 1.1.1 → 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.
Files changed (86) hide show
  1. package/README.md +144 -342
  2. package/bin/create-project.js +1246 -90
  3. package/bin/lib/claudeInterface.js +209 -0
  4. package/lib/aiAgentSelector.js +155 -0
  5. package/lib/templates/README.md +176 -0
  6. package/lib/templates/catalog.js +230 -0
  7. package/lib/templates/filter.js +257 -0
  8. package/lib/templates/index.js +45 -0
  9. package/lib/templates/metadata/agents.json +413 -0
  10. package/lib/templates/metadata-extractor.js +329 -0
  11. package/lib/templates/search.js +356 -0
  12. package/package.json +13 -5
  13. package/template/{agents → .claude/agents}/checker.md +29 -0
  14. package/template/.claude/settings.json +32 -0
  15. package/template/.claude/skills/codex-review/SKILL.md +139 -0
  16. package/template/.claude/skills/prd/SKILL.md +343 -0
  17. package/template/.claude/skills/ralph/SKILL.md +339 -0
  18. package/template/.claude/skills/secops/SKILL.md +259 -0
  19. package/template/.codex/skills/codex-review/SKILL.md +139 -0
  20. package/template/.codex/skills/prd/SKILL.md +343 -0
  21. package/template/.codex/skills/ralph/SKILL.md +339 -0
  22. package/template/AGENTS.md +43 -0
  23. package/template/CLAUDE.md +141 -21
  24. package/template/CONTRIBUTING.md +37 -0
  25. package/template/agents/README.md +15 -171
  26. package/template/docs/ROADMAP.md +0 -36
  27. package/template/docs/agent-orchestration.md +24 -141
  28. package/template/docs/codex-setup.md +32 -0
  29. package/template/hooks/codex-review/index.js +105 -0
  30. package/template/hooks/workflow-selector/index.js +398 -0
  31. package/template/scripts/codex-review/codex-review.sh +266 -0
  32. package/template/scripts/ralph/CLAUDE.md +174 -0
  33. package/template/scripts/ralph/CODEX.md +76 -0
  34. package/template/scripts/ralph/ralph.sh +150 -0
  35. package/template/tickets/ticket-list.md +17 -68
  36. package/template/agents/ai-engineer.md +0 -31
  37. package/template/agents/api-documenter.md +0 -31
  38. package/template/agents/architect-review.md +0 -42
  39. package/template/agents/backend-architect.md +0 -29
  40. package/template/agents/business-analyst.md +0 -34
  41. package/template/agents/c-pro.md +0 -34
  42. package/template/agents/cloud-architect.md +0 -31
  43. package/template/agents/code-reviewer.md +0 -28
  44. package/template/agents/content-marketer.md +0 -34
  45. package/template/agents/context-manager.md +0 -63
  46. package/template/agents/cpp-pro.md +0 -37
  47. package/template/agents/customer-support.md +0 -34
  48. package/template/agents/data-engineer.md +0 -31
  49. package/template/agents/data-scientist.md +0 -28
  50. package/template/agents/database-admin.md +0 -31
  51. package/template/agents/database-optimizer.md +0 -31
  52. package/template/agents/debugger.md +0 -29
  53. package/template/agents/deployment-engineer.md +0 -31
  54. package/template/agents/devops-troubleshooter.md +0 -31
  55. package/template/agents/dx-optimizer.md +0 -62
  56. package/template/agents/error-detective.md +0 -31
  57. package/template/agents/frontend-developer.md +0 -30
  58. package/template/agents/golang-pro.md +0 -31
  59. package/template/agents/graphql-architect.md +0 -31
  60. package/template/agents/incident-responder.md +0 -73
  61. package/template/agents/javascript-pro.md +0 -34
  62. package/template/agents/legacy-modernizer.md +0 -31
  63. package/template/agents/ml-engineer.md +0 -31
  64. package/template/agents/mlops-engineer.md +0 -56
  65. package/template/agents/mobile-developer.md +0 -31
  66. package/template/agents/network-engineer.md +0 -31
  67. package/template/agents/payment-integration.md +0 -31
  68. package/template/agents/performance-engineer.md +0 -31
  69. package/template/agents/prompt-engineer.md +0 -58
  70. package/template/agents/python-pro.md +0 -31
  71. package/template/agents/quant-analyst.md +0 -31
  72. package/template/agents/risk-manager.md +0 -40
  73. package/template/agents/rust-pro.md +0 -34
  74. package/template/agents/sales-automator.md +0 -34
  75. package/template/agents/search-specialist.md +0 -58
  76. package/template/agents/security-auditor.md +0 -31
  77. package/template/agents/sql-pro.md +0 -34
  78. package/template/agents/terraform-specialist.md +0 -34
  79. package/template/agents/test-automator.md +0 -31
  80. /package/template/{agents → .claude/agents}/backend.md +0 -0
  81. /package/template/{agents → .claude/agents}/blockchain.md +0 -0
  82. /package/template/{agents → .claude/agents}/coder.md +0 -0
  83. /package/template/{agents → .claude/agents}/frontend.md +0 -0
  84. /package/template/{agents → .claude/agents}/planner.md +0 -0
  85. /package/template/{agents → .claude/agents}/researcher.md +0 -0
  86. /package/template/{agents → .claude/agents}/shadcn.md +0 -0
@@ -3,9 +3,13 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
  const readline = require('readline');
6
+ const TemplateCatalog = require('../lib/templates/catalog');
7
+ const TemplateFilter = require('../lib/templates/filter');
8
+ const TemplateSearch = require('../lib/templates/search');
6
9
 
7
10
  // Parse CLI arguments
8
11
  const args = process.argv.slice(2);
12
+
9
13
  const flags = {
10
14
  force: false,
11
15
  dryRun: false,
@@ -13,7 +17,9 @@ const flags = {
13
17
  allAgents: false,
14
18
  noAgents: false,
15
19
  agents: false,
16
- browseAgents: false
20
+ browseAgents: false,
21
+ browse: false,
22
+ prompt: null
17
23
  };
18
24
 
19
25
  let projectName = '.';
@@ -35,6 +41,10 @@ for (let i = 0; i < args.length; i++) {
35
41
  flags.agents = true;
36
42
  } else if (arg === '--browse-agents') {
37
43
  flags.browseAgents = true;
44
+ } else if (arg === '--browse') {
45
+ flags.browse = true;
46
+ } else if (arg === '--install-hooks') {
47
+ flags.installHooks = true;
38
48
  } else if (!arg.startsWith('-')) {
39
49
  projectName = arg;
40
50
  }
@@ -45,23 +55,27 @@ if (flags.help) {
45
55
  console.log(`
46
56
  Usage: ccsetup [project-name] [options]
47
57
 
58
+ Commands:
59
+ ccsetup Interactive mode
60
+ ccsetup <name> Create a new Claude Code project
61
+
48
62
  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
63
+ --force, -f Skip all prompts and overwrite existing files
64
+ --dry-run, -d Show what would be done without making changes
65
+ --agents Interactive agent selection mode
66
+ --all-agents Include all agents without prompting
67
+ --no-agents Skip agent selection entirely
68
+ --browse-agents Copy all agents to /agents folder for browsing
69
+ --browse Enhanced template browsing and selection interface
70
+ --help, -h Show this help message
71
+
72
+ Advanced:
73
+ --install-hooks Install workflow selection hook to .claude/hooks (optional, power users only)
56
74
 
57
75
  Examples:
58
- ccsetup # Create in current directory
59
- 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
63
- ccsetup my-app --all-agents # Include all agents automatically
64
- ccsetup --browse-agents # Copy all agents for manual selection
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
65
79
  `);
66
80
  process.exit(0);
67
81
  }
@@ -83,16 +97,26 @@ function validateProjectName(name) {
83
97
  }
84
98
 
85
99
  // 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);
89
- }
100
+ function validateFlags() {
101
+ // Existing validations
102
+ if (flags.allAgents && flags.noAgents) {
103
+ console.error('Error: Cannot use --all-agents and --no-agents together');
104
+ process.exit(1);
105
+ }
90
106
 
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);
107
+ if (flags.browseAgents && (flags.allAgents || flags.noAgents || flags.agents || flags.browse)) {
108
+ console.error('Error: --browse-agents cannot be used with other agent flags');
109
+ process.exit(1);
110
+ }
111
+
112
+ if (flags.browse && (flags.allAgents || flags.noAgents || flags.browseAgents)) {
113
+ console.error('Error: --browse cannot be used with other agent flags except --agents');
114
+ process.exit(1);
115
+ }
94
116
  }
95
117
 
118
+ validateFlags();
119
+
96
120
  // Validate the project name
97
121
  if (projectName !== '.') {
98
122
  try {
@@ -105,6 +129,7 @@ if (projectName !== '.') {
105
129
 
106
130
  const targetDir = path.resolve(process.cwd(), projectName);
107
131
  const templateDir = path.join(__dirname, '..', 'template');
132
+ const VALID_AI_PROVIDERS = new Set(['claude', 'codex', 'both']);
108
133
 
109
134
  // Create readline interface only if needed
110
135
  let rl = null;
@@ -155,6 +180,72 @@ function validateAgentFile(file) {
155
180
  return true;
156
181
  }
157
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
+
158
249
  // Function to check if Claude Code is installed
159
250
  function checkClaudeCode() {
160
251
  const claudeDir = path.join(targetDir, '.claude');
@@ -254,6 +345,23 @@ async function initializeClaudeDirectory(selectedAgentFiles, conflictStrategy, d
254
345
  }
255
346
  }
256
347
 
348
+ // Copy .claude/settings.json
349
+ const settingsSrc = path.join(templateClaudeDir, 'settings.json');
350
+ const settingsDest = path.join(claudeDir, 'settings.json');
351
+ if (fs.existsSync(settingsSrc)) {
352
+ if (!fs.existsSync(settingsDest)) {
353
+ if (!dryRun) {
354
+ fs.copyFileSync(settingsSrc, settingsDest);
355
+ }
356
+ createdItems.push('.claude/settings.json');
357
+ if (dryRun) {
358
+ console.log(' ✨ Would copy: .claude/settings.json');
359
+ }
360
+ } else {
361
+ skippedItems.push('.claude/settings.json');
362
+ }
363
+ }
364
+
257
365
  // Copy .claude/agents/README.md
258
366
  const agentsReadmeSrc = path.join(templateClaudeDir, 'agents', 'README.md');
259
367
  const agentsReadmeDest = path.join(claudeAgentsDir, 'README.md');
@@ -303,8 +411,68 @@ async function initializeClaudeDirectory(selectedAgentFiles, conflictStrategy, d
303
411
  }
304
412
  }
305
413
 
414
+ // Copy .claude/skills/ directory (recursively)
415
+ const templateSkillsDir = path.join(templateClaudeDir, 'skills');
416
+ if (fs.existsSync(templateSkillsDir)) {
417
+ const claudeSkillsDir = path.join(claudeDir, 'skills');
418
+ const skillDirs = fs.readdirSync(templateSkillsDir).filter(d => {
419
+ return fs.statSync(path.join(templateSkillsDir, d)).isDirectory();
420
+ });
421
+
422
+ for (const skillName of skillDirs) {
423
+ const skillSrcDir = path.join(templateSkillsDir, skillName);
424
+ const skillDestDir = path.join(claudeSkillsDir, skillName);
425
+ const skillFile = path.join(skillSrcDir, 'SKILL.md');
426
+
427
+ if (fs.existsSync(skillFile)) {
428
+ if (!fs.existsSync(path.join(skillDestDir, 'SKILL.md'))) {
429
+ if (!dryRun) {
430
+ fs.mkdirSync(skillDestDir, { recursive: true });
431
+ fs.copyFileSync(skillFile, path.join(skillDestDir, 'SKILL.md'));
432
+ }
433
+ createdItems.push(`.claude/skills/${skillName}/SKILL.md`);
434
+ if (dryRun) {
435
+ console.log(` ✨ Would copy: .claude/skills/${skillName}/SKILL.md`);
436
+ }
437
+ } else {
438
+ skippedItems.push(`.claude/skills/${skillName}/SKILL.md`);
439
+ }
440
+ }
441
+ }
442
+ }
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
+
306
474
  // Copy selected agents to .claude/agents
307
- const templateAgentsDir = path.join(templateDir, 'agents');
475
+ const templateAgentsDir = path.join(templateDir, '.claude', 'agents');
308
476
  let copiedAgents = 0;
309
477
  let skippedAgents = 0;
310
478
 
@@ -391,6 +559,126 @@ async function initializeClaudeDirectory(selectedAgentFiles, conflictStrategy, d
391
559
  }
392
560
  }
393
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
+
394
682
  // Function to parse agent frontmatter
395
683
  function parseAgentFrontmatter(filePath) {
396
684
  try {
@@ -426,7 +714,7 @@ function parseAgentFrontmatter(filePath) {
426
714
 
427
715
  // Function to get available agents
428
716
  function getAvailableAgents() {
429
- const agentsDir = path.join(templateDir, 'agents');
717
+ const agentsDir = path.join(templateDir, '.claude', 'agents');
430
718
  const agents = [];
431
719
 
432
720
  try {
@@ -451,6 +739,274 @@ function getAvailableAgents() {
451
739
  return agents.sort((a, b) => a.name.localeCompare(b.name));
452
740
  }
453
741
 
742
+ // Install Claude Code hooks
743
+ async function installClaudeHooks() {
744
+ console.log('\n🪝 Installing Claude Code Workflow Selection Hook...\n');
745
+
746
+ const claudeDir = path.join(process.cwd(), '.claude');
747
+ const hooksDir = path.join(claudeDir, 'hooks');
748
+ const settingsFile = path.join(claudeDir, 'settings.json');
749
+
750
+ // Check if .claude directory exists
751
+ if (!fs.existsSync(claudeDir)) {
752
+ console.error('❌ Error: .claude directory not found.');
753
+ console.log('\n💡 Please run "claude init" first to initialize Claude Code in this directory.\n');
754
+ return;
755
+ }
756
+
757
+ try {
758
+ // Create hooks directory
759
+ if (!fs.existsSync(hooksDir)) {
760
+ fs.mkdirSync(hooksDir, { recursive: true });
761
+ console.log('✅ Created .claude/hooks directory');
762
+ }
763
+
764
+ // Copy workflow-selector hook
765
+ const hookSourceDir = path.join(templateDir, 'hooks', 'workflow-selector');
766
+ const hookDestDir = path.join(hooksDir, 'workflow-selector');
767
+
768
+ if (!fs.existsSync(hookSourceDir)) {
769
+ console.error('❌ Error: Hook source files not found in template.');
770
+ return;
771
+ }
772
+
773
+ // Check if hook already exists
774
+ const hookFile = path.join(hookSourceDir, 'index.js');
775
+ const destFile = path.join(hookDestDir, 'index.js');
776
+ let shouldCopyHook = true;
777
+
778
+ if (fs.existsSync(destFile)) {
779
+ console.log('⚠️ Workflow-selector hook already exists.');
780
+ const selectModule = await import('@inquirer/select');
781
+ const select = selectModule.default;
782
+
783
+ const action = await select({
784
+ message: 'How would you like to proceed?',
785
+ choices: [
786
+ {
787
+ name: '📋 Keep existing - Preserve your customizations',
788
+ value: 'keep',
789
+ description: 'Keep your existing hook file unchanged'
790
+ },
791
+ {
792
+ name: '🔄 Update - Replace with latest version',
793
+ value: 'replace',
794
+ description: 'Replace with the latest hook (backup will be created)'
795
+ },
796
+ {
797
+ name: '👀 Compare - View differences first',
798
+ value: 'compare',
799
+ description: 'Compare existing and new versions before deciding'
800
+ },
801
+ {
802
+ name: '❌ Skip - Cancel hook installation',
803
+ value: 'skip',
804
+ description: 'Skip installing the hook file'
805
+ }
806
+ ]
807
+ });
808
+
809
+ if (action === 'skip') {
810
+ console.log('⏭️ Skipped hook file installation');
811
+ shouldCopyHook = false;
812
+ } else if (action === 'keep') {
813
+ console.log('✅ Keeping existing hook file');
814
+ shouldCopyHook = false;
815
+ } else if (action === 'compare') {
816
+ // Show comparison
817
+ console.log('\n📄 Existing hook file summary:');
818
+ const existingContent = fs.readFileSync(destFile, 'utf8');
819
+ const existingLines = existingContent.split('\n').length;
820
+ console.log(` Lines: ${existingLines}`);
821
+ console.log(` Size: ${fs.statSync(destFile).size} bytes`);
822
+ console.log(` Modified: ${fs.statSync(destFile).mtime.toLocaleString()}`);
823
+
824
+ const confirmModule = await import('@inquirer/confirm');
825
+ const confirm = confirmModule.default;
826
+ const shouldReplace = await confirm({
827
+ message: 'Replace with new version?',
828
+ default: false
829
+ });
830
+
831
+ if (shouldReplace) {
832
+ // Create backup
833
+ const backupFile = destFile + '.backup-' + Date.now();
834
+ fs.copyFileSync(destFile, backupFile);
835
+ console.log(`✅ Created backup: ${path.basename(backupFile)}`);
836
+ shouldCopyHook = true;
837
+ } else {
838
+ shouldCopyHook = false;
839
+ }
840
+ } else if (action === 'replace') {
841
+ // Create backup
842
+ const backupFile = destFile + '.backup-' + Date.now();
843
+ fs.copyFileSync(destFile, backupFile);
844
+ console.log(`✅ Created backup: ${path.basename(backupFile)}`);
845
+ shouldCopyHook = true;
846
+ }
847
+ }
848
+
849
+ // Create workflow-selector directory if needed
850
+ if (!fs.existsSync(hookDestDir)) {
851
+ fs.mkdirSync(hookDestDir, { recursive: true });
852
+ }
853
+
854
+ // Copy hook file if needed
855
+ if (shouldCopyHook) {
856
+ fs.copyFileSync(hookFile, destFile);
857
+ console.log('✅ Installed workflow-selector hook');
858
+ }
859
+
860
+ // Update settings.json
861
+ let settings = {};
862
+ if (fs.existsSync(settingsFile)) {
863
+ const content = fs.readFileSync(settingsFile, 'utf8');
864
+ try {
865
+ settings = JSON.parse(content);
866
+ } catch (e) {
867
+ console.warn('⚠️ Warning: Could not parse existing settings.json, creating new one');
868
+ }
869
+ }
870
+
871
+ // Add hook configuration intelligently
872
+ if (!settings.hooks) {
873
+ settings.hooks = {};
874
+ }
875
+
876
+ // Check if UserPromptSubmit hooks already exist
877
+ const workflowHookCommand = "node $CLAUDE_PROJECT_DIR/.claude/hooks/workflow-selector/index.js";
878
+ let hookExists = false;
879
+
880
+ if (settings.hooks.UserPromptSubmit && Array.isArray(settings.hooks.UserPromptSubmit)) {
881
+ // Check if our hook is already configured
882
+ hookExists = settings.hooks.UserPromptSubmit.some(hookConfig =>
883
+ hookConfig.hooks && hookConfig.hooks.some(hook =>
884
+ hook.type === 'command' && hook.command === workflowHookCommand
885
+ )
886
+ );
887
+
888
+ if (hookExists) {
889
+ console.log('✅ Workflow hook already configured in settings.json');
890
+ } else {
891
+ // Ask user how to proceed
892
+ const selectModule = await import('@inquirer/select');
893
+ const select = selectModule.default;
894
+
895
+ console.log('\n⚠️ Existing UserPromptSubmit hooks detected in settings.json');
896
+ const action = await select({
897
+ message: 'How would you like to add the workflow hook?',
898
+ choices: [
899
+ {
900
+ name: '➕ Add to existing - Preserve current hooks and add workflow hook',
901
+ value: 'add',
902
+ description: 'Keep all existing hooks and add the workflow hook'
903
+ },
904
+ {
905
+ name: '🔄 Replace all - Replace existing hooks with workflow hook',
906
+ value: 'replace',
907
+ description: 'Replace all existing hooks (backup will be created)'
908
+ },
909
+ {
910
+ name: '❌ Skip - Don\'t modify hooks configuration',
911
+ value: 'skip',
912
+ description: 'Keep settings.json unchanged'
913
+ }
914
+ ]
915
+ });
916
+
917
+ if (action === 'add') {
918
+ // Add our hook to existing array
919
+ settings.hooks.UserPromptSubmit.push({
920
+ "matcher": ".*",
921
+ "hooks": [
922
+ {
923
+ "type": "command",
924
+ "command": workflowHookCommand
925
+ }
926
+ ]
927
+ });
928
+ console.log('✅ Added workflow hook to existing hooks');
929
+ } else if (action === 'replace') {
930
+ // Backup existing settings
931
+ const backupFile = settingsFile + '.backup-' + Date.now();
932
+ fs.writeFileSync(backupFile, JSON.stringify(settings, null, 2));
933
+ console.log(`✅ Created settings backup: ${path.basename(backupFile)}`);
934
+
935
+ // Replace with our hook
936
+ settings.hooks.UserPromptSubmit = [
937
+ {
938
+ "matcher": ".*",
939
+ "hooks": [
940
+ {
941
+ "type": "command",
942
+ "command": workflowHookCommand
943
+ }
944
+ ]
945
+ }
946
+ ];
947
+ console.log('✅ Replaced existing hooks with workflow hook');
948
+ } else {
949
+ console.log('⏭️ Skipped settings.json modification');
950
+ return;
951
+ }
952
+ }
953
+ } else {
954
+ // No existing UserPromptSubmit hooks, safe to add
955
+ settings.hooks.UserPromptSubmit = [
956
+ {
957
+ "matcher": ".*",
958
+ "hooks": [
959
+ {
960
+ "type": "command",
961
+ "command": workflowHookCommand
962
+ }
963
+ ]
964
+ }
965
+ ];
966
+ console.log('✅ Added workflow hook configuration');
967
+ }
968
+
969
+ // Write updated settings only if we made changes
970
+ if (!hookExists) {
971
+ fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
972
+ console.log('✅ Updated .claude/settings.json');
973
+ }
974
+
975
+ // Copy agent-orchestration.md if it doesn't exist
976
+ const orchestrationSource = path.join(templateDir, 'docs', 'agent-orchestration.md');
977
+ const orchestrationDest = path.join(process.cwd(), 'docs', 'agent-orchestration.md');
978
+
979
+ if (!fs.existsSync(orchestrationDest) && fs.existsSync(orchestrationSource)) {
980
+ const docsDir = path.join(process.cwd(), 'docs');
981
+ if (!fs.existsSync(docsDir)) {
982
+ fs.mkdirSync(docsDir, { recursive: true });
983
+ }
984
+ fs.copyFileSync(orchestrationSource, orchestrationDest);
985
+ console.log('✅ Copied agent-orchestration.md to docs/');
986
+ }
987
+
988
+ console.log('\n🎉 Workflow selection hook installed successfully!\n');
989
+ console.log('The hook will:');
990
+ console.log(' • Analyze your prompts to determine task type');
991
+ console.log(' • Suggest appropriate workflows (Feature Development, Bug Fix, etc.)');
992
+ console.log(' • Generate relevant todo items');
993
+ console.log(' • Guide agent selection based on the task\n');
994
+ console.log('📝 Note: The hook reads workflows from docs/agent-orchestration.md');
995
+ console.log('💡 You can disable the hook by editing .claude/settings.json\n');
996
+
997
+ } catch (error) {
998
+ console.error('❌ Error installing hooks:', error.message);
999
+ }
1000
+ }
1001
+
1002
+ // Setup mode selection function
1003
+ async function selectSetupMode() {
1004
+ if (flags.installHooks) return 'install-hooks';
1005
+ if (flags.agents) return 'agents-only';
1006
+ return 'full';
1007
+ }
1008
+
1009
+
454
1010
  // Dynamic import for ESM module
455
1011
  async function importCheckbox() {
456
1012
  try {
@@ -512,6 +1068,341 @@ async function selectAgents(availableAgents) {
512
1068
  return validatedFiles;
513
1069
  }
514
1070
 
1071
+ async function enhancedAgentSelection() {
1072
+ try {
1073
+ console.log('\n🔍 Loading template catalog...');
1074
+ const catalog = TemplateCatalog.createInstance();
1075
+ const templates = await catalog.load();
1076
+
1077
+ if (!templates.agents || templates.agents.length === 0) {
1078
+ console.log('❌ No agents found in catalog. Falling back to basic selection.');
1079
+ return await selectAgents(getAvailableAgents());
1080
+ }
1081
+
1082
+ console.log(`✅ Found ${templates.agents.length} agents in catalog\n`);
1083
+
1084
+ // Import select for mode selection
1085
+ const selectModule = await import('@inquirer/select');
1086
+ const select = selectModule.default;
1087
+
1088
+ const mode = await select({
1089
+ message: 'How would you like to browse and select agents?',
1090
+ choices: [
1091
+ {
1092
+ name: '🔍 Search & Filter - Find agents by keywords and categories',
1093
+ value: 'search',
1094
+ description: 'Use search and filtering to find exactly what you need'
1095
+ },
1096
+ {
1097
+ name: '📂 Browse by Category - Explore agents organized by type',
1098
+ value: 'category',
1099
+ description: 'Browse agents grouped by their specialization'
1100
+ },
1101
+ {
1102
+ name: '🏷️ Browse by Tags - Find agents with specific capabilities',
1103
+ value: 'tags',
1104
+ description: 'Explore agents based on their tags and features'
1105
+ },
1106
+ {
1107
+ name: '📋 Simple List - Traditional selection from all agents',
1108
+ value: 'simple',
1109
+ description: 'Classic checkbox selection from complete agent list'
1110
+ }
1111
+ ]
1112
+ });
1113
+
1114
+ let selectedAgents = [];
1115
+
1116
+ switch (mode) {
1117
+ case 'search':
1118
+ selectedAgents = await searchAndFilterSelection(templates.agents);
1119
+ break;
1120
+ case 'category':
1121
+ selectedAgents = await categoryBrowseSelection(templates.agents);
1122
+ break;
1123
+ case 'tags':
1124
+ selectedAgents = await tagBrowseSelection(templates.agents);
1125
+ break;
1126
+ case 'simple':
1127
+ default:
1128
+ selectedAgents = await simpleListSelection(templates.agents);
1129
+ break;
1130
+ }
1131
+
1132
+ return selectedAgents.map(agent => agent.files[0]).filter(file => file && validateAgentFile(file));
1133
+
1134
+ } catch (error) {
1135
+ console.warn(`⚠️ Enhanced selection failed: ${error.message}`);
1136
+ console.log('Falling back to basic agent selection...\n');
1137
+ return await selectAgents(getAvailableAgents());
1138
+ }
1139
+ }
1140
+
1141
+ async function searchAndFilterSelection(agents) {
1142
+ const inputModule = await import('@inquirer/input');
1143
+ const input = inputModule.default;
1144
+ const selectModule = await import('@inquirer/select');
1145
+ const select = selectModule.default;
1146
+ const checkboxModule = await import('@inquirer/checkbox');
1147
+ const checkbox = checkboxModule.default;
1148
+
1149
+ console.log('\n🔍 Search & Filter Mode\n');
1150
+
1151
+ let currentResults = agents;
1152
+ const selectedAgents = [];
1153
+
1154
+ while (true) {
1155
+ console.log(`\n📊 Current results: ${currentResults.length} agents`);
1156
+
1157
+ const action = await select({
1158
+ message: 'What would you like to do?',
1159
+ choices: [
1160
+ { name: '🔍 Search by keyword', value: 'search' },
1161
+ { name: '📂 Filter by category', value: 'category' },
1162
+ { name: '🏷️ Filter by tags', value: 'tags' },
1163
+ { name: '📋 Select from current results', value: 'select' },
1164
+ { name: '🔄 Reset filters', value: 'reset' },
1165
+ { name: '✅ Finish selection', value: 'done' }
1166
+ ]
1167
+ });
1168
+
1169
+ if (action === 'done') break;
1170
+
1171
+ if (action === 'reset') {
1172
+ currentResults = agents;
1173
+ continue;
1174
+ }
1175
+
1176
+ if (action === 'search') {
1177
+ const query = await input({
1178
+ message: 'Enter search terms:',
1179
+ validate: (input) => input.trim().length > 0 ? true : 'Please enter a search term'
1180
+ });
1181
+
1182
+ const search = new TemplateSearch(currentResults);
1183
+ currentResults = search.search(query.trim()).getResults();
1184
+ console.log(`Found ${currentResults.length} agents matching "${query}"`);
1185
+ continue;
1186
+ }
1187
+
1188
+ if (action === 'category') {
1189
+ const filter = new TemplateFilter(currentResults);
1190
+ const categories = filter.getSubcategories();
1191
+
1192
+ if (categories.length === 0) {
1193
+ console.log('No categories available in current results.');
1194
+ continue;
1195
+ }
1196
+
1197
+ const category = await select({
1198
+ message: 'Select category:',
1199
+ choices: [
1200
+ { name: 'All categories', value: 'all' },
1201
+ ...categories.map(cat => ({ name: cat, value: cat }))
1202
+ ]
1203
+ });
1204
+
1205
+ currentResults = filter.byCategory(category).getResults();
1206
+ console.log(`Filtered to ${currentResults.length} agents in category "${category}"`);
1207
+ continue;
1208
+ }
1209
+
1210
+ if (action === 'tags') {
1211
+ const filter = new TemplateFilter(currentResults);
1212
+ const tagCloud = filter.getTagsByFrequency();
1213
+
1214
+ if (tagCloud.length === 0) {
1215
+ console.log('No tags available in current results.');
1216
+ continue;
1217
+ }
1218
+
1219
+ const tags = await checkbox({
1220
+ message: 'Select tags to filter by:',
1221
+ choices: tagCloud.slice(0, 15).map(({ tag, count }) => ({
1222
+ name: `${tag} (${count})`,
1223
+ value: tag
1224
+ })),
1225
+ pageSize: 10
1226
+ });
1227
+
1228
+ if (tags.length > 0) {
1229
+ currentResults = filter.byTags(tags).getResults();
1230
+ console.log(`Filtered to ${currentResults.length} agents with tags: ${tags.join(', ')}`);
1231
+ }
1232
+ continue;
1233
+ }
1234
+
1235
+ if (action === 'select') {
1236
+ if (currentResults.length === 0) {
1237
+ console.log('No agents in current results to select from.');
1238
+ continue;
1239
+ }
1240
+
1241
+ const choices = currentResults.map(agent => ({
1242
+ name: `${agent.name}\n ${agent.description}`,
1243
+ value: agent,
1244
+ checked: selectedAgents.some(selected => selected.id === agent.id)
1245
+ }));
1246
+
1247
+ const newSelections = await checkbox({
1248
+ message: `Select agents (${currentResults.length} available):`,
1249
+ choices,
1250
+ pageSize: 8
1251
+ });
1252
+
1253
+ // Update selected agents
1254
+ selectedAgents.length = 0;
1255
+ selectedAgents.push(...newSelections);
1256
+
1257
+ console.log(`Selected ${selectedAgents.length} agents`);
1258
+ }
1259
+ }
1260
+
1261
+ return selectedAgents;
1262
+ }
1263
+
1264
+ async function categoryBrowseSelection(agents) {
1265
+ const selectModule = await import('@inquirer/select');
1266
+ const select = selectModule.default;
1267
+ const checkboxModule = await import('@inquirer/checkbox');
1268
+ const checkbox = checkboxModule.default;
1269
+
1270
+ console.log('\n📂 Category Browse Mode\n');
1271
+
1272
+ const filter = new TemplateFilter(agents);
1273
+ const categoryCatalog = {};
1274
+
1275
+ // Group agents by subcategory
1276
+ agents.forEach(agent => {
1277
+ if (!categoryCatalog[agent.subcategory]) {
1278
+ categoryCatalog[agent.subcategory] = [];
1279
+ }
1280
+ categoryCatalog[agent.subcategory].push(agent);
1281
+ });
1282
+
1283
+ const categories = Object.keys(categoryCatalog).sort();
1284
+
1285
+ if (categories.length === 0) {
1286
+ console.log('No categories found. Using simple selection.');
1287
+ return await simpleListSelection(agents);
1288
+ }
1289
+
1290
+ console.log(`Found ${categories.length} categories:`);
1291
+ categories.forEach(cat => {
1292
+ console.log(` 📂 ${cat} (${categoryCatalog[cat].length} agents)`);
1293
+ });
1294
+
1295
+ const selectedAgents = [];
1296
+
1297
+ while (true) {
1298
+ const action = await select({
1299
+ message: `Select a category to browse (${selectedAgents.length} agents selected):`,
1300
+ choices: [
1301
+ ...categories.map(cat => ({
1302
+ name: `📂 ${cat} (${categoryCatalog[cat].length} agents)`,
1303
+ value: cat
1304
+ })),
1305
+ { name: '✅ Finish selection', value: 'done' }
1306
+ ]
1307
+ });
1308
+
1309
+ if (action === 'done') break;
1310
+
1311
+ const categoryAgents = categoryCatalog[action];
1312
+ const choices = categoryAgents.map(agent => ({
1313
+ name: `${agent.name}\n ${agent.description}`,
1314
+ value: agent,
1315
+ checked: selectedAgents.some(selected => selected.id === agent.id)
1316
+ }));
1317
+
1318
+ const selections = await checkbox({
1319
+ message: `Select agents from ${action}:`,
1320
+ choices,
1321
+ pageSize: 8
1322
+ });
1323
+
1324
+ // Update selected agents for this category
1325
+ selectedAgents = selectedAgents.filter(agent => agent.subcategory !== action);
1326
+ selectedAgents.push(...selections);
1327
+
1328
+ console.log(`Updated selection: ${selectedAgents.length} total agents selected`);
1329
+ }
1330
+
1331
+ return selectedAgents;
1332
+ }
1333
+
1334
+ async function tagBrowseSelection(agents) {
1335
+ const selectModule = await import('@inquirer/select');
1336
+ const select = selectModule.default;
1337
+ const checkboxModule = await import('@inquirer/checkbox');
1338
+ const checkbox = checkboxModule.default;
1339
+
1340
+ console.log('\n🏷️ Tag Browse Mode\n');
1341
+
1342
+ const filter = new TemplateFilter(agents);
1343
+ const tagCloud = filter.getTagsByFrequency();
1344
+
1345
+ if (tagCloud.length === 0) {
1346
+ console.log('No tags found. Using simple selection.');
1347
+ return await simpleListSelection(agents);
1348
+ }
1349
+
1350
+ console.log(`Found ${tagCloud.length} tags. Most popular:`);
1351
+ tagCloud.slice(0, 10).forEach(({ tag, count }) => {
1352
+ console.log(` 🏷️ ${tag} (${count} agents)`);
1353
+ });
1354
+
1355
+ const selectedTags = await checkbox({
1356
+ message: 'Select tags to filter agents:',
1357
+ choices: tagCloud.map(({ tag, count }) => ({
1358
+ name: `${tag} (${count} agents)`,
1359
+ value: tag
1360
+ })),
1361
+ pageSize: 12
1362
+ });
1363
+
1364
+ if (selectedTags.length === 0) {
1365
+ console.log('No tags selected. Using all agents.');
1366
+ return await simpleListSelection(agents);
1367
+ }
1368
+
1369
+ const filteredAgents = filter.byTags(selectedTags).getResults();
1370
+ console.log(`\nFiltered to ${filteredAgents.length} agents with selected tags`);
1371
+
1372
+ if (filteredAgents.length === 0) {
1373
+ console.log('No agents match the selected tags.');
1374
+ return [];
1375
+ }
1376
+
1377
+ return await simpleListSelection(filteredAgents);
1378
+ }
1379
+
1380
+ async function simpleListSelection(agents) {
1381
+ const checkboxModule = await import('@inquirer/checkbox');
1382
+ const checkbox = checkboxModule.default;
1383
+
1384
+ console.log('\n📋 Simple List Selection\n');
1385
+
1386
+ if (agents.length === 0) {
1387
+ console.log('No agents available for selection.');
1388
+ return [];
1389
+ }
1390
+
1391
+ const choices = agents.map(agent => ({
1392
+ name: `${agent.name}\n ${agent.description}`,
1393
+ value: agent,
1394
+ checked: false
1395
+ }));
1396
+
1397
+ const selectedAgents = await checkbox({
1398
+ message: `Select agents (${agents.length} available):`,
1399
+ choices,
1400
+ pageSize: 10
1401
+ });
1402
+
1403
+ return selectedAgents;
1404
+ }
1405
+
515
1406
  async function main() {
516
1407
  try {
517
1408
  // Ensure template directory exists
@@ -519,18 +1410,53 @@ async function main() {
519
1410
  throw new Error(`Template directory not found: ${templateDir}`);
520
1411
  }
521
1412
 
522
- // Handle --agents flag for agent selection only
523
- if (flags.agents) {
524
- console.log('🤖 Interactive Agent Selection\n');
525
- const availableAgents = getAvailableAgents();
526
-
527
- if (availableAgents.length === 0) {
528
- console.log('No agents available for selection.');
1413
+
1414
+ // Add mode selection early (before agent selection)
1415
+ const setupMode = await selectSetupMode();
1416
+
1417
+ if (setupMode === 'install-hooks') {
1418
+ // Install Claude Code hooks
1419
+ await installClaudeHooks();
1420
+ return;
1421
+ }
1422
+
1423
+ if (!flags.force && !flags.dryRun && rl) {
1424
+ rl.close();
1425
+ rl = null;
1426
+ }
1427
+
1428
+ const aiProvider = await selectAiProvider();
1429
+
1430
+ // Handle --agents and --browse flags for agent selection only
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');
529
1435
  process.exit(0);
530
1436
  }
1437
+
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
+ }
531
1443
 
532
- // Perform interactive selection
533
- const selectedAgentFiles = await selectAgents(availableAgents);
1444
+ let selectedAgentFiles = [];
1445
+
1446
+ if (flags.browse) {
1447
+ // Use enhanced selection interface
1448
+ selectedAgentFiles = await enhancedAgentSelection();
1449
+ } else {
1450
+ // Use traditional selection
1451
+ const availableAgents = getAvailableAgents();
1452
+
1453
+ if (availableAgents.length === 0) {
1454
+ console.log('No agents available for selection.');
1455
+ process.exit(0);
1456
+ }
1457
+
1458
+ selectedAgentFiles = await selectAgents(availableAgents);
1459
+ }
534
1460
 
535
1461
  if (selectedAgentFiles.length === 0) {
536
1462
  console.log('\n❌ No agents selected.');
@@ -548,13 +1474,20 @@ async function main() {
548
1474
  console.log(`\n${colors.green}${colors.bold}✅ You selected ${selectedAgentFiles.length} agent${selectedAgentFiles.length === 1 ? '' : 's'}:${colors.reset}\n`);
549
1475
 
550
1476
  // 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
- });
1477
+ if (flags.browse) {
1478
+ // For enhanced selection, we already have agent data
1479
+ console.log(`${colors.dim}Selected files: ${selectedAgentFiles.join(', ')}${colors.reset}\n`);
1480
+ } else {
1481
+ // For traditional selection, show agent details
1482
+ const availableAgents = getAvailableAgents();
1483
+ selectedAgentFiles.forEach(file => {
1484
+ const agent = availableAgents.find(a => a.file === file);
1485
+ if (agent) {
1486
+ console.log(` ${colors.cyan}${colors.bold}${agent.name}${colors.reset}`);
1487
+ console.log(` ${colors.dim}${agent.description}${colors.reset}\n`);
1488
+ }
1489
+ });
1490
+ }
558
1491
 
559
1492
  console.log(`${colors.yellow}${colors.bold}📝 Next Steps:${colors.reset}`);
560
1493
  console.log(`${colors.dim}1. Make sure Claude Code is initialized: ${colors.reset}${colors.cyan}claude init${colors.reset}`);
@@ -565,6 +1498,13 @@ async function main() {
565
1498
  process.exit(0);
566
1499
  }
567
1500
 
1501
+ if (!flags.force && !flags.dryRun && !rl) {
1502
+ rl = readline.createInterface({
1503
+ input: process.stdin,
1504
+ output: process.stdout
1505
+ });
1506
+ }
1507
+
568
1508
  // Additional path validation
569
1509
  const normalizedTarget = path.normalize(targetDir);
570
1510
  const normalizedCwd = path.normalize(process.cwd());
@@ -579,8 +1519,12 @@ async function main() {
579
1519
  }
580
1520
 
581
1521
  // Check for Claude Code installation
582
- const claudeStatus = checkClaudeCode();
583
- if (projectName === '.') {
1522
+ const claudeStatus = includesClaude(aiProvider) ? checkClaudeCode() : {
1523
+ isInstalled: false,
1524
+ hasClaudeDir: false,
1525
+ hasClaudeCodeFile: false
1526
+ };
1527
+ if (includesClaude(aiProvider) && projectName === '.') {
584
1528
  if (flags.dryRun) {
585
1529
  console.log('⚠️ Note: Claude Code detection skipped in dry-run mode for current directory\n');
586
1530
  } else if (!claudeStatus.isInstalled && !claudeStatus.hasClaudeDir) {
@@ -590,8 +1534,15 @@ async function main() {
590
1534
  } else {
591
1535
  console.log('⚠️ Claude Code not detected in this project.');
592
1536
  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') {
1537
+
1538
+ // Use inquirer instead of readline prompt since rl might be closed
1539
+ const confirmModule = await import('@inquirer/confirm');
1540
+ const confirm = confirmModule.default;
1541
+ const shouldCreate = await confirm({
1542
+ message: 'Would you like to create .claude directory?',
1543
+ default: true
1544
+ });
1545
+ if (!shouldCreate) {
595
1546
  console.log('\nTo manually initialize Claude Code:');
596
1547
  console.log('1. Install Claude Code CLI: https://docs.anthropic.com/claude-code/quickstart');
597
1548
  console.log('2. Run \'claude init\' in your project directory');
@@ -607,7 +1558,12 @@ async function main() {
607
1558
 
608
1559
  // Escape targetDir for safe display
609
1560
  const safeTargetDir = targetDir.replace(/[^\w\s\-./\\:]/g, '');
610
- console.log(`${flags.dryRun ? 'Would create' : 'Creating'} Claude Code project in ${safeTargetDir}...`);
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}...`);
611
1567
 
612
1568
  if (projectName !== '.') {
613
1569
  if (!fs.existsSync(targetDir)) {
@@ -628,8 +1584,8 @@ async function main() {
628
1584
  }
629
1585
 
630
1586
  // Check Claude Code in new directory after creation
631
- const newDirClaudeStatus = checkClaudeCode();
632
- if (!flags.dryRun && !newDirClaudeStatus.isInstalled) {
1587
+ const newDirClaudeStatus = includesClaude(aiProvider) ? checkClaudeCode() : { isInstalled: true };
1588
+ if (includesClaude(aiProvider) && !flags.dryRun && !newDirClaudeStatus.isInstalled) {
633
1589
  console.log('\n⚠️ Note: Claude Code is not initialized in the new project directory.');
634
1590
  console.log('After setup, remember to:');
635
1591
  console.log(`1. cd ${JSON.stringify(projectName)}`);
@@ -644,7 +1600,9 @@ async function main() {
644
1600
  // Group conflicts by category
645
1601
  const conflictsByCategory = {
646
1602
  'CLAUDE.md': [],
1603
+ 'AGENTS.md': [],
647
1604
  'agents': [],
1605
+ 'codex': [],
648
1606
  'docs': [],
649
1607
  'plans': [],
650
1608
  'tickets': []
@@ -653,7 +1611,9 @@ async function main() {
653
1611
  // Store conflict strategies per category
654
1612
  const conflictStrategies = {
655
1613
  'CLAUDE.md': 'skip',
1614
+ 'AGENTS.md': 'skip',
656
1615
  'agents': 'skip',
1616
+ 'codex': 'skip',
657
1617
  'docs': 'skip',
658
1618
  'plans': 'skip',
659
1619
  'tickets': 'skip'
@@ -665,7 +1625,10 @@ async function main() {
665
1625
 
666
1626
 
667
1627
  // Determine which agents to include
668
- if (flags.noAgents) {
1628
+ if (!includesClaude(aiProvider)) {
1629
+ selectedAgentFiles = [];
1630
+ console.log('\n⏭️ Skipping Claude agents for Codex-only setup');
1631
+ } else if (flags.noAgents) {
669
1632
  selectedAgentFiles = [];
670
1633
  console.log('\n⏭️ Skipping agent selection (--no-agents flag)');
671
1634
  } else if (flags.browseAgents) {
@@ -691,19 +1654,27 @@ async function main() {
691
1654
  const select = selectModule.default;
692
1655
 
693
1656
  const agentMode = await select({
694
- message: 'Choose your setup mode:',
1657
+ message: 'Would you like to include AI agents in your project?',
695
1658
  choices: [
696
1659
  {
697
- name: 'Browse Mode - Copy all 50+ agents to /agents folder (explore later)',
698
- value: 'browse'
1660
+ name: ' Select Agents - Choose specific agents for your needs',
1661
+ value: 'select',
1662
+ description: 'Interactive selection of agents based on your project'
1663
+ },
1664
+ {
1665
+ name: '🔍 Enhanced Selection - Advanced browsing with search and filters',
1666
+ value: 'enhanced',
1667
+ description: 'Use the enhanced interface with categories, tags, and search'
699
1668
  },
700
1669
  {
701
- name: 'Select Agents - Choose specific agents to include now',
702
- value: 'select'
1670
+ name: '📚 Copy All Agents - Get all 8 agents to explore',
1671
+ value: 'browse',
1672
+ description: 'Copies all agents to /agents folder for manual review'
703
1673
  },
704
1674
  {
705
- name: 'Skip - Don\'t include any agents',
706
- value: 'skip'
1675
+ name: '⏭️ Skip Agents - Just set up the basic structure',
1676
+ value: 'skip',
1677
+ description: 'You can always add agents later'
707
1678
  }
708
1679
  ]
709
1680
  });
@@ -721,6 +1692,10 @@ async function main() {
721
1692
  // Interactive selection
722
1693
  selectedAgentFiles = await selectAgents(availableAgents);
723
1694
  console.log(`\n✅ Selected ${selectedAgentFiles.length} agent${selectedAgentFiles.length === 1 ? '' : 's'}`);
1695
+ } else if (agentMode === 'enhanced') {
1696
+ // Enhanced selection with catalog system
1697
+ selectedAgentFiles = await enhancedAgentSelection();
1698
+ console.log(`\n✅ Selected ${selectedAgentFiles.length} agent${selectedAgentFiles.length === 1 ? '' : 's'}`);
724
1699
  }
725
1700
 
726
1701
  // Recreate readline interface after agent selection
@@ -734,7 +1709,7 @@ async function main() {
734
1709
  // In dry-run mode, show what would happen
735
1710
  console.log(`\nWould prompt for agent selection mode`);
736
1711
  console.log(`Would then prompt for agent selection from ${availableAgents.length} available agents`);
737
- selectedAgentFiles = availableAgents.map(a => a.file).filter(validateAgentFile); // Include all for scanning purposes
1712
+ selectedAgentFiles = availableAgents.map(a => a.file).filter(validateAgentFile); // Include all for dry-run preview
738
1713
  }
739
1714
 
740
1715
  function scanTemplate(src, dest, relativePath = '', skipAgents = false) {
@@ -760,6 +1735,15 @@ async function main() {
760
1735
  if (path.basename(src) === '.claude') {
761
1736
  return; // Don't process .claude directory in regular template scan
762
1737
  }
1738
+
1739
+ if (path.basename(src) === '.codex') {
1740
+ return; // Don't process .codex directory in regular template scan
1741
+ }
1742
+
1743
+ // Skip hooks directory - hooks should only be installed to .claude/hooks via --install-hooks
1744
+ if (path.basename(src) === 'hooks') {
1745
+ return; // Don't copy hooks to project root
1746
+ }
763
1747
 
764
1748
  // Skip agents directory if we're handling it separately
765
1749
  if (skipAgents && path.basename(src) === 'agents') {
@@ -862,6 +1846,14 @@ async function main() {
862
1846
  );
863
1847
  });
864
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
+
865
1857
  allItems.push({
866
1858
  src,
867
1859
  dest,
@@ -876,8 +1868,12 @@ async function main() {
876
1868
  // Categorize the conflict
877
1869
  if (relativePath === 'CLAUDE.md') {
878
1870
  conflictsByCategory['CLAUDE.md'].push(relativePath);
1871
+ } else if (relativePath === 'AGENTS.md') {
1872
+ conflictsByCategory['AGENTS.md'].push(relativePath);
879
1873
  } else if (relativePath.startsWith('agents/')) {
880
1874
  conflictsByCategory['agents'].push(relativePath);
1875
+ } else if (relativePath.startsWith('.codex/')) {
1876
+ conflictsByCategory['codex'].push(relativePath);
881
1877
  } else if (relativePath.startsWith('docs/')) {
882
1878
  conflictsByCategory['docs'].push(relativePath);
883
1879
  } else if (relativePath.startsWith('plans/')) {
@@ -894,6 +1890,40 @@ async function main() {
894
1890
 
895
1891
  scanTemplate(templateDir, targetDir, '', true);
896
1892
 
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);
1918
+ }
1919
+ }
1920
+ }
1921
+
1922
+ walk(templateCodexDir);
1923
+ }
1924
+
1925
+ scanCodexConflicts();
1926
+
897
1927
  // Handle force flag
898
1928
  if (flags.force) {
899
1929
  // Set all strategies to overwrite
@@ -917,7 +1947,9 @@ async function main() {
917
1947
  // Ask for resolution strategy for each category with conflicts
918
1948
  const categories = [
919
1949
  { key: 'CLAUDE.md', name: 'CLAUDE.md', emoji: '📄' },
1950
+ { key: 'AGENTS.md', name: 'AGENTS.md', emoji: '🧭' },
920
1951
  { key: 'agents', name: 'Agents', emoji: '🤖' },
1952
+ { key: 'codex', name: 'Codex', emoji: '🧠' },
921
1953
  { key: 'docs', name: 'Documentation', emoji: '📚' },
922
1954
  { key: 'plans', name: 'Plans', emoji: '📋' },
923
1955
  { key: 'tickets', name: 'Tickets', emoji: '🎫' }
@@ -933,20 +1965,32 @@ async function main() {
933
1965
  console.log(' 2) rename (r) - Save template files with -ccsetup suffix');
934
1966
  console.log(' 3) overwrite (o) - Replace with template versions');
935
1967
 
936
- const userInput = await prompt(`Your choice for ${category.name} [s/r/o]: `);
937
- const strategy = normalizeConflictStrategy(userInput);
1968
+ // Use inquirer instead of readline prompt
1969
+ const selectModule = await import('@inquirer/select');
1970
+ const select = selectModule.default;
1971
+ const strategy = await select({
1972
+ message: `Your choice for ${category.name}:`,
1973
+ choices: [
1974
+ { name: '(s)kip - Keep your existing files', value: 'skip' },
1975
+ { name: '(r)ename - Create backups and use template versions', value: 'rename' },
1976
+ { name: '(o)verwrite - Replace with template versions', value: 'overwrite' }
1977
+ ],
1978
+ default: 'skip'
1979
+ });
938
1980
 
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
- }
1981
+ // Strategy will always be valid when using select, no need for validation
944
1982
 
945
- 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') {
1983
+ if (strategy === 'overwrite' && (category.key === 'CLAUDE.md' || category.key === 'AGENTS.md')) {
1984
+ // Use inquirer instead of readline prompt
1985
+ const confirmModule = await import('@inquirer/confirm');
1986
+ const confirm = confirmModule.default;
1987
+ const shouldOverwrite = await confirm({
1988
+ message: `⚠️ Are you sure you want to overwrite ${category.key}? This will lose your project instructions!`,
1989
+ default: false
1990
+ });
1991
+ if (!shouldOverwrite) {
948
1992
  conflictStrategies[category.key] = 'skip';
949
- console.log('Keeping existing CLAUDE.md');
1993
+ console.log(`Keeping existing ${category.key}`);
950
1994
  continue;
951
1995
  }
952
1996
  }
@@ -981,8 +2025,12 @@ async function main() {
981
2025
  let strategy = 'skip'; // default
982
2026
  if (item.relativePath === 'CLAUDE.md') {
983
2027
  strategy = conflictStrategies['CLAUDE.md'];
2028
+ } else if (item.relativePath === 'AGENTS.md') {
2029
+ strategy = conflictStrategies['AGENTS.md'];
984
2030
  } else if (item.relativePath.startsWith('agents/')) {
985
2031
  strategy = conflictStrategies['agents'];
2032
+ } else if (item.relativePath.startsWith('.codex/')) {
2033
+ strategy = conflictStrategies['codex'];
986
2034
  } else if (item.relativePath.startsWith('docs/')) {
987
2035
  strategy = conflictStrategies['docs'];
988
2036
  } else if (item.relativePath.startsWith('plans/')) {
@@ -1015,9 +2063,11 @@ async function main() {
1015
2063
  } else if (strategy === 'overwrite') {
1016
2064
  if (!flags.dryRun) {
1017
2065
  fs.copyFileSync(item.src, item.dest);
2066
+ console.log(` ♻️ Replaced: ${item.relativePath}`);
2067
+ } else {
2068
+ console.log(` ♻️ Would replace: ${item.relativePath}`);
1018
2069
  }
1019
2070
  overwrittenCount++;
1020
- console.log(` ♻️ ${flags.dryRun ? 'Would replace' : 'Replaced'}: ${item.relativePath}`);
1021
2071
  }
1022
2072
  } else {
1023
2073
  if (!flags.dryRun) {
@@ -1026,6 +2076,7 @@ async function main() {
1026
2076
  if (!fs.existsSync(destDir)) {
1027
2077
  fs.mkdirSync(destDir, { recursive: true });
1028
2078
  }
2079
+
1029
2080
  fs.copyFileSync(item.src, item.dest);
1030
2081
  } else {
1031
2082
  console.log(` ✨ Would copy: ${item.relativePath}`);
@@ -1045,32 +2096,42 @@ async function main() {
1045
2096
  }
1046
2097
  }
1047
2098
 
1048
- // Initialize .claude directory and copy agents
2099
+ // Initialize provider-specific directories
1049
2100
  let claudeInitResult = null;
1050
- // Always initialize .claude directory structure (it will handle existing directories)
1051
- console.log(`\n🔧 ${claudeStatus.hasClaudeDir ? 'Updating' : 'Initializing'} .claude directory structure...`);
1052
- claudeInitResult = await initializeClaudeDirectory(selectedAgentFiles, conflictStrategies['agents'], flags.dryRun);
1053
-
1054
- if (claudeInitResult.createdItems.length > 0) {
1055
- console.log(` ✅ Created ${claudeInitResult.createdItems.length} items in .claude directory`);
1056
- }
1057
- if (claudeInitResult.copiedAgents > 0) {
1058
- console.log(` 🤖 Copied ${claudeInitResult.copiedAgents} agents to .claude/agents`);
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
+ }
1059
2115
  }
1060
- if (claudeInitResult.skippedAgents > 0) {
1061
- console.log(` ⏭️ Skipped ${claudeInitResult.skippedAgents} existing agents in .claude/agents`);
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
+ }
1062
2123
  }
1063
2124
 
1064
- console.log(`\n✅ Claude Code project ${flags.dryRun ? 'would be' : ''} created successfully!`);
2125
+ console.log(`\n✅ ${projectLabel} project ${flags.dryRun ? 'would be ' : ''}created successfully!`);
1065
2126
 
1066
2127
  // Show summary of what happened
1067
- if (fileConflicts.length > 0 || copiedCount > 0 || selectedAgentFiles.length > 0 || claudeInitResult) {
2128
+ if (fileConflicts.length > 0 || copiedCount > 0 || selectedAgentFiles.length > 0 || claudeInitResult || codexInitResult) {
1068
2129
  console.log('\n📊 Summary:');
1069
2130
  if (copiedCount > 0) console.log(` ✨ ${copiedCount} new files ${flags.dryRun ? 'would be' : ''} copied`);
1070
2131
  if (skippedCount > 0) console.log(` ⏭️ ${skippedCount} existing files ${flags.dryRun ? 'would be' : ''} kept unchanged`);
1071
2132
  if (renamedCount > 0) console.log(` 📄 ${renamedCount} template files ${flags.dryRun ? 'would be' : ''} saved with -ccsetup suffix`);
1072
2133
  if (overwrittenCount > 0) console.log(` ♻️ ${overwrittenCount} files ${flags.dryRun ? 'would be' : ''} replaced with template versions`);
1073
- if (!flags.noAgents && !flags.dryRun) {
2134
+ if (includesClaude(aiProvider) && !flags.noAgents && !flags.dryRun) {
1074
2135
  if (flags.browseAgents) {
1075
2136
  const agentCount = availableAgents.length;
1076
2137
  console.log(` 📚 ${agentCount} agent${agentCount === 1 ? '' : 's'} ${flags.dryRun ? 'would be' : ''} copied to /agents for browsing`);
@@ -1082,6 +2143,9 @@ async function main() {
1082
2143
  if (claudeInitResult && claudeInitResult.createdItems.length > 0) {
1083
2144
  console.log(` 📁 ${claudeInitResult.createdItems.length} items created in .claude directory`);
1084
2145
  }
2146
+ if (codexInitResult && codexInitResult.createdItems.length > 0) {
2147
+ console.log(` 🧠 ${codexInitResult.createdItems.length} items created in .codex directory`);
2148
+ }
1085
2149
  }
1086
2150
 
1087
2151
  if (!flags.dryRun) {
@@ -1090,15 +2154,21 @@ async function main() {
1090
2154
  // Escape project name to prevent command injection
1091
2155
  const escapedProjectName = JSON.stringify(projectName);
1092
2156
  console.log(` cd ${escapedProjectName}`);
1093
- const finalClaudeStatus = checkClaudeCode();
1094
- if (!finalClaudeStatus.isInstalled) {
2157
+ const finalClaudeStatus = includesClaude(aiProvider) ? checkClaudeCode() : { isInstalled: true };
2158
+ if (includesClaude(aiProvider) && !finalClaudeStatus.isInstalled) {
1095
2159
  console.log(' claude init # Initialize Claude Code in the project');
1096
2160
  }
1097
2161
  }
1098
- console.log(' 1. Edit CLAUDE.md to add your project-specific instructions');
1099
- console.log(' 2. Update docs/ROADMAP.md with your project goals');
1100
- console.log(' 3. Update docs/agent-orchestration.md to define agent workflows');
1101
- console.log(' 4. Start creating tickets in the tickets/ directory');
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`);
1102
2172
 
1103
2173
  if (flags.browseAgents) {
1104
2174
  console.log('\n📚 Agent Browse Mode:');
@@ -1114,6 +2184,92 @@ async function main() {
1114
2184
  console.log(' You can compare them with your existing files or copy sections you need');
1115
2185
  }
1116
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
+
2192
+ // Ask user if they want the workflow selector hook
2193
+ if (includesClaude(aiProvider) && setupMode === 'full' && !flags.dryRun) {
2194
+ const claudeDir = path.join(targetDir, '.claude');
2195
+ if (fs.existsSync(claudeDir)) {
2196
+ try {
2197
+ let wantHook = false;
2198
+
2199
+ if (!flags.force) {
2200
+ const confirmModule = await import('@inquirer/confirm');
2201
+ const confirm = confirmModule.default;
2202
+
2203
+ wantHook = await confirm({
2204
+ message: 'Enable workflow selector hook? (suggests agent workflows per prompt, controlled via CCSETUP_WORKFLOW env var)',
2205
+ default: false
2206
+ });
2207
+ }
2208
+
2209
+ if (wantHook) {
2210
+ // Create hooks directory
2211
+ const hooksDir = path.join(claudeDir, 'hooks');
2212
+ if (!fs.existsSync(hooksDir)) {
2213
+ fs.mkdirSync(hooksDir, { recursive: true });
2214
+ }
2215
+
2216
+ // Copy workflow-selector hook
2217
+ const hookSourceDir = path.join(templateDir, 'hooks', 'workflow-selector');
2218
+ const hookDestDir = path.join(hooksDir, 'workflow-selector');
2219
+
2220
+ if (fs.existsSync(hookSourceDir)) {
2221
+ if (!fs.existsSync(hookDestDir)) {
2222
+ fs.mkdirSync(hookDestDir, { recursive: true });
2223
+ }
2224
+
2225
+ const hookFile = path.join(hookSourceDir, 'index.js');
2226
+ const destFile = path.join(hookDestDir, 'index.js');
2227
+ fs.copyFileSync(hookFile, destFile);
2228
+
2229
+ // Update settings.json
2230
+ const settingsFile = path.join(claudeDir, 'settings.json');
2231
+ let settings = {};
2232
+
2233
+ if (fs.existsSync(settingsFile)) {
2234
+ try {
2235
+ settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8'));
2236
+ } catch (e) {
2237
+ // Start with empty settings
2238
+ }
2239
+ }
2240
+
2241
+ if (!settings.hooks) {
2242
+ settings.hooks = {};
2243
+ }
2244
+
2245
+ settings.hooks.UserPromptSubmit = [
2246
+ {
2247
+ "matcher": ".*",
2248
+ "hooks": [
2249
+ {
2250
+ "type": "command",
2251
+ "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/workflow-selector/index.js"
2252
+ }
2253
+ ]
2254
+ }
2255
+ ];
2256
+
2257
+ fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
2258
+ console.log(' ✅ Workflow selection hook installed');
2259
+ console.log(' 📝 To activate, set the environment variable:');
2260
+ console.log(' export CCSETUP_WORKFLOW=1');
2261
+ console.log(' 💡 The hook suggests workflows and asks before applying them');
2262
+ }
2263
+ } else {
2264
+ console.log(' ⏭️ Skipped workflow selector hook');
2265
+ console.log(' 💡 You can install it later with: npx ccsetup --install-hooks');
2266
+ }
2267
+ } catch (error) {
2268
+ console.warn(' ⚠️ Could not install workflow hook:', error.message);
2269
+ }
2270
+ }
2271
+ }
2272
+
1117
2273
  console.log('\nHappy coding with Claude! 🎉');
1118
2274
  }
1119
2275
 
@@ -1132,4 +2288,4 @@ main().catch(err => {
1132
2288
  rl.close();
1133
2289
  }
1134
2290
  process.exit(1);
1135
- });
2291
+ });