agentic-team-templates 0.4.1 → 0.5.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 (3) hide show
  1. package/README.md +44 -10
  2. package/package.json +1 -1
  3. package/src/index.js +308 -130
package/README.md CHANGED
@@ -3,7 +3,18 @@
3
3
  [![npm version](https://img.shields.io/npm/v/cursor-templates.svg)](https://www.npmjs.com/package/cursor-templates)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- AI coding assistant templates for Cursor IDE. Pre-configured rules and guidelines that help AI assistants write better code in your projects.
6
+ **Compatible with:**
7
+
8
+ ![Cursor](https://img.shields.io/badge/Cursor_IDE-black?style=flat&logo=cursor)
9
+ ![Claude Code](https://img.shields.io/badge/Claude_Code-cc785c?style=flat&logo=anthropic)
10
+ ![GitHub Copilot](https://img.shields.io/badge/GitHub_Copilot-000?style=flat&logo=githubcopilot)
11
+
12
+ AI coding assistant templates for Cursor IDE, Claude Code, and GitHub Copilot. Pre-configured rules and guidelines that help AI assistants write better code in your projects.
13
+
14
+ **Installs (configurable via `--ide`):**
15
+ - **`CLAUDE.md`** - Development guide for Claude-based assistants (Claude Code, Cursor with Claude)
16
+ - **`.cursorrules/`** - Rule files for Cursor IDE
17
+ - **`.github/copilot-instructions.md`** - Instructions for GitHub Copilot
7
18
 
8
19
  > **Disclaimer:** This project is provided for **educational and experimental purposes only**. The author takes no responsibility for any actions, outputs, or consequences resulting from an LLM or AI assistant following these rules. Use at your own risk. Always review AI-generated code before deploying to production.
9
20
 
@@ -32,9 +43,7 @@ Navigate to your project directory and run:
32
43
  npx cursor-templates web-frontend
33
44
  ```
34
45
 
35
- This installs two things in your project:
36
- - **`CLAUDE.md`** - Main development guide for the AI assistant
37
- - **`.cursorrules/`** - Directory containing domain-specific coding rules
46
+ This installs the template rules in your project directory.
38
47
 
39
48
  ### Install Multiple Templates
40
49
 
@@ -66,14 +75,33 @@ Re-run with `@latest` to get updated templates:
66
75
  npx cursor-templates@latest web-frontend
67
76
  ```
68
77
 
78
+ ### Install for Specific IDE
79
+
80
+ By default, templates install for all supported IDEs (Cursor, Claude, Copilot). Use `--ide` to target specific tools:
81
+
82
+ ```bash
83
+ # Install only for Cursor IDE
84
+ npx cursor-templates web-frontend --ide=cursor
85
+
86
+ # Install only for Claude Code
87
+ npx cursor-templates web-frontend --ide=claude
88
+
89
+ # Install only for GitHub Copilot
90
+ npx cursor-templates web-frontend --ide=codex
91
+
92
+ # Install for multiple IDEs
93
+ npx cursor-templates web-frontend --ide=cursor --ide=codex
94
+ ```
95
+
69
96
  ### CLI Options
70
97
 
71
98
  | Option | Description |
72
99
  |--------|-------------|
100
+ | `--ide=<name>` | Target IDE: `cursor`, `claude`, or `codex` (can be used multiple times) |
73
101
  | `--list`, `-l` | List all available templates |
74
- | `--dry-run`, `-d` | Preview changes without writing files |
102
+ | `--dry-run` | Preview changes without writing files |
103
+ | `--force`, `-f` | Overwrite existing files |
75
104
  | `--help`, `-h` | Show help message |
76
- | `--version`, `-v` | Show version number |
77
105
 
78
106
  ## Available Templates
79
107
 
@@ -123,8 +151,11 @@ After running `npx cursor-templates web-frontend`:
123
151
 
124
152
  ```
125
153
  your-project/
126
- ├── CLAUDE.md # AI development guide
127
- └── .cursorrules/ # Rule files
154
+ ├── CLAUDE.md # Development guide (Claude Code, Cursor)
155
+ ├── .cursorrules/ # Rule files (Cursor IDE)
156
+ │ └── ...
157
+ └── .github/
158
+ └── copilot-instructions.md # Instructions (GitHub Copilot)
128
159
  ├── core-principles.md # Shared
129
160
  ├── code-quality.md # Shared
130
161
  ├── security-fundamentals.md # Shared
@@ -155,7 +186,7 @@ All API calls go through `/lib/api.ts`...
155
186
 
156
187
  ### Modify Existing Rules
157
188
 
158
- Edit any file in `.cursorrules/` directly. Changes take effect immediately in Cursor.
189
+ Edit any file in `.cursorrules/` or `CLAUDE.md` directly. Changes take effect immediately.
159
190
 
160
191
  ### Combine with Existing Rules
161
192
 
@@ -196,7 +227,10 @@ npx cursor-templates ml-ai data-engineering
196
227
  ## Requirements
197
228
 
198
229
  - **Node.js**: 18.0.0 or higher
199
- - **Cursor IDE**: Any version with rules support
230
+ - **Supported IDEs/Tools**:
231
+ - Cursor IDE (any version with `.cursorrules/` support)
232
+ - Claude Code (reads `CLAUDE.md` automatically)
233
+ - GitHub Copilot (reads `.github/copilot-instructions.md`)
200
234
 
201
235
  ## How to Contribute
202
236
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-team-templates",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "AI coding assistant templates for Cursor IDE. Pre-configured rules and guidelines that help AI assistants write better code. - use at your own risk",
5
5
  "keywords": [
6
6
  "cursor",
package/src/index.js CHANGED
@@ -50,6 +50,10 @@ const SHARED_RULES = [
50
50
  'security-fundamentals.md'
51
51
  ];
52
52
 
53
+ // Supported IDEs/tools
54
+ const SUPPORTED_IDES = ['cursor', 'claude', 'codex'];
55
+ const DEFAULT_IDES = ['cursor', 'claude', 'codex']; // Default: install for all IDEs
56
+
53
57
  // Colors
54
58
  const colors = {
55
59
  red: (s) => `\x1b[31m${s}\x1b[0m`,
@@ -69,19 +73,27 @@ function printBanner() {
69
73
 
70
74
  function printHelp() {
71
75
  console.log(`${colors.yellow('Usage:')}
72
- npx cursor-templates <templates...>
76
+ npx cursor-templates <templates...> [options]
73
77
 
74
78
  ${colors.yellow('Options:')}
79
+ --ide=<name> Install for specific IDE (cursor, claude, codex)
80
+ Can be specified multiple times: --ide=cursor --ide=claude
81
+ Default: all (cursor, claude, codex)
75
82
  --list, -l List available templates
76
83
  --help, -h Show this help message
77
84
  --dry-run Show what would be installed
78
85
  --force, -f Overwrite existing files (default: skip)
79
86
 
87
+ ${colors.yellow('IDE Targets:')}
88
+ cursor .cursorrules/ directory (Cursor IDE)
89
+ claude CLAUDE.md file (Claude Code, Cursor with Claude)
90
+ codex .github/copilot-instructions.md (GitHub Copilot)
91
+
80
92
  ${colors.yellow('Examples:')}
81
93
  npx cursor-templates web-frontend
82
- npx cursor-templates web-frontend web-backend
83
- npx cursor-templates fullstack
84
- npx cursor-templates mobile utility-agent
94
+ npx cursor-templates web-frontend --ide=cursor
95
+ npx cursor-templates web-frontend --ide=claude --ide=codex
96
+ npx cursor-templates fullstack --ide=codex
85
97
  npx cursor-templates web-backend --force
86
98
 
87
99
  ${colors.dim('Shared rules (code-quality, security, git-workflow, etc.) are always included.')}
@@ -469,146 +481,291 @@ function generateClaudeMdToPath(targetDir, installedTemplates, destPath) {
469
481
  fs.writeFileSync(destPath, content);
470
482
  }
471
483
 
472
- function install(targetDir, templates, dryRun = false, force = false) {
473
- const cursorrules = path.join(targetDir, '.cursorrules');
474
-
475
- if (!dryRun && !fs.existsSync(cursorrules)) {
476
- fs.mkdirSync(cursorrules, { recursive: true });
477
- }
484
+ /**
485
+ * Generate content for GitHub Copilot instructions file
486
+ */
487
+ function generateCopilotInstructionsContent(installedTemplates) {
488
+ const templateList = installedTemplates
489
+ .map(t => `- ${t}: ${TEMPLATES[t].description}`)
490
+ .join('\n');
491
+
492
+ // Read and concatenate all shared rules
493
+ const sharedRulesContent = SHARED_RULES.map(rule => {
494
+ const rulePath = path.join(TEMPLATES_DIR, '_shared', rule);
495
+ try {
496
+ return fs.readFileSync(rulePath, 'utf8');
497
+ } catch {
498
+ return '';
499
+ }
500
+ }).filter(Boolean).join('\n\n---\n\n');
501
+
502
+ // Read and concatenate template-specific rules
503
+ const templateRulesContent = installedTemplates.map(template => {
504
+ return TEMPLATES[template].rules.map(rule => {
505
+ const rulePath = path.join(TEMPLATES_DIR, template, '.cursorrules', rule);
506
+ try {
507
+ return fs.readFileSync(rulePath, 'utf8');
508
+ } catch {
509
+ return '';
510
+ }
511
+ }).filter(Boolean).join('\n\n');
512
+ }).join('\n\n---\n\n');
513
+
514
+ return `# Copilot Instructions
515
+
516
+ This file provides coding guidelines for GitHub Copilot in this project.
517
+
518
+ ## Project Configuration
519
+
520
+ **Installed Templates:** ${installedTemplates.join(', ')}
521
+
522
+ ${templateList}
523
+
524
+ ---
525
+
526
+ ## Core Principles
527
+
528
+ ### Honesty Over Output
529
+ - If something doesn't work, say it doesn't work
530
+ - If you don't know, say you don't know
531
+ - Never hide errors or suppress warnings
532
+
533
+ ### Security First
534
+ - Zero trust: Every input is hostile until proven otherwise
535
+ - Validate and sanitize all inputs
536
+ - No secrets in code or logs
537
+
538
+ ### Tests Are Required
539
+ - No feature is complete without tests
540
+ - Test behavior, not implementation
541
+
542
+ ### Code Quality
543
+ - SOLID principles
544
+ - DRY (Don't Repeat Yourself)
545
+ - Explicit over implicit
546
+
547
+ ---
548
+
549
+ ## Shared Guidelines
550
+
551
+ ${sharedRulesContent}
552
+
553
+ ---
554
+
555
+ ## Template-Specific Guidelines
556
+
557
+ ${templateRulesContent}
558
+
559
+ ---
560
+
561
+ ## Definition of Done
562
+
563
+ A feature is complete when:
564
+ - [ ] Code written and reviewed
565
+ - [ ] Tests written and passing
566
+ - [ ] No linting errors
567
+ - [ ] Security reviewed
568
+ - [ ] Documentation updated
569
+ `;
570
+ }
571
+
572
+ function install(targetDir, templates, dryRun = false, force = false, ides = DEFAULT_IDES) {
573
+ const stats = { copied: 0, skipped: 0, updated: 0, renamed: 0 };
574
+ const renamedFiles = [];
575
+ const installedFor = [];
478
576
 
479
577
  console.log(`${colors.blue('Installing to:')} ${targetDir}`);
578
+ console.log(`${colors.blue('Target IDEs:')} ${ides.join(', ')}`);
480
579
  if (!force) {
481
580
  console.log(colors.dim('(identical files skipped, modified files preserved with ours saved as *-1.md)'));
482
- console.log(colors.dim('(CLAUDE.md: missing sections merged intelligently)'));
483
581
  }
484
582
  console.log();
485
583
 
486
- const stats = { copied: 0, skipped: 0, updated: 0, renamed: 0 };
487
- const renamedFiles = [];
488
-
489
- // 1. Install shared rules
490
- console.log(colors.green('► Installing shared rules...'));
491
- for (const rule of SHARED_RULES) {
492
- const src = path.join(TEMPLATES_DIR, '_shared', rule);
493
- const dest = path.join(cursorrules, rule);
584
+ // 1. Install .cursorrules/ for Cursor IDE
585
+ if (ides.includes('cursor')) {
586
+ installedFor.push('cursor');
587
+ const cursorrules = path.join(targetDir, '.cursorrules');
494
588
 
495
- if (dryRun) {
496
- const exists = fs.existsSync(dest);
497
- if (!exists) {
498
- console.log(` ${colors.dim('[copy]')} ${rule}`);
499
- } else if (force) {
500
- console.log(` ${colors.dim('[update]')} ${rule}`);
501
- } else if (filesMatch(src, dest)) {
502
- console.log(` ${colors.yellow('[skip]')} ${rule} (identical)`);
503
- } else {
504
- const altName = path.basename(getAlternateFilename(dest));
505
- console.log(` ${colors.blue('[rename]')} ${rule} → ${altName}`);
506
- }
507
- } else {
508
- const result = copyFile(src, dest, force);
509
- stats[result.status]++;
510
- if (result.status === 'skipped') {
511
- console.log(` ${colors.yellow('[skip]')} ${rule} (identical)`);
512
- } else if (result.status === 'renamed') {
513
- const altName = path.basename(result.destFile);
514
- renamedFiles.push({ original: rule, renamed: altName });
515
- console.log(` ${colors.blue('[rename]')} ${rule} → ${altName}`);
516
- } else {
517
- console.log(` ${colors.dim(`[${result.status}]`)} ${rule}`);
518
- }
589
+ if (!dryRun && !fs.existsSync(cursorrules)) {
590
+ fs.mkdirSync(cursorrules, { recursive: true });
519
591
  }
520
- }
521
- console.log();
522
592
 
523
- // 2. Install template-specific rules
524
- for (const template of templates) {
525
- console.log(colors.green(`► Installing ${template} template...`));
526
-
527
- for (const rule of TEMPLATES[template].rules) {
528
- const src = path.join(TEMPLATES_DIR, template, '.cursorrules', rule);
529
- const dest = path.join(cursorrules, `${template}-${rule}`);
530
- const destName = `${template}-${rule}`;
593
+ // Install shared rules
594
+ console.log(colors.green('► Installing shared rules (.cursorrules/)...'));
595
+ for (const rule of SHARED_RULES) {
596
+ const src = path.join(TEMPLATES_DIR, '_shared', rule);
597
+ const dest = path.join(cursorrules, rule);
531
598
 
532
599
  if (dryRun) {
533
600
  const exists = fs.existsSync(dest);
534
601
  if (!exists) {
535
- console.log(` ${colors.dim('[copy]')} ${destName}`);
602
+ console.log(` ${colors.dim('[copy]')} ${rule}`);
536
603
  } else if (force) {
537
- console.log(` ${colors.dim('[update]')} ${destName}`);
604
+ console.log(` ${colors.dim('[update]')} ${rule}`);
538
605
  } else if (filesMatch(src, dest)) {
539
- console.log(` ${colors.yellow('[skip]')} ${destName} (identical)`);
606
+ console.log(` ${colors.yellow('[skip]')} ${rule} (identical)`);
540
607
  } else {
541
608
  const altName = path.basename(getAlternateFilename(dest));
542
- console.log(` ${colors.blue('[rename]')} ${destName} → ${altName}`);
609
+ console.log(` ${colors.blue('[rename]')} ${rule} → ${altName}`);
543
610
  }
544
611
  } else {
545
612
  const result = copyFile(src, dest, force);
546
613
  stats[result.status]++;
547
614
  if (result.status === 'skipped') {
548
- console.log(` ${colors.yellow('[skip]')} ${destName} (identical)`);
615
+ console.log(` ${colors.yellow('[skip]')} ${rule} (identical)`);
549
616
  } else if (result.status === 'renamed') {
550
617
  const altName = path.basename(result.destFile);
551
- renamedFiles.push({ original: destName, renamed: altName });
552
- console.log(` ${colors.blue('[rename]')} ${destName} → ${altName}`);
618
+ renamedFiles.push({ original: rule, renamed: altName });
619
+ console.log(` ${colors.blue('[rename]')} ${rule} → ${altName}`);
553
620
  } else {
554
- console.log(` ${colors.dim(`[${result.status}]`)} ${destName}`);
621
+ console.log(` ${colors.dim(`[${result.status}]`)} ${rule}`);
555
622
  }
556
623
  }
557
624
  }
558
625
  console.log();
626
+
627
+ // Install template-specific rules
628
+ for (const template of templates) {
629
+ console.log(colors.green(`► Installing ${template} template (.cursorrules/)...`));
630
+
631
+ for (const rule of TEMPLATES[template].rules) {
632
+ const src = path.join(TEMPLATES_DIR, template, '.cursorrules', rule);
633
+ const dest = path.join(cursorrules, `${template}-${rule}`);
634
+ const destName = `${template}-${rule}`;
635
+
636
+ if (dryRun) {
637
+ const exists = fs.existsSync(dest);
638
+ if (!exists) {
639
+ console.log(` ${colors.dim('[copy]')} ${destName}`);
640
+ } else if (force) {
641
+ console.log(` ${colors.dim('[update]')} ${destName}`);
642
+ } else if (filesMatch(src, dest)) {
643
+ console.log(` ${colors.yellow('[skip]')} ${destName} (identical)`);
644
+ } else {
645
+ const altName = path.basename(getAlternateFilename(dest));
646
+ console.log(` ${colors.blue('[rename]')} ${destName} → ${altName}`);
647
+ }
648
+ } else {
649
+ const result = copyFile(src, dest, force);
650
+ stats[result.status]++;
651
+ if (result.status === 'skipped') {
652
+ console.log(` ${colors.yellow('[skip]')} ${destName} (identical)`);
653
+ } else if (result.status === 'renamed') {
654
+ const altName = path.basename(result.destFile);
655
+ renamedFiles.push({ original: destName, renamed: altName });
656
+ console.log(` ${colors.blue('[rename]')} ${destName} → ${altName}`);
657
+ } else {
658
+ console.log(` ${colors.dim(`[${result.status}]`)} ${destName}`);
659
+ }
660
+ }
661
+ }
662
+ console.log();
663
+ }
559
664
  }
560
665
 
561
- // 3. Generate CLAUDE.md
562
- const claudePath = path.join(targetDir, 'CLAUDE.md');
563
- const claudeExists = fs.existsSync(claudePath);
564
- const templateContent = generateClaudeMdContent(templates);
565
-
566
- console.log(colors.green('► Generating CLAUDE.md...'));
567
- if (dryRun) {
568
- if (!claudeExists) {
569
- console.log(` ${colors.dim('[copy]')} CLAUDE.md`);
666
+ // 2. Generate CLAUDE.md for Claude Code
667
+ if (ides.includes('claude')) {
668
+ installedFor.push('claude');
669
+ const claudePath = path.join(targetDir, 'CLAUDE.md');
670
+ const claudeExists = fs.existsSync(claudePath);
671
+ const templateContent = generateClaudeMdContent(templates);
672
+
673
+ console.log(colors.green('► Generating CLAUDE.md (Claude Code)...'));
674
+ if (dryRun) {
675
+ if (!claudeExists) {
676
+ console.log(` ${colors.dim('[copy]')} CLAUDE.md`);
677
+ } else if (force) {
678
+ console.log(` ${colors.dim('[update]')} CLAUDE.md`);
679
+ } else {
680
+ const existingContent = fs.readFileSync(claudePath, 'utf8');
681
+ const { missing } = findMissingSections(existingContent, templateContent);
682
+ if (missing.length === 0) {
683
+ console.log(` ${colors.yellow('[skip]')} CLAUDE.md (all sections present)`);
684
+ } else {
685
+ console.log(` ${colors.blue('[merge]')} CLAUDE.md (would add ${missing.length} section(s))`);
686
+ for (const section of missing) {
687
+ console.log(` ${colors.dim('+')} ${section.heading}`);
688
+ }
689
+ }
690
+ }
691
+ } else if (!claudeExists) {
692
+ fs.writeFileSync(claudePath, templateContent);
693
+ console.log(` ${colors.dim('[copied]')} CLAUDE.md`);
694
+ stats.copied++;
570
695
  } else if (force) {
571
- console.log(` ${colors.dim('[update]')} CLAUDE.md`);
696
+ fs.writeFileSync(claudePath, templateContent);
697
+ console.log(` ${colors.dim('[updated]')} CLAUDE.md`);
698
+ stats.updated++;
572
699
  } else {
573
- // Check what would be merged
574
700
  const existingContent = fs.readFileSync(claudePath, 'utf8');
575
- const { missing } = findMissingSections(existingContent, templateContent);
576
- if (missing.length === 0) {
701
+ const { merged, addedSections } = mergeClaudeContent(existingContent, templateContent);
702
+
703
+ if (addedSections.length === 0) {
577
704
  console.log(` ${colors.yellow('[skip]')} CLAUDE.md (all sections present)`);
705
+ stats.skipped++;
578
706
  } else {
579
- console.log(` ${colors.blue('[merge]')} CLAUDE.md (would add ${missing.length} section(s))`);
580
- for (const section of missing) {
581
- console.log(` ${colors.dim('+')} ${section.heading}`);
707
+ fs.writeFileSync(claudePath, merged);
708
+ console.log(` ${colors.blue('[merged]')} CLAUDE.md`);
709
+ console.log(` ${colors.green('Added sections:')}`);
710
+ for (const heading of addedSections) {
711
+ console.log(` ${colors.dim('+')} ${heading}`);
582
712
  }
713
+ stats.updated++;
583
714
  }
584
715
  }
585
- } else if (!claudeExists) {
586
- fs.writeFileSync(claudePath, templateContent);
587
- console.log(` ${colors.dim('[copied]')} CLAUDE.md`);
588
- stats.copied++;
589
- } else if (force) {
590
- fs.writeFileSync(claudePath, templateContent);
591
- console.log(` ${colors.dim('[updated]')} CLAUDE.md`);
592
- stats.updated++;
593
- } else {
594
- // Intelligent merge: append only missing sections
595
- const existingContent = fs.readFileSync(claudePath, 'utf8');
596
- const { merged, addedSections } = mergeClaudeContent(existingContent, templateContent);
716
+ console.log();
717
+ }
718
+
719
+ // 3. Generate .github/copilot-instructions.md for GitHub Copilot (Codex)
720
+ if (ides.includes('codex')) {
721
+ installedFor.push('codex');
722
+ const githubDir = path.join(targetDir, '.github');
723
+ const copilotPath = path.join(githubDir, 'copilot-instructions.md');
724
+ const copilotExists = fs.existsSync(copilotPath);
725
+ const copilotContent = generateCopilotInstructionsContent(templates);
726
+
727
+ if (!dryRun && !fs.existsSync(githubDir)) {
728
+ fs.mkdirSync(githubDir, { recursive: true });
729
+ }
597
730
 
598
- if (addedSections.length === 0) {
599
- console.log(` ${colors.yellow('[skip]')} CLAUDE.md (all sections present)`);
600
- stats.skipped++;
601
- } else {
602
- fs.writeFileSync(claudePath, merged);
603
- console.log(` ${colors.blue('[merged]')} CLAUDE.md`);
604
- console.log(` ${colors.green('Added sections:')}`);
605
- for (const heading of addedSections) {
606
- console.log(` ${colors.dim('+')} ${heading}`);
731
+ console.log(colors.green('► Generating .github/copilot-instructions.md (GitHub Copilot)...'));
732
+ if (dryRun) {
733
+ if (!copilotExists) {
734
+ console.log(` ${colors.dim('[copy]')} .github/copilot-instructions.md`);
735
+ } else if (force) {
736
+ console.log(` ${colors.dim('[update]')} .github/copilot-instructions.md`);
737
+ } else {
738
+ const existingContent = fs.readFileSync(copilotPath, 'utf8');
739
+ const { missing } = findMissingSections(existingContent, copilotContent);
740
+ if (missing.length === 0) {
741
+ console.log(` ${colors.yellow('[skip]')} .github/copilot-instructions.md (all sections present)`);
742
+ } else {
743
+ console.log(` ${colors.blue('[merge]')} .github/copilot-instructions.md (would add ${missing.length} section(s))`);
744
+ }
607
745
  }
746
+ } else if (!copilotExists) {
747
+ fs.writeFileSync(copilotPath, copilotContent);
748
+ console.log(` ${colors.dim('[copied]')} .github/copilot-instructions.md`);
749
+ stats.copied++;
750
+ } else if (force) {
751
+ fs.writeFileSync(copilotPath, copilotContent);
752
+ console.log(` ${colors.dim('[updated]')} .github/copilot-instructions.md`);
608
753
  stats.updated++;
754
+ } else {
755
+ const existingContent = fs.readFileSync(copilotPath, 'utf8');
756
+ const { merged, addedSections } = mergeClaudeContent(existingContent, copilotContent);
757
+
758
+ if (addedSections.length === 0) {
759
+ console.log(` ${colors.yellow('[skip]')} .github/copilot-instructions.md (all sections present)`);
760
+ stats.skipped++;
761
+ } else {
762
+ fs.writeFileSync(copilotPath, merged);
763
+ console.log(` ${colors.blue('[merged]')} .github/copilot-instructions.md`);
764
+ stats.updated++;
765
+ }
609
766
  }
767
+ console.log();
610
768
  }
611
- console.log();
612
769
 
613
770
  // Summary
614
771
  console.log(colors.green('════════════════════════════════════════════════════════════'));
@@ -627,7 +784,18 @@ function install(targetDir, templates, dryRun = false, force = false) {
627
784
  }
628
785
  console.log();
629
786
 
630
- console.log(colors.yellow('Templates installed:'));
787
+ console.log(colors.yellow('Installed for:'));
788
+ for (const ide of installedFor) {
789
+ const ideInfo = {
790
+ cursor: '.cursorrules/ (Cursor IDE)',
791
+ claude: 'CLAUDE.md (Claude Code)',
792
+ codex: '.github/copilot-instructions.md (GitHub Copilot)'
793
+ };
794
+ console.log(` - ${ideInfo[ide]}`);
795
+ }
796
+ console.log();
797
+
798
+ console.log(colors.yellow('Templates:'));
631
799
  console.log(' - _shared (always included)');
632
800
  for (const template of templates) {
633
801
  console.log(` - ${template}`);
@@ -645,45 +813,52 @@ function install(targetDir, templates, dryRun = false, force = false) {
645
813
  }
646
814
 
647
815
  console.log(colors.blue('Next steps:'));
648
- console.log(' 1. Review CLAUDE.md for any customization');
649
- console.log(' 2. Commit the new files to your repository');
816
+ if (installedFor.includes('claude')) {
817
+ console.log(' 1. Review CLAUDE.md for any customization');
818
+ }
819
+ if (installedFor.includes('codex')) {
820
+ console.log(' 2. Review .github/copilot-instructions.md');
821
+ }
822
+ console.log(' 3. Commit the new files to your repository');
650
823
  console.log();
651
824
  }
652
825
 
653
826
  export function run(args) {
654
827
  const templates = [];
828
+ const ides = [];
655
829
  let dryRun = false;
656
830
  let force = false;
657
831
 
658
832
  // Parse arguments
659
833
  for (const arg of args) {
660
- switch (arg) {
661
- case '--list':
662
- case '-l':
663
- printBanner();
664
- printTemplates();
665
- process.exit(0);
666
- break;
667
- case '--help':
668
- case '-h':
669
- printBanner();
670
- printHelp();
671
- process.exit(0);
672
- break;
673
- case '--dry-run':
674
- dryRun = true;
675
- break;
676
- case '--force':
677
- case '-f':
678
- force = true;
679
- break;
680
- default:
681
- if (arg.startsWith('-')) {
682
- console.error(colors.red(`Error: Unknown option '${arg}'`));
683
- printHelp();
684
- process.exit(1);
685
- }
686
- templates.push(arg);
834
+ if (arg === '--list' || arg === '-l') {
835
+ printBanner();
836
+ printTemplates();
837
+ process.exit(0);
838
+ } else if (arg === '--help' || arg === '-h') {
839
+ printBanner();
840
+ printHelp();
841
+ process.exit(0);
842
+ } else if (arg === '--dry-run') {
843
+ dryRun = true;
844
+ } else if (arg === '--force' || arg === '-f') {
845
+ force = true;
846
+ } else if (arg.startsWith('--ide=')) {
847
+ const ide = arg.slice(6).toLowerCase();
848
+ if (!SUPPORTED_IDES.includes(ide)) {
849
+ console.error(colors.red(`Error: Unknown IDE '${ide}'`));
850
+ console.error(colors.dim(`Supported: ${SUPPORTED_IDES.join(', ')}`));
851
+ process.exit(1);
852
+ }
853
+ if (!ides.includes(ide)) {
854
+ ides.push(ide);
855
+ }
856
+ } else if (arg.startsWith('-')) {
857
+ console.error(colors.red(`Error: Unknown option '${arg}'`));
858
+ printHelp();
859
+ process.exit(1);
860
+ } else {
861
+ templates.push(arg);
687
862
  }
688
863
  }
689
864
 
@@ -704,6 +879,9 @@ export function run(args) {
704
879
  }
705
880
  }
706
881
 
882
+ // Use default IDEs if none specified
883
+ const targetIdes = ides.length > 0 ? ides : DEFAULT_IDES;
884
+
707
885
  if (dryRun) {
708
886
  console.log(colors.yellow('DRY RUN - No changes will be made\n'));
709
887
  }
@@ -713,5 +891,5 @@ export function run(args) {
713
891
  }
714
892
 
715
893
  // Install to current directory
716
- install(process.cwd(), templates, dryRun, force);
894
+ install(process.cwd(), templates, dryRun, force, targetIdes);
717
895
  }