agentic-team-templates 0.4.2 → 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.
- package/README.md +30 -5
- package/package.json +1 -1
- package/src/index.js +308 -130
package/README.md
CHANGED
|
@@ -7,12 +7,14 @@
|
|
|
7
7
|
|
|
8
8
|

|
|
9
9
|

|
|
10
|
+

|
|
10
11
|
|
|
11
|
-
AI coding assistant templates for Cursor IDE and
|
|
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.
|
|
12
13
|
|
|
13
|
-
**Installs:**
|
|
14
|
+
**Installs (configurable via `--ide`):**
|
|
14
15
|
- **`CLAUDE.md`** - Development guide for Claude-based assistants (Claude Code, Cursor with Claude)
|
|
15
16
|
- **`.cursorrules/`** - Rule files for Cursor IDE
|
|
17
|
+
- **`.github/copilot-instructions.md`** - Instructions for GitHub Copilot
|
|
16
18
|
|
|
17
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.
|
|
18
20
|
|
|
@@ -73,14 +75,33 @@ Re-run with `@latest` to get updated templates:
|
|
|
73
75
|
npx cursor-templates@latest web-frontend
|
|
74
76
|
```
|
|
75
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
|
+
|
|
76
96
|
### CLI Options
|
|
77
97
|
|
|
78
98
|
| Option | Description |
|
|
79
99
|
|--------|-------------|
|
|
100
|
+
| `--ide=<name>` | Target IDE: `cursor`, `claude`, or `codex` (can be used multiple times) |
|
|
80
101
|
| `--list`, `-l` | List all available templates |
|
|
81
|
-
| `--dry-run
|
|
102
|
+
| `--dry-run` | Preview changes without writing files |
|
|
103
|
+
| `--force`, `-f` | Overwrite existing files |
|
|
82
104
|
| `--help`, `-h` | Show help message |
|
|
83
|
-
| `--version`, `-v` | Show version number |
|
|
84
105
|
|
|
85
106
|
## Available Templates
|
|
86
107
|
|
|
@@ -131,7 +152,10 @@ After running `npx cursor-templates web-frontend`:
|
|
|
131
152
|
```
|
|
132
153
|
your-project/
|
|
133
154
|
├── CLAUDE.md # Development guide (Claude Code, Cursor)
|
|
134
|
-
|
|
155
|
+
├── .cursorrules/ # Rule files (Cursor IDE)
|
|
156
|
+
│ └── ...
|
|
157
|
+
└── .github/
|
|
158
|
+
└── copilot-instructions.md # Instructions (GitHub Copilot)
|
|
135
159
|
├── core-principles.md # Shared
|
|
136
160
|
├── code-quality.md # Shared
|
|
137
161
|
├── security-fundamentals.md # Shared
|
|
@@ -206,6 +230,7 @@ npx cursor-templates ml-ai data-engineering
|
|
|
206
230
|
- **Supported IDEs/Tools**:
|
|
207
231
|
- Cursor IDE (any version with `.cursorrules/` support)
|
|
208
232
|
- Claude Code (reads `CLAUDE.md` automatically)
|
|
233
|
+
- GitHub Copilot (reads `.github/copilot-instructions.md`)
|
|
209
234
|
|
|
210
235
|
## How to Contribute
|
|
211
236
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-team-templates",
|
|
3
|
-
"version": "0.
|
|
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
|
|
83
|
-
npx cursor-templates
|
|
84
|
-
npx cursor-templates
|
|
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
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
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
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
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
|
-
|
|
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
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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]')} ${
|
|
602
|
+
console.log(` ${colors.dim('[copy]')} ${rule}`);
|
|
536
603
|
} else if (force) {
|
|
537
|
-
console.log(` ${colors.dim('[update]')} ${
|
|
604
|
+
console.log(` ${colors.dim('[update]')} ${rule}`);
|
|
538
605
|
} else if (filesMatch(src, dest)) {
|
|
539
|
-
console.log(` ${colors.yellow('[skip]')} ${
|
|
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]')} ${
|
|
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]')} ${
|
|
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:
|
|
552
|
-
console.log(` ${colors.blue('[rename]')} ${
|
|
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}]`)} ${
|
|
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
|
-
//
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
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
|
-
|
|
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 {
|
|
576
|
-
|
|
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
|
-
|
|
580
|
-
|
|
581
|
-
|
|
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
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
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
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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('
|
|
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
|
-
|
|
649
|
-
|
|
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
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
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
|
}
|