agents-templated 1.2.9 → 1.2.11
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 -21
- package/bin/cli.js +281 -225
- package/index.js +16 -30
- package/lib/instructions.js +167 -0
- package/lib/layout.js +95 -0
- package/package.json +2 -1
- package/templates/README.md +27 -18
- package/templates/instructions/source/core.md +36 -0
package/README.md
CHANGED
|
@@ -120,11 +120,11 @@ Agents Templated automatically configures 4 major AI coding assistants:
|
|
|
120
120
|
| AI Agent | Config File | Auto-Discovery |
|
|
121
121
|
|----------|-------------|----------------|
|
|
122
122
|
| **Cursor** | `.cursorrules` | ✅ Auto-loads in Cursor IDE |
|
|
123
|
-
| **GitHub Copilot** | `.github/copilot-instructions.md` | ✅ Auto-loads in VS Code |
|
|
124
|
-
| **Claude** | `CLAUDE.md` | ✅
|
|
125
|
-
| **Gemini** | `GEMINI.md` | ✅
|
|
123
|
+
| **GitHub Copilot** | `.github/instructions/copilot-instructions.md` (+ shim `.github/copilot-instructions.md`) | ✅ Auto-loads in VS Code |
|
|
124
|
+
| **Claude** | `.github/instructions/CLAUDE.md` (+ shim `CLAUDE.md`) | ✅ Compatible |
|
|
125
|
+
| **Gemini** | `.github/instructions/GEMINI.md` (+ shim `GEMINI.md`) | ✅ Compatible |
|
|
126
126
|
|
|
127
|
-
**
|
|
127
|
+
**Single source of truth:** `instructions/source/core.md` drives generated tool-compatible instruction files.
|
|
128
128
|
|
|
129
129
|
---
|
|
130
130
|
|
|
@@ -134,13 +134,21 @@ When you run `agents-templated init`, you get:
|
|
|
134
134
|
|
|
135
135
|
```
|
|
136
136
|
your-project/
|
|
137
|
+
├── instructions/
|
|
138
|
+
│ └── source/
|
|
139
|
+
│ └── core.md # Canonical instruction source of truth
|
|
140
|
+
│
|
|
137
141
|
├── agent-docs/ # 📚 Comprehensive documentation
|
|
138
|
-
│ ├── AGENTS.MD # AI assistant guide
|
|
139
142
|
│ ├── ARCHITECTURE.md # Project architecture & tech stack
|
|
140
143
|
│ └── README.md # Human-readable setup guide
|
|
141
144
|
│
|
|
142
|
-
├──
|
|
143
|
-
│ ├──
|
|
145
|
+
├── .github/
|
|
146
|
+
│ ├── instructions/ # Canonical generated instructions
|
|
147
|
+
│ │ ├── AGENTS.md
|
|
148
|
+
│ │ ├── copilot-instructions.md
|
|
149
|
+
│ │ ├── CLAUDE.md
|
|
150
|
+
│ │ ├── GEMINI.md
|
|
151
|
+
│ │ └── rules/
|
|
144
152
|
│ │ ├── core.mdc # Core development principles
|
|
145
153
|
│ │ ├── security.mdc # Security patterns (CRITICAL)
|
|
146
154
|
│ │ ├── testing.mdc # Testing strategy
|
|
@@ -151,7 +159,18 @@ your-project/
|
|
|
151
159
|
│ │ ├── intent-routing.mdc # Intent-to-command routing policy
|
|
152
160
|
│ │ ├── system-workflow.mdc # End-to-end delivery lifecycle gates
|
|
153
161
|
│ │ └── hardening.mdc # Hardening and obfuscation guidance
|
|
154
|
-
│ ├──
|
|
162
|
+
│ ├── skills/
|
|
163
|
+
│ │ ├── find-skills/ # Skill discovery helper
|
|
164
|
+
│ │ ├── feature-delivery/ # Scoped feature delivery workflow
|
|
165
|
+
│ │ ├── bug-triage/ # Reproduction-first defect workflow
|
|
166
|
+
│ │ ├── app-hardening/ # Hardening and release-evidence workflow
|
|
167
|
+
│ │ ├── ui-ux-pro-max/ # Advanced UI/UX design implementation skill
|
|
168
|
+
│ │ ├── README.md # Guide for creating custom skills
|
|
169
|
+
│ │ └── [your-custom-skills]/ # Your project-specific skills
|
|
170
|
+
│ └── copilot-instructions.md # Compatibility shim for Copilot
|
|
171
|
+
│
|
|
172
|
+
├── agents/ # 🤖 Deterministic command contracts
|
|
173
|
+
│ └── commands/
|
|
155
174
|
│ │ ├── SCHEMA.md # Global slash-command response schema
|
|
156
175
|
│ │ ├── plan.md # /plan contract
|
|
157
176
|
│ │ ├── fix.md # /fix contract
|
|
@@ -159,20 +178,10 @@ your-project/
|
|
|
159
178
|
│ │ ├── release.md # /release contract
|
|
160
179
|
│ │ ├── ... # Other command contracts
|
|
161
180
|
│ │ └── README.md # Commands directory guide
|
|
162
|
-
│ └── skills/
|
|
163
|
-
│ ├── find-skills/ # Skill discovery helper
|
|
164
|
-
│ ├── feature-delivery/ # Scoped feature delivery workflow
|
|
165
|
-
│ ├── bug-triage/ # Reproduction-first defect workflow
|
|
166
|
-
│ ├── app-hardening/ # Hardening and release-evidence workflow
|
|
167
|
-
│ ├── ui-ux-pro-max/ # Advanced UI/UX design implementation skill
|
|
168
|
-
│ ├── README.md # Guide for creating custom skills
|
|
169
|
-
│ └── [your-custom-skills]/ # Your project-specific skills
|
|
170
|
-
│
|
|
171
|
-
├── .github/
|
|
172
|
-
│ └── copilot-instructions.md # GitHub Copilot config
|
|
173
181
|
│
|
|
174
|
-
├──
|
|
175
|
-
├──
|
|
182
|
+
├── AGENTS.MD # Compatibility shim for generic agents
|
|
183
|
+
├── CLAUDE.md # Compatibility shim for Claude tooling
|
|
184
|
+
├── GEMINI.md # Compatibility shim for Gemini tooling
|
|
176
185
|
├── .cursorrules # Cursor IDE config
|
|
177
186
|
├── .gitignore # Pre-configured Git ignore
|
|
178
187
|
└── README.md # Project documentation
|
package/bin/cli.js
CHANGED
|
@@ -6,6 +6,19 @@ const fs = require('fs-extra');
|
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const chalk = require('chalk');
|
|
8
8
|
const { version } = require('../package.json');
|
|
9
|
+
const {
|
|
10
|
+
LAYOUT,
|
|
11
|
+
resolveRulesDir,
|
|
12
|
+
resolveSkillsDir,
|
|
13
|
+
hasAnyLayout,
|
|
14
|
+
getLegacyMigrationPlan
|
|
15
|
+
} = require('../lib/layout');
|
|
16
|
+
const {
|
|
17
|
+
CORE_SOURCE_REL_PATH,
|
|
18
|
+
GENERATED_INSTRUCTION_PATHS,
|
|
19
|
+
writeGeneratedInstructions,
|
|
20
|
+
validateInstructionDrift
|
|
21
|
+
} = require('../lib/instructions');
|
|
9
22
|
|
|
10
23
|
// Resolve the templates directory - works in both dev and installed contexts
|
|
11
24
|
const getTemplatesDir = () => {
|
|
@@ -104,8 +117,8 @@ program
|
|
|
104
117
|
choices: [
|
|
105
118
|
{ name: 'All components', value: 'all' },
|
|
106
119
|
{ name: 'Documentation files (agent-docs/)', value: 'docs' },
|
|
107
|
-
{ name: 'Agent rules (
|
|
108
|
-
{ name: 'Skills (
|
|
120
|
+
{ name: 'Agent rules (.github/instructions/rules/*.mdc)', value: 'rules' },
|
|
121
|
+
{ name: 'Skills (.github/skills/*)', value: 'skills' },
|
|
109
122
|
{ name: 'AI Agent instructions (Cursor, Copilot, VSCode, Gemini)', value: 'github' }
|
|
110
123
|
],
|
|
111
124
|
default: ['all']
|
|
@@ -136,18 +149,19 @@ program
|
|
|
136
149
|
if (installAll || choices.includes('docs')) {
|
|
137
150
|
console.log(chalk.yellow('Installing documentation files...'));
|
|
138
151
|
const sourceDir = path.join(templateDir, 'agent-docs');
|
|
139
|
-
const
|
|
140
|
-
await fs.ensureDir(
|
|
141
|
-
await copyDirectory(sourceDir,
|
|
152
|
+
const targetDocsDir = path.join(targetDir, 'agent-docs');
|
|
153
|
+
await fs.ensureDir(targetDocsDir);
|
|
154
|
+
await copyDirectory(sourceDir, targetDocsDir, options.force);
|
|
155
|
+
await copyFiles(templateDir, targetDir, [CORE_SOURCE_REL_PATH], options.force);
|
|
142
156
|
}
|
|
143
157
|
|
|
144
158
|
// Install agent rules
|
|
145
159
|
if (installAll || choices.includes('rules')) {
|
|
146
160
|
console.log(chalk.yellow('Installing agent rules...'));
|
|
147
|
-
await fs.ensureDir(path.join(targetDir,
|
|
161
|
+
await fs.ensureDir(path.join(targetDir, LAYOUT.canonical.rulesDir));
|
|
148
162
|
await copyDirectory(
|
|
149
163
|
path.join(templateDir, 'agents', 'rules'),
|
|
150
|
-
path.join(targetDir,
|
|
164
|
+
path.join(targetDir, LAYOUT.canonical.rulesDir),
|
|
151
165
|
options.force
|
|
152
166
|
);
|
|
153
167
|
}
|
|
@@ -155,10 +169,10 @@ program
|
|
|
155
169
|
// Install skills
|
|
156
170
|
if (installAll || choices.includes('skills')) {
|
|
157
171
|
console.log(chalk.yellow('Installing skills...'));
|
|
158
|
-
await fs.ensureDir(path.join(targetDir,
|
|
172
|
+
await fs.ensureDir(path.join(targetDir, LAYOUT.canonical.skillsDir));
|
|
159
173
|
await copyDirectory(
|
|
160
174
|
path.join(templateDir, 'agents', 'skills'),
|
|
161
|
-
path.join(targetDir,
|
|
175
|
+
path.join(targetDir, LAYOUT.canonical.skillsDir),
|
|
162
176
|
options.force
|
|
163
177
|
);
|
|
164
178
|
}
|
|
@@ -166,24 +180,20 @@ program
|
|
|
166
180
|
// Install AI Agent instructions (Cursor, Copilot, Claude, Gemini)
|
|
167
181
|
if (installAll || choices.includes('github')) {
|
|
168
182
|
console.log(chalk.yellow('Installing AI agent instructions...'));
|
|
169
|
-
await fs.ensureDir(path.join(targetDir, '.github'));
|
|
170
|
-
await copyFiles(templateDir, targetDir, [
|
|
171
|
-
|
|
172
|
-
'.github/copilot-instructions.md',
|
|
173
|
-
'CLAUDE.md',
|
|
174
|
-
'GEMINI.md'
|
|
175
|
-
], options.force);
|
|
183
|
+
await fs.ensureDir(path.join(targetDir, '.github', 'instructions'));
|
|
184
|
+
await copyFiles(templateDir, targetDir, ['.cursorrules'], options.force);
|
|
185
|
+
await writeGeneratedInstructions(targetDir, templateDir, options.force);
|
|
176
186
|
console.log(chalk.gray(' ✓ Cursor (.cursorrules)'));
|
|
177
|
-
console.log(chalk.gray(' ✓ GitHub Copilot (.github/copilot-instructions.md)'));
|
|
178
|
-
console.log(chalk.gray(' ✓ Claude (CLAUDE.md)'));
|
|
179
|
-
console.log(chalk.gray(' ✓ Google Gemini (GEMINI.md)'));
|
|
187
|
+
console.log(chalk.gray(' ✓ GitHub Copilot (.github/instructions/copilot-instructions.md + compat shim)'));
|
|
188
|
+
console.log(chalk.gray(' ✓ Claude (.github/instructions/CLAUDE.md + compat shim)'));
|
|
189
|
+
console.log(chalk.gray(' ✓ Google Gemini (.github/instructions/GEMINI.md + compat shim)'));
|
|
180
190
|
}
|
|
181
191
|
|
|
182
192
|
console.log(chalk.green.bold('\nInstallation complete!\n'));
|
|
183
193
|
console.log(chalk.cyan('Next steps:'));
|
|
184
|
-
console.log(chalk.white(' 1. Review
|
|
194
|
+
console.log(chalk.white(' 1. Review instructions/source/core.md (canonical AI guide)'));
|
|
185
195
|
console.log(chalk.white(' 2. Review agent-docs/ARCHITECTURE.md for project guidelines'));
|
|
186
|
-
console.log(chalk.white(' 3. Review
|
|
196
|
+
console.log(chalk.white(' 3. Review .github/instructions/ for generated tool-compatible files'));
|
|
187
197
|
console.log(chalk.white(' 4. Configure your AI assistant (Cursor, Copilot, etc.)'));
|
|
188
198
|
console.log(chalk.white(' 5. Adapt the rules to your technology stack\n'));
|
|
189
199
|
|
|
@@ -195,89 +205,69 @@ program
|
|
|
195
205
|
|
|
196
206
|
program
|
|
197
207
|
.command('wizard')
|
|
198
|
-
.description('Interactive setup wizard
|
|
208
|
+
.description('Interactive setup wizard')
|
|
199
209
|
.action(async () => {
|
|
200
210
|
try {
|
|
201
211
|
const templateDir = getTemplatesDir();
|
|
212
|
+
const targetDir = process.cwd();
|
|
202
213
|
console.log(chalk.blue.bold('\n🧙 Agents Templated Setup Wizard\n'));
|
|
203
214
|
console.log(chalk.gray('Let\'s set up your project with the right patterns and guidelines.\n'));
|
|
204
215
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
type: 'list',
|
|
209
|
-
name: 'category',
|
|
210
|
-
message: 'What type of project is this?',
|
|
211
|
-
choices: [
|
|
212
|
-
{ name: '🌐 Full-stack Web Application', value: 'fullstack' },
|
|
213
|
-
{ name: '⚛️ Frontend Only', value: 'frontend' },
|
|
214
|
-
{ name: '🔧 Backend API', value: 'backend' },
|
|
215
|
-
{ name: '📦 NPM Package/Library', value: 'package' },
|
|
216
|
-
{ name: '🤖 CLI Tool', value: 'cli' },
|
|
217
|
-
{ name: '🎯 Custom/Other', value: 'custom' }
|
|
218
|
-
]
|
|
219
|
-
}
|
|
220
|
-
]);
|
|
216
|
+
const hasExistingSetup = await hasInstalledTemplates(targetDir);
|
|
217
|
+
if (hasExistingSetup) {
|
|
218
|
+
console.log(chalk.cyan('Detected an existing agents-templated setup in this project.\n'));
|
|
221
219
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (stackAnswers.category === 'fullstack' || stackAnswers.category === 'frontend') {
|
|
225
|
-
const frontendAnswers = await inquirer.prompt([
|
|
220
|
+
const modeAnswer = await inquirer.prompt([
|
|
226
221
|
{
|
|
227
222
|
type: 'list',
|
|
228
|
-
name: '
|
|
229
|
-
message: '
|
|
223
|
+
name: 'mode',
|
|
224
|
+
message: 'How would you like to proceed?',
|
|
230
225
|
choices: [
|
|
231
|
-
'
|
|
232
|
-
'
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
'Vanilla JS / HTML',
|
|
236
|
-
'Other'
|
|
237
|
-
]
|
|
226
|
+
{ name: 'Update existing setup (recommended)', value: 'update' },
|
|
227
|
+
{ name: 'Run full setup flow', value: 'setup' }
|
|
228
|
+
],
|
|
229
|
+
default: 'update'
|
|
238
230
|
}
|
|
239
231
|
]);
|
|
240
|
-
techStack.frontend = frontendAnswers.framework;
|
|
241
|
-
}
|
|
242
232
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
'
|
|
266
|
-
'
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
233
|
+
if (modeAnswer.mode === 'update') {
|
|
234
|
+
const updateAnswers = await inquirer.prompt([
|
|
235
|
+
{
|
|
236
|
+
type: 'checkbox',
|
|
237
|
+
name: 'components',
|
|
238
|
+
message: 'Select components to update:',
|
|
239
|
+
choices: [
|
|
240
|
+
{ name: 'All components', value: 'all', checked: true },
|
|
241
|
+
{ name: 'Documentation (agent-docs/)', value: 'docs' },
|
|
242
|
+
{ name: 'Agent Rules (security, testing, database, etc.)', value: 'rules' },
|
|
243
|
+
{ name: 'Skills (reusable agent capabilities)', value: 'skills' },
|
|
244
|
+
{ name: 'AI Agent instructions (Cursor, Copilot, VSCode, Gemini)', value: 'github' }
|
|
245
|
+
],
|
|
246
|
+
validate: (answer) => {
|
|
247
|
+
if (answer.length === 0) {
|
|
248
|
+
return 'You must choose at least one component.';
|
|
249
|
+
}
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
type: 'confirm',
|
|
255
|
+
name: 'overwrite',
|
|
256
|
+
message: 'Overwrite existing files while updating?',
|
|
257
|
+
default: true
|
|
258
|
+
}
|
|
259
|
+
]);
|
|
260
|
+
|
|
261
|
+
console.log(chalk.blue('\n📦 Updating selected components...\n'));
|
|
262
|
+
await updateSelectedComponents(targetDir, templateDir, updateAnswers.components, updateAnswers.overwrite);
|
|
263
|
+
|
|
264
|
+
console.log(chalk.green.bold('\n✅ Update complete!\n'));
|
|
265
|
+
console.log(chalk.gray('Tip: run "agents-templated validate" to verify your setup.\n'));
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
278
268
|
}
|
|
279
269
|
|
|
280
|
-
//
|
|
270
|
+
// Component selection
|
|
281
271
|
const componentAnswers = await inquirer.prompt([
|
|
282
272
|
{
|
|
283
273
|
type: 'checkbox',
|
|
@@ -307,7 +297,6 @@ program
|
|
|
307
297
|
// Step 3: Install components
|
|
308
298
|
console.log(chalk.blue('\n📦 Installing components...\n'));
|
|
309
299
|
|
|
310
|
-
const targetDir = process.cwd();
|
|
311
300
|
const options = {
|
|
312
301
|
force: componentAnswers.overwrite,
|
|
313
302
|
docs: componentAnswers.components.includes('docs'),
|
|
@@ -323,15 +312,16 @@ program
|
|
|
323
312
|
const targetDocsDir = path.join(targetDir, 'agent-docs');
|
|
324
313
|
await fs.ensureDir(targetDocsDir);
|
|
325
314
|
await copyDirectory(sourceDocsDir, targetDocsDir, options.force);
|
|
315
|
+
await copyFiles(templateDir, targetDir, [CORE_SOURCE_REL_PATH], options.force);
|
|
326
316
|
}
|
|
327
317
|
|
|
328
318
|
// Install agent rules
|
|
329
319
|
if (options.rules) {
|
|
330
320
|
console.log(chalk.yellow('Installing agent rules...'));
|
|
331
|
-
await fs.ensureDir(path.join(targetDir,
|
|
321
|
+
await fs.ensureDir(path.join(targetDir, LAYOUT.canonical.rulesDir));
|
|
332
322
|
await copyDirectory(
|
|
333
323
|
path.join(templateDir, 'agents', 'rules'),
|
|
334
|
-
path.join(targetDir,
|
|
324
|
+
path.join(targetDir, LAYOUT.canonical.rulesDir),
|
|
335
325
|
options.force
|
|
336
326
|
);
|
|
337
327
|
}
|
|
@@ -339,10 +329,10 @@ program
|
|
|
339
329
|
// Install skills
|
|
340
330
|
if (options.skills) {
|
|
341
331
|
console.log(chalk.yellow('Installing skills...'));
|
|
342
|
-
await fs.ensureDir(path.join(targetDir,
|
|
332
|
+
await fs.ensureDir(path.join(targetDir, LAYOUT.canonical.skillsDir));
|
|
343
333
|
await copyDirectory(
|
|
344
334
|
path.join(templateDir, 'agents', 'skills'),
|
|
345
|
-
path.join(targetDir,
|
|
335
|
+
path.join(targetDir, LAYOUT.canonical.skillsDir),
|
|
346
336
|
options.force
|
|
347
337
|
);
|
|
348
338
|
}
|
|
@@ -350,64 +340,24 @@ program
|
|
|
350
340
|
// Install AI Agent instructions (Cursor, Copilot, VSCode, Gemini)
|
|
351
341
|
if (options.github) {
|
|
352
342
|
console.log(chalk.yellow('Installing AI agent instructions...'));
|
|
353
|
-
await fs.ensureDir(path.join(targetDir, '.github'));
|
|
354
|
-
await copyFiles(templateDir, targetDir, [
|
|
355
|
-
|
|
356
|
-
'.github/copilot-instructions.md',
|
|
357
|
-
'AGENTS.md',
|
|
358
|
-
'CLAUDE.md',
|
|
359
|
-
'GEMINI.md'
|
|
360
|
-
], options.force);
|
|
343
|
+
await fs.ensureDir(path.join(targetDir, '.github', 'instructions'));
|
|
344
|
+
await copyFiles(templateDir, targetDir, ['.cursorrules'], options.force);
|
|
345
|
+
await writeGeneratedInstructions(targetDir, templateDir, options.force);
|
|
361
346
|
console.log(chalk.gray(' ✓ Cursor (.cursorrules)'));
|
|
362
|
-
console.log(chalk.gray(' ✓ GitHub Copilot (.github/copilot-instructions.md)'));
|
|
363
|
-
console.log(chalk.gray(' ✓ Generic AI (AGENTS.md)'));
|
|
364
|
-
console.log(chalk.gray(' ✓ Claude (CLAUDE.md)'));
|
|
365
|
-
console.log(chalk.gray(' ✓ Google Gemini (GEMINI.md)'));
|
|
347
|
+
console.log(chalk.gray(' ✓ GitHub Copilot (.github/instructions/copilot-instructions.md + compat shim)'));
|
|
348
|
+
console.log(chalk.gray(' ✓ Generic AI (.github/instructions/AGENTS.md + compat shim)'));
|
|
349
|
+
console.log(chalk.gray(' ✓ Claude (.github/instructions/CLAUDE.md + compat shim)'));
|
|
350
|
+
console.log(chalk.gray(' ✓ Google Gemini (.github/instructions/GEMINI.md + compat shim)'));
|
|
366
351
|
}
|
|
367
352
|
|
|
368
|
-
//
|
|
353
|
+
// Show summary and next steps
|
|
369
354
|
console.log(chalk.green.bold('\n✅ Installation complete!\n'));
|
|
370
355
|
|
|
371
|
-
console.log(chalk.blue('📋 Your Project Setup:\n'));
|
|
372
|
-
if (techStack.frontend) console.log(chalk.white(` Frontend: ${techStack.frontend}`));
|
|
373
|
-
if (techStack.backend) console.log(chalk.white(` Backend: ${techStack.backend}`));
|
|
374
|
-
if (techStack.database) console.log(chalk.white(` Database: ${techStack.database}`));
|
|
375
|
-
|
|
376
356
|
console.log(chalk.cyan('\n📚 Next Steps:\n'));
|
|
377
|
-
console.log(chalk.white(' 1. Review
|
|
357
|
+
console.log(chalk.white(' 1. Review instructions/source/core.md (canonical AI guide)'));
|
|
378
358
|
console.log(chalk.white(' 2. Review agent-docs/ARCHITECTURE.md for project guidelines'));
|
|
379
|
-
console.log(chalk.white(' 3. Review
|
|
380
|
-
console.log(chalk.white('
|
|
381
|
-
|
|
382
|
-
if (techStack.frontend || techStack.backend) {
|
|
383
|
-
console.log(chalk.cyan('\n📦 Recommended Packages:\n'));
|
|
384
|
-
|
|
385
|
-
if (techStack.frontend?.includes('React')) {
|
|
386
|
-
console.log(chalk.white(' npm install react react-dom next typescript'));
|
|
387
|
-
console.log(chalk.white(' npm install -D @types/react @types/node'));
|
|
388
|
-
} else if (techStack.frontend?.includes('Vue')) {
|
|
389
|
-
console.log(chalk.white(' npm install vue nuxt typescript'));
|
|
390
|
-
} else if (techStack.frontend?.includes('Svelte')) {
|
|
391
|
-
console.log(chalk.white(' npm install svelte @sveltejs/kit typescript'));
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
if (techStack.backend?.includes('Node.js')) {
|
|
395
|
-
console.log(chalk.white(' npm install express zod dotenv'));
|
|
396
|
-
console.log(chalk.white(' npm install -D @types/express'));
|
|
397
|
-
} else if (techStack.backend?.includes('Python')) {
|
|
398
|
-
console.log(chalk.white(' pip install django djangorestframework pydantic'));
|
|
399
|
-
} else if (techStack.backend?.includes('Go')) {
|
|
400
|
-
console.log(chalk.white(' go get github.com/gin-gonic/gin'));
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
if (techStack.database?.includes('PostgreSQL')) {
|
|
404
|
-
console.log(chalk.white(' npm install @prisma/client (or) npm install pg'));
|
|
405
|
-
} else if (techStack.database?.includes('MongoDB')) {
|
|
406
|
-
console.log(chalk.white(' npm install mongoose'));
|
|
407
|
-
} else if (techStack.database?.includes('Supabase')) {
|
|
408
|
-
console.log(chalk.white(' npm install @supabase/supabase-js'));
|
|
409
|
-
}
|
|
410
|
-
}
|
|
359
|
+
console.log(chalk.white(' 3. Review .github/instructions/ for generated tool-compatible files'));
|
|
360
|
+
console.log(chalk.white(' 4. Customize .github/instructions/rules/*.mdc for your tech stack'));
|
|
411
361
|
|
|
412
362
|
console.log(chalk.cyan('\n🔒 Security Reminder:\n'));
|
|
413
363
|
console.log(chalk.white(' • Review agents/rules/security.mdc'));
|
|
@@ -427,8 +377,8 @@ program
|
|
|
427
377
|
.action(() => {
|
|
428
378
|
console.log(chalk.blue.bold('\nAvailable Components:\n'));
|
|
429
379
|
console.log(chalk.yellow('docs') + ' - Documentation files (agent-docs/ directory)');
|
|
430
|
-
console.log(chalk.yellow('rules') + ' - Agent rules (
|
|
431
|
-
console.log(chalk.yellow('skills') + ' - Agent skills (
|
|
380
|
+
console.log(chalk.yellow('rules') + ' - Agent rules (.github/instructions/rules/*.mdc)');
|
|
381
|
+
console.log(chalk.yellow('skills') + ' - Agent skills (.github/skills/*)');
|
|
432
382
|
console.log(chalk.yellow('github') + ' - AI Agent instructions (Cursor, Copilot, VSCode, Gemini)');
|
|
433
383
|
console.log(chalk.yellow('all') + ' - All components');
|
|
434
384
|
|
|
@@ -454,11 +404,12 @@ program
|
|
|
454
404
|
let warnings = [];
|
|
455
405
|
let passed = [];
|
|
456
406
|
|
|
457
|
-
// Check
|
|
458
|
-
|
|
459
|
-
|
|
407
|
+
// Check canonical source file
|
|
408
|
+
const coreSourcePath = path.join(targetDir, CORE_SOURCE_REL_PATH);
|
|
409
|
+
if (await fs.pathExists(coreSourcePath)) {
|
|
410
|
+
passed.push(`✓ ${CORE_SOURCE_REL_PATH} found`);
|
|
460
411
|
} else {
|
|
461
|
-
warnings.push(`⚠
|
|
412
|
+
warnings.push(`⚠ ${CORE_SOURCE_REL_PATH} missing - run 'agents-templated init --docs'`);
|
|
462
413
|
}
|
|
463
414
|
|
|
464
415
|
const docFiles = ['ARCHITECTURE.md'];
|
|
@@ -478,32 +429,67 @@ program
|
|
|
478
429
|
|
|
479
430
|
// Check agent rules
|
|
480
431
|
const ruleFiles = ['core.mdc', 'security.mdc', 'testing.mdc', 'frontend.mdc', 'database.mdc', 'style.mdc'];
|
|
481
|
-
const
|
|
482
|
-
|
|
432
|
+
const canonicalRulesDir = path.join(targetDir, LAYOUT.canonical.rulesDir);
|
|
433
|
+
const legacyRulesDir = path.join(targetDir, LAYOUT.legacy.rulesDirs[0]);
|
|
434
|
+
const rulesDir = path.join(targetDir, resolveRulesDir(targetDir));
|
|
435
|
+
|
|
436
|
+
if (!(await fs.pathExists(canonicalRulesDir)) && await fs.pathExists(legacyRulesDir)) {
|
|
437
|
+
issues.push(`✗ Legacy rules layout detected at ${LAYOUT.legacy.rulesDirs[0]} - run 'agents-templated update --all' to migrate`);
|
|
438
|
+
}
|
|
439
|
+
|
|
483
440
|
if (await fs.pathExists(rulesDir)) {
|
|
441
|
+
const relativeRulesDir = path.relative(targetDir, rulesDir) || LAYOUT.canonical.rulesDir;
|
|
484
442
|
for (const file of ruleFiles) {
|
|
485
443
|
if (await fs.pathExists(path.join(rulesDir, file))) {
|
|
486
|
-
passed.push(`✓
|
|
444
|
+
passed.push(`✓ ${relativeRulesDir}/${file} found`);
|
|
487
445
|
} else {
|
|
488
|
-
warnings.push(`⚠
|
|
446
|
+
warnings.push(`⚠ ${relativeRulesDir}/${file} missing`);
|
|
489
447
|
}
|
|
490
448
|
}
|
|
491
449
|
} else {
|
|
492
|
-
warnings.push(`⚠
|
|
450
|
+
warnings.push(`⚠ ${LAYOUT.canonical.rulesDir} directory missing - run 'agents-templated init --rules'`);
|
|
493
451
|
}
|
|
494
452
|
|
|
495
453
|
// Check skills
|
|
496
|
-
const
|
|
454
|
+
const canonicalSkillsDir = path.join(targetDir, LAYOUT.canonical.skillsDir);
|
|
455
|
+
const legacySkillsDir = path.join(targetDir, LAYOUT.legacy.skillsDirs[0]);
|
|
456
|
+
const skillsDir = path.join(targetDir, resolveSkillsDir(targetDir));
|
|
457
|
+
|
|
458
|
+
if (!(await fs.pathExists(canonicalSkillsDir)) && await fs.pathExists(legacySkillsDir)) {
|
|
459
|
+
issues.push(`✗ Legacy skills layout detected at ${LAYOUT.legacy.skillsDirs[0]} - run 'agents-templated update --all' to migrate`);
|
|
460
|
+
}
|
|
461
|
+
|
|
497
462
|
if (await fs.pathExists(skillsDir)) {
|
|
498
463
|
const skills = await fs.readdir(skillsDir);
|
|
499
|
-
|
|
464
|
+
const relativeSkillsDir = path.relative(targetDir, skillsDir) || LAYOUT.canonical.skillsDir;
|
|
465
|
+
passed.push(`✓ ${skills.length} skills installed in ${relativeSkillsDir}`);
|
|
500
466
|
} else {
|
|
501
|
-
warnings.push(`⚠
|
|
467
|
+
warnings.push(`⚠ ${LAYOUT.canonical.skillsDir} directory missing - run 'agents-templated init --skills'`);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Check generated instruction files and drift
|
|
471
|
+
const hasInstructionFootprint =
|
|
472
|
+
await fs.pathExists(path.join(targetDir, '.github', 'instructions')) ||
|
|
473
|
+
await fs.pathExists(path.join(targetDir, '.claude', 'rules')) ||
|
|
474
|
+
await fs.pathExists(path.join(targetDir, GENERATED_INSTRUCTION_PATHS.compatibility.copilot)) ||
|
|
475
|
+
await fs.pathExists(path.join(targetDir, GENERATED_INSTRUCTION_PATHS.compatibility.claude)) ||
|
|
476
|
+
await fs.pathExists(path.join(targetDir, GENERATED_INSTRUCTION_PATHS.compatibility.gemini)) ||
|
|
477
|
+
await fs.pathExists(path.join(targetDir, GENERATED_INSTRUCTION_PATHS.compatibility.generic));
|
|
478
|
+
|
|
479
|
+
if (hasInstructionFootprint) {
|
|
480
|
+
const instructionDrift = await validateInstructionDrift(targetDir);
|
|
481
|
+
if (instructionDrift.missingCore) {
|
|
482
|
+
issues.push(`✗ Canonical instruction source missing - run 'agents-templated init --docs --github'`);
|
|
483
|
+
} else if (!instructionDrift.ok) {
|
|
484
|
+
issues.push(`✗ Generated instruction files are out of sync: ${instructionDrift.driftFiles.join(', ')}`);
|
|
485
|
+
} else {
|
|
486
|
+
passed.push('✓ Generated instruction files are in sync with canonical source');
|
|
487
|
+
}
|
|
502
488
|
}
|
|
503
489
|
|
|
504
|
-
|
|
505
|
-
const
|
|
506
|
-
if (await fs.pathExists(
|
|
490
|
+
const canonicalCopilotFile = path.join(targetDir, GENERATED_INSTRUCTION_PATHS.canonical.copilot);
|
|
491
|
+
const compatCopilotFile = path.join(targetDir, GENERATED_INSTRUCTION_PATHS.compatibility.copilot);
|
|
492
|
+
if (await fs.pathExists(canonicalCopilotFile) || await fs.pathExists(compatCopilotFile)) {
|
|
507
493
|
passed.push(`✓ GitHub Copilot configuration found`);
|
|
508
494
|
} else {
|
|
509
495
|
warnings.push(`⚠ GitHub Copilot configuration missing - run 'agents-templated init --github'`);
|
|
@@ -639,6 +625,81 @@ async function copyDirectory(sourceDir, targetDir, force = false) {
|
|
|
639
625
|
}
|
|
640
626
|
}
|
|
641
627
|
|
|
628
|
+
async function hasInstalledTemplates(targetDir) {
|
|
629
|
+
return await hasAnyLayout(targetDir) ||
|
|
630
|
+
await fs.pathExists(path.join(targetDir, CORE_SOURCE_REL_PATH)) ||
|
|
631
|
+
await fs.pathExists(path.join(targetDir, GENERATED_INSTRUCTION_PATHS.compatibility.generic));
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
async function updateSelectedComponents(targetDir, templateDir, selectedComponents, overwrite = true) {
|
|
635
|
+
const components = selectedComponents.includes('all')
|
|
636
|
+
? ['docs', 'rules', 'skills', 'github']
|
|
637
|
+
: selectedComponents;
|
|
638
|
+
|
|
639
|
+
if (components.includes('docs')) {
|
|
640
|
+
console.log(chalk.yellow('Updating documentation files...'));
|
|
641
|
+
await fs.ensureDir(path.join(targetDir, 'agent-docs'));
|
|
642
|
+
await copyDirectory(
|
|
643
|
+
path.join(templateDir, 'agent-docs'),
|
|
644
|
+
path.join(targetDir, 'agent-docs'),
|
|
645
|
+
overwrite
|
|
646
|
+
);
|
|
647
|
+
await copyFiles(templateDir, targetDir, [CORE_SOURCE_REL_PATH], overwrite);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
if (components.includes('rules')) {
|
|
651
|
+
console.log(chalk.yellow('Updating agent rules...'));
|
|
652
|
+
await fs.ensureDir(path.join(targetDir, LAYOUT.canonical.rulesDir));
|
|
653
|
+
await copyDirectory(
|
|
654
|
+
path.join(templateDir, 'agents', 'rules'),
|
|
655
|
+
path.join(targetDir, LAYOUT.canonical.rulesDir),
|
|
656
|
+
overwrite
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
if (components.includes('skills')) {
|
|
661
|
+
console.log(chalk.yellow('Updating skills...'));
|
|
662
|
+
await fs.ensureDir(path.join(targetDir, LAYOUT.canonical.skillsDir));
|
|
663
|
+
await copyDirectory(
|
|
664
|
+
path.join(templateDir, 'agents', 'skills'),
|
|
665
|
+
path.join(targetDir, LAYOUT.canonical.skillsDir),
|
|
666
|
+
overwrite
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
if (components.includes('github')) {
|
|
671
|
+
console.log(chalk.yellow('Updating AI agent instructions...'));
|
|
672
|
+
await fs.ensureDir(path.join(targetDir, '.github', 'instructions'));
|
|
673
|
+
await copyFiles(templateDir, targetDir, ['.cursorrules'], overwrite);
|
|
674
|
+
await writeGeneratedInstructions(targetDir, templateDir, overwrite);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
if ((components.includes('docs') || components.includes('github')) && !components.includes('github')) {
|
|
678
|
+
await writeGeneratedInstructions(targetDir, templateDir, overwrite);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
async function applyLegacyMigrationPlan(targetDir, migrationPlan, overwrite = true) {
|
|
683
|
+
for (const move of migrationPlan) {
|
|
684
|
+
const sourcePath = path.join(targetDir, move.source);
|
|
685
|
+
const targetPath = path.join(targetDir, move.target);
|
|
686
|
+
|
|
687
|
+
if (!(await fs.pathExists(sourcePath))) {
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
await fs.ensureDir(path.dirname(targetPath));
|
|
692
|
+
|
|
693
|
+
if (await fs.pathExists(targetPath)) {
|
|
694
|
+
await copyDirectory(sourcePath, targetPath, overwrite);
|
|
695
|
+
await fs.remove(sourcePath);
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
await fs.move(sourcePath, targetPath, { overwrite });
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
642
703
|
program
|
|
643
704
|
.command('update')
|
|
644
705
|
.description('Check for and apply template updates')
|
|
@@ -657,8 +718,7 @@ program
|
|
|
657
718
|
console.log(chalk.blue.bold('\n🔄 Checking for template updates...\n'));
|
|
658
719
|
|
|
659
720
|
// Check if templates are installed
|
|
660
|
-
const hasTemplates = await
|
|
661
|
-
await fs.pathExists(path.join(targetDir, 'agents'));
|
|
721
|
+
const hasTemplates = await hasInstalledTemplates(targetDir);
|
|
662
722
|
|
|
663
723
|
if (!hasTemplates) {
|
|
664
724
|
console.log(chalk.yellow('No templates detected in this directory.'));
|
|
@@ -666,6 +726,38 @@ program
|
|
|
666
726
|
process.exit(0);
|
|
667
727
|
}
|
|
668
728
|
|
|
729
|
+
const migrationPlan = await getLegacyMigrationPlan(targetDir);
|
|
730
|
+
if (migrationPlan.length > 0) {
|
|
731
|
+
console.log(chalk.yellow('Legacy layout detected. Migration to canonical Copilot-style paths is required.\n'));
|
|
732
|
+
migrationPlan.forEach(({ source, target }) => {
|
|
733
|
+
console.log(chalk.white(` ${source} -> ${target}`));
|
|
734
|
+
});
|
|
735
|
+
console.log('');
|
|
736
|
+
|
|
737
|
+
if (options.checkOnly) {
|
|
738
|
+
console.log(chalk.red('Migration required before validation can pass.'));
|
|
739
|
+
console.log(chalk.gray('Run "agents-templated update" and confirm migration.\n'));
|
|
740
|
+
process.exit(1);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
const migrationAnswer = await inquirer.prompt([
|
|
744
|
+
{
|
|
745
|
+
type: 'confirm',
|
|
746
|
+
name: 'proceed',
|
|
747
|
+
message: 'Migrate legacy directories now?',
|
|
748
|
+
default: true
|
|
749
|
+
}
|
|
750
|
+
]);
|
|
751
|
+
|
|
752
|
+
if (!migrationAnswer.proceed) {
|
|
753
|
+
console.log(chalk.red('\nMigration skipped. Setup remains non-canonical.\n'));
|
|
754
|
+
process.exit(1);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
await applyLegacyMigrationPlan(targetDir, migrationPlan, true);
|
|
758
|
+
console.log(chalk.green('✓ Legacy layout migration completed.\n'));
|
|
759
|
+
}
|
|
760
|
+
|
|
669
761
|
const hasComponentSelection = options.all || options.docs || options.rules || options.skills || options.github;
|
|
670
762
|
|
|
671
763
|
// Component refresh mode: update selected parts directly without stack/wizard prompts
|
|
@@ -678,46 +770,7 @@ program
|
|
|
678
770
|
|
|
679
771
|
console.log(chalk.blue('📦 Updating selected components...\n'));
|
|
680
772
|
|
|
681
|
-
|
|
682
|
-
console.log(chalk.yellow('Updating documentation files...'));
|
|
683
|
-
await fs.ensureDir(path.join(targetDir, 'agent-docs'));
|
|
684
|
-
await copyDirectory(
|
|
685
|
-
path.join(templateDir, 'agent-docs'),
|
|
686
|
-
path.join(targetDir, 'agent-docs'),
|
|
687
|
-
true
|
|
688
|
-
);
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
if (selectedComponents.includes('rules')) {
|
|
692
|
-
console.log(chalk.yellow('Updating agent rules...'));
|
|
693
|
-
await fs.ensureDir(path.join(targetDir, 'agents', 'rules'));
|
|
694
|
-
await copyDirectory(
|
|
695
|
-
path.join(templateDir, 'agents', 'rules'),
|
|
696
|
-
path.join(targetDir, 'agents', 'rules'),
|
|
697
|
-
true
|
|
698
|
-
);
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
if (selectedComponents.includes('skills')) {
|
|
702
|
-
console.log(chalk.yellow('Updating skills...'));
|
|
703
|
-
await fs.ensureDir(path.join(targetDir, 'agents', 'skills'));
|
|
704
|
-
await copyDirectory(
|
|
705
|
-
path.join(templateDir, 'agents', 'skills'),
|
|
706
|
-
path.join(targetDir, 'agents', 'skills'),
|
|
707
|
-
true
|
|
708
|
-
);
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
if (selectedComponents.includes('github')) {
|
|
712
|
-
console.log(chalk.yellow('Updating AI agent instructions...'));
|
|
713
|
-
await fs.ensureDir(path.join(targetDir, '.github'));
|
|
714
|
-
await copyFiles(templateDir, targetDir, [
|
|
715
|
-
'.cursorrules',
|
|
716
|
-
'.github/copilot-instructions.md',
|
|
717
|
-
'CLAUDE.md',
|
|
718
|
-
'GEMINI.md'
|
|
719
|
-
], true);
|
|
720
|
-
}
|
|
773
|
+
await updateSelectedComponents(targetDir, templateDir, selectedComponents, true);
|
|
721
774
|
|
|
722
775
|
console.log(chalk.green.bold('\n✅ Selected component updates applied successfully!\n'));
|
|
723
776
|
process.exit(0);
|
|
@@ -726,27 +779,27 @@ program
|
|
|
726
779
|
// List potential updates
|
|
727
780
|
const updates = [];
|
|
728
781
|
const checkFiles = [
|
|
729
|
-
{
|
|
730
|
-
{
|
|
731
|
-
{
|
|
732
|
-
{
|
|
733
|
-
{
|
|
734
|
-
{
|
|
735
|
-
{
|
|
736
|
-
{
|
|
737
|
-
{
|
|
782
|
+
{ targetFile: CORE_SOURCE_REL_PATH, templateFile: CORE_SOURCE_REL_PATH, component: 'root' },
|
|
783
|
+
{ targetFile: 'agent-docs/ARCHITECTURE.md', templateFile: 'agent-docs/ARCHITECTURE.md', component: 'docs' },
|
|
784
|
+
{ targetFile: `${LAYOUT.canonical.rulesDir}/security.mdc`, templateFile: 'agents/rules/security.mdc', component: 'rules' },
|
|
785
|
+
{ targetFile: `${LAYOUT.canonical.rulesDir}/testing.mdc`, templateFile: 'agents/rules/testing.mdc', component: 'rules' },
|
|
786
|
+
{ targetFile: `${LAYOUT.canonical.rulesDir}/core.mdc`, templateFile: 'agents/rules/core.mdc', component: 'rules' },
|
|
787
|
+
{ targetFile: `${LAYOUT.canonical.skillsDir}/README.md`, templateFile: 'agents/skills/README.md', component: 'skills' },
|
|
788
|
+
{ targetFile: `${LAYOUT.canonical.skillsDir}/find-skills/SKILL.md`, templateFile: 'agents/skills/find-skills/SKILL.md', component: 'skills' },
|
|
789
|
+
{ targetFile: `${LAYOUT.canonical.skillsDir}/ui-ux-pro-max/SKILL.md`, templateFile: 'agents/skills/ui-ux-pro-max/SKILL.md', component: 'skills' },
|
|
790
|
+
{ targetFile: '.cursorrules', templateFile: '.cursorrules', component: 'github' }
|
|
738
791
|
];
|
|
739
792
|
|
|
740
|
-
for (const {
|
|
741
|
-
const targetPath = path.join(targetDir,
|
|
742
|
-
const templatePath = path.join(templateDir,
|
|
793
|
+
for (const {targetFile, templateFile, component} of checkFiles) {
|
|
794
|
+
const targetPath = path.join(targetDir, targetFile);
|
|
795
|
+
const templatePath = path.join(templateDir, templateFile);
|
|
743
796
|
|
|
744
797
|
if (await fs.pathExists(targetPath) && await fs.pathExists(templatePath)) {
|
|
745
798
|
const targetContent = await fs.readFile(targetPath, 'utf8');
|
|
746
799
|
const templateContent = await fs.readFile(templatePath, 'utf8');
|
|
747
800
|
|
|
748
801
|
if (targetContent !== templateContent) {
|
|
749
|
-
updates.push({
|
|
802
|
+
updates.push({ targetFile, templateFile, component });
|
|
750
803
|
}
|
|
751
804
|
}
|
|
752
805
|
}
|
|
@@ -757,8 +810,8 @@ program
|
|
|
757
810
|
}
|
|
758
811
|
|
|
759
812
|
console.log(chalk.yellow(`Found ${updates.length} file(s) with updates available:\n`));
|
|
760
|
-
updates.forEach(({
|
|
761
|
-
console.log(chalk.white(` 📄 ${
|
|
813
|
+
updates.forEach(({ targetFile }) => {
|
|
814
|
+
console.log(chalk.white(` 📄 ${targetFile}`));
|
|
762
815
|
});
|
|
763
816
|
console.log('');
|
|
764
817
|
|
|
@@ -785,20 +838,23 @@ program
|
|
|
785
838
|
// Apply updates
|
|
786
839
|
console.log(chalk.blue('\n📦 Applying updates...\n'));
|
|
787
840
|
|
|
788
|
-
for (const {
|
|
789
|
-
const targetPath = path.join(targetDir,
|
|
790
|
-
const templatePath = path.join(templateDir,
|
|
841
|
+
for (const { targetFile, templateFile } of updates) {
|
|
842
|
+
const targetPath = path.join(targetDir, targetFile);
|
|
843
|
+
const templatePath = path.join(templateDir, templateFile);
|
|
791
844
|
|
|
792
845
|
// Backup original file
|
|
793
846
|
const backupPath = `${targetPath}.backup`;
|
|
794
847
|
await fs.copy(targetPath, backupPath);
|
|
795
|
-
console.log(chalk.gray(` Backed up: ${
|
|
848
|
+
console.log(chalk.gray(` Backed up: ${targetFile}.backup`));
|
|
796
849
|
|
|
797
850
|
// Copy new version
|
|
798
851
|
await fs.copy(templatePath, targetPath, { overwrite: true });
|
|
799
|
-
console.log(chalk.green(` ✓ Updated: ${
|
|
852
|
+
console.log(chalk.green(` ✓ Updated: ${targetFile}`));
|
|
800
853
|
}
|
|
801
854
|
|
|
855
|
+
await writeGeneratedInstructions(targetDir, templateDir, true);
|
|
856
|
+
console.log(chalk.green(' ✓ Regenerated instruction compatibility files'));
|
|
857
|
+
|
|
802
858
|
console.log(chalk.green.bold('\n✅ Updates applied successfully!\n'));
|
|
803
859
|
console.log(chalk.gray('Backup files created with .backup extension\n'));
|
|
804
860
|
|
|
@@ -886,7 +942,7 @@ program
|
|
|
886
942
|
console.log(chalk.blue('\n📚 Quick Tips:\n'));
|
|
887
943
|
console.log(chalk.white(' • Run "agents-templated validate" to check setup'));
|
|
888
944
|
console.log(chalk.white(' • Run "agents-templated wizard" for guided setup'));
|
|
889
|
-
console.log(chalk.white(' • Review
|
|
945
|
+
console.log(chalk.white(' • Review .github/instructions/rules/security.mdc for security patterns\n'));
|
|
890
946
|
|
|
891
947
|
} catch (error) {
|
|
892
948
|
console.error(chalk.red('Error:'), error.message);
|
package/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const fs = require('fs-extra');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const { LAYOUT } = require('./lib/layout');
|
|
4
|
+
const { CORE_SOURCE_REL_PATH, writeGeneratedInstructions } = require('./lib/instructions');
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Programmatic API for agents-templated
|
|
@@ -26,9 +28,9 @@ async function install(targetDir, options = {}) {
|
|
|
26
28
|
// Documentation files
|
|
27
29
|
if (installAll || options.docs) {
|
|
28
30
|
files.push(
|
|
29
|
-
'AGENTS.md',
|
|
30
31
|
'agent-docs/ARCHITECTURE.md',
|
|
31
|
-
'agent-docs/README.md'
|
|
32
|
+
'agent-docs/README.md',
|
|
33
|
+
CORE_SOURCE_REL_PATH
|
|
32
34
|
);
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -49,51 +51,35 @@ async function install(targetDir, options = {}) {
|
|
|
49
51
|
|
|
50
52
|
// Agent rules
|
|
51
53
|
if (installAll || options.rules) {
|
|
52
|
-
await fs.ensureDir(path.join(targetDir,
|
|
54
|
+
await fs.ensureDir(path.join(targetDir, LAYOUT.canonical.rulesDir));
|
|
53
55
|
await copyDirectory(
|
|
54
56
|
path.join(templateDir, 'agents', 'rules'),
|
|
55
|
-
path.join(targetDir,
|
|
57
|
+
path.join(targetDir, LAYOUT.canonical.rulesDir),
|
|
56
58
|
options.force
|
|
57
59
|
);
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
// Skills
|
|
61
63
|
if (installAll || options.skills) {
|
|
62
|
-
await fs.ensureDir(path.join(targetDir,
|
|
64
|
+
await fs.ensureDir(path.join(targetDir, LAYOUT.canonical.skillsDir));
|
|
63
65
|
await copyDirectory(
|
|
64
66
|
path.join(templateDir, 'agents', 'skills'),
|
|
65
|
-
path.join(targetDir,
|
|
67
|
+
path.join(targetDir, LAYOUT.canonical.skillsDir),
|
|
66
68
|
options.force
|
|
67
69
|
);
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
// AI Agent instructions (Cursor, Copilot, Claude, Gemini)
|
|
71
73
|
if (installAll || options.github) {
|
|
72
|
-
await fs.ensureDir(path.join(targetDir, '.github'));
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
'GEMINI.md'
|
|
79
|
-
];
|
|
80
|
-
|
|
81
|
-
for (const config of agentConfigs) {
|
|
82
|
-
const sourcePath = path.join(templateDir, config);
|
|
83
|
-
const targetPath = path.join(targetDir, config);
|
|
84
|
-
|
|
85
|
-
if (await fs.pathExists(sourcePath)) {
|
|
86
|
-
await fs.copy(sourcePath, targetPath, { overwrite: options.force });
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Copy GitHub Copilot instructions
|
|
91
|
-
const sourceGithubPath = path.join(templateDir, '.github', 'copilot-instructions.md');
|
|
92
|
-
const targetGithubPath = path.join(targetDir, '.github', 'copilot-instructions.md');
|
|
93
|
-
|
|
94
|
-
if (await fs.pathExists(sourceGithubPath)) {
|
|
95
|
-
await fs.copy(sourceGithubPath, targetGithubPath, { overwrite: options.force });
|
|
74
|
+
await fs.ensureDir(path.join(targetDir, '.github', 'instructions'));
|
|
75
|
+
|
|
76
|
+
const cursorSource = path.join(templateDir, '.cursorrules');
|
|
77
|
+
const cursorTarget = path.join(targetDir, '.cursorrules');
|
|
78
|
+
if (await fs.pathExists(cursorSource)) {
|
|
79
|
+
await fs.copy(cursorSource, cursorTarget, { overwrite: options.force });
|
|
96
80
|
}
|
|
81
|
+
|
|
82
|
+
await writeGeneratedInstructions(targetDir, templateDir, options.force);
|
|
97
83
|
}
|
|
98
84
|
}
|
|
99
85
|
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
|
|
4
|
+
const CORE_SOURCE_REL_PATH = 'instructions/source/core.md';
|
|
5
|
+
|
|
6
|
+
const GENERATED_INSTRUCTION_PATHS = {
|
|
7
|
+
canonical: {
|
|
8
|
+
generic: '.github/instructions/AGENTS.md',
|
|
9
|
+
copilot: '.github/instructions/copilot-instructions.md',
|
|
10
|
+
claude: '.github/instructions/CLAUDE.md',
|
|
11
|
+
gemini: '.github/instructions/GEMINI.md'
|
|
12
|
+
},
|
|
13
|
+
styleCompat: {
|
|
14
|
+
githubInstructions: '.github/instructions/agents.instructions.md',
|
|
15
|
+
claudeRules: '.claude/rules/claude.instructions.md'
|
|
16
|
+
},
|
|
17
|
+
compatibility: {
|
|
18
|
+
generic: 'AGENTS.MD',
|
|
19
|
+
copilot: '.github/copilot-instructions.md',
|
|
20
|
+
claude: 'CLAUDE.md',
|
|
21
|
+
gemini: 'GEMINI.md'
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function getLegacyCoreCandidates() {
|
|
26
|
+
return ['AGENTS.MD', 'AGENTS.md'];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function buildHeaders(toolName) {
|
|
30
|
+
return [
|
|
31
|
+
'<!-- GENERATED FILE - DO NOT EDIT DIRECTLY -->',
|
|
32
|
+
`<!-- Source of truth: ${CORE_SOURCE_REL_PATH} -->`,
|
|
33
|
+
`<!-- Tool profile: ${toolName} -->`,
|
|
34
|
+
''
|
|
35
|
+
].join('\n');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function buildCompatInstruction(toolName, corePath) {
|
|
39
|
+
const titles = {
|
|
40
|
+
generic: '# AGENTS Instructions',
|
|
41
|
+
copilot: '# GitHub Copilot Instructions',
|
|
42
|
+
claude: '# Claude Instructions',
|
|
43
|
+
gemini: '# Gemini Instructions'
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return [
|
|
47
|
+
`${titles[toolName]}`,
|
|
48
|
+
'',
|
|
49
|
+
`Primary policy source: \`${corePath}\`.`,
|
|
50
|
+
'',
|
|
51
|
+
'Always apply these mandatory rules:',
|
|
52
|
+
'- Security-first implementation (validate inputs, authz/authn, rate limiting).',
|
|
53
|
+
'- Testing-first delivery with unit + integration coverage for changed logic.',
|
|
54
|
+
'- Strong typing and runtime boundary validation.',
|
|
55
|
+
'- Do not expose secrets or sensitive data in logs/errors.',
|
|
56
|
+
'',
|
|
57
|
+
'Use the canonical source for full policy and architecture guidance.',
|
|
58
|
+
''
|
|
59
|
+
].join('\n');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function buildGeneratedArtifacts(coreContent) {
|
|
63
|
+
const corePath = CORE_SOURCE_REL_PATH;
|
|
64
|
+
const files = {};
|
|
65
|
+
|
|
66
|
+
files[GENERATED_INSTRUCTION_PATHS.canonical.generic] = `${buildHeaders('generic')}${coreContent.trim()}\n`;
|
|
67
|
+
files[GENERATED_INSTRUCTION_PATHS.canonical.copilot] = `${buildHeaders('copilot')}${buildCompatInstruction('copilot', corePath)}`;
|
|
68
|
+
files[GENERATED_INSTRUCTION_PATHS.canonical.claude] = `${buildHeaders('claude')}${buildCompatInstruction('claude', corePath)}`;
|
|
69
|
+
files[GENERATED_INSTRUCTION_PATHS.canonical.gemini] = `${buildHeaders('gemini')}${buildCompatInstruction('gemini', corePath)}`;
|
|
70
|
+
files[GENERATED_INSTRUCTION_PATHS.styleCompat.githubInstructions] = `${buildHeaders('github-instructions-style')}${buildCompatInstruction('generic', corePath)}`;
|
|
71
|
+
files[GENERATED_INSTRUCTION_PATHS.styleCompat.claudeRules] = `${buildHeaders('claude-rules-style')}${buildCompatInstruction('claude', corePath)}`;
|
|
72
|
+
|
|
73
|
+
files[GENERATED_INSTRUCTION_PATHS.compatibility.generic] = `${buildHeaders('generic-compat')}${buildCompatInstruction('generic', corePath)}`;
|
|
74
|
+
files[GENERATED_INSTRUCTION_PATHS.compatibility.copilot] = `${buildHeaders('copilot-compat')}${buildCompatInstruction('copilot', corePath)}`;
|
|
75
|
+
files[GENERATED_INSTRUCTION_PATHS.compatibility.claude] = `${buildHeaders('claude-compat')}${buildCompatInstruction('claude', corePath)}`;
|
|
76
|
+
files[GENERATED_INSTRUCTION_PATHS.compatibility.gemini] = `${buildHeaders('gemini-compat')}${buildCompatInstruction('gemini', corePath)}`;
|
|
77
|
+
|
|
78
|
+
return files;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function resolveCoreContent(targetDir, templateDir) {
|
|
82
|
+
const canonicalPath = path.join(targetDir, CORE_SOURCE_REL_PATH);
|
|
83
|
+
if (await fs.pathExists(canonicalPath)) {
|
|
84
|
+
return fs.readFile(canonicalPath, 'utf8');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (const legacyFile of getLegacyCoreCandidates()) {
|
|
88
|
+
const legacyPath = path.join(targetDir, legacyFile);
|
|
89
|
+
if (await fs.pathExists(legacyPath)) {
|
|
90
|
+
return fs.readFile(legacyPath, 'utf8');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const templateCorePath = path.join(templateDir, CORE_SOURCE_REL_PATH);
|
|
95
|
+
return fs.readFile(templateCorePath, 'utf8');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function ensureCoreSource(targetDir, templateDir, force = false) {
|
|
99
|
+
const targetCorePath = path.join(targetDir, CORE_SOURCE_REL_PATH);
|
|
100
|
+
|
|
101
|
+
if (await fs.pathExists(targetCorePath) && !force) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const coreContent = await resolveCoreContent(targetDir, templateDir);
|
|
106
|
+
await fs.ensureDir(path.dirname(targetCorePath));
|
|
107
|
+
await fs.writeFile(targetCorePath, coreContent, 'utf8');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function writeGeneratedInstructions(targetDir, templateDir, force = false) {
|
|
111
|
+
await ensureCoreSource(targetDir, templateDir, force);
|
|
112
|
+
const corePath = path.join(targetDir, CORE_SOURCE_REL_PATH);
|
|
113
|
+
const coreContent = await fs.readFile(corePath, 'utf8');
|
|
114
|
+
const artifacts = buildGeneratedArtifacts(coreContent);
|
|
115
|
+
|
|
116
|
+
for (const [relPath, content] of Object.entries(artifacts)) {
|
|
117
|
+
const targetPath = path.join(targetDir, relPath);
|
|
118
|
+
|
|
119
|
+
if (await fs.pathExists(targetPath) && !force) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
await fs.ensureDir(path.dirname(targetPath));
|
|
124
|
+
await fs.writeFile(targetPath, content, 'utf8');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function validateInstructionDrift(targetDir) {
|
|
129
|
+
const corePath = path.join(targetDir, CORE_SOURCE_REL_PATH);
|
|
130
|
+
if (!(await fs.pathExists(corePath))) {
|
|
131
|
+
return {
|
|
132
|
+
ok: false,
|
|
133
|
+
missingCore: true,
|
|
134
|
+
driftFiles: []
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const coreContent = await fs.readFile(corePath, 'utf8');
|
|
139
|
+
const expected = buildGeneratedArtifacts(coreContent);
|
|
140
|
+
const driftFiles = [];
|
|
141
|
+
|
|
142
|
+
for (const [relPath, expectedContent] of Object.entries(expected)) {
|
|
143
|
+
const filePath = path.join(targetDir, relPath);
|
|
144
|
+
if (!(await fs.pathExists(filePath))) {
|
|
145
|
+
driftFiles.push(relPath);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const actual = await fs.readFile(filePath, 'utf8');
|
|
150
|
+
if (actual !== expectedContent) {
|
|
151
|
+
driftFiles.push(relPath);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
ok: driftFiles.length === 0,
|
|
157
|
+
missingCore: false,
|
|
158
|
+
driftFiles
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
module.exports = {
|
|
163
|
+
CORE_SOURCE_REL_PATH,
|
|
164
|
+
GENERATED_INSTRUCTION_PATHS,
|
|
165
|
+
writeGeneratedInstructions,
|
|
166
|
+
validateInstructionDrift
|
|
167
|
+
};
|
package/lib/layout.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
|
|
4
|
+
const LAYOUT = {
|
|
5
|
+
canonical: {
|
|
6
|
+
docsDir: 'agent-docs',
|
|
7
|
+
rulesDir: '.github/instructions/rules',
|
|
8
|
+
skillsDir: '.github/skills'
|
|
9
|
+
},
|
|
10
|
+
legacy: {
|
|
11
|
+
rulesDirs: ['agents/rules'],
|
|
12
|
+
skillsDirs: ['agents/skills']
|
|
13
|
+
},
|
|
14
|
+
compatible: {
|
|
15
|
+
rulesDirs: ['.claude/rules', '.github/instructions'],
|
|
16
|
+
skillsDirs: ['.claude/skills', '.agents/skills']
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function firstExistingPath(baseDir, relativePaths) {
|
|
21
|
+
return relativePaths.find((relPath) => fs.existsSync(path.join(baseDir, relPath))) || null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function resolveRulesDir(baseDir) {
|
|
25
|
+
const candidates = [
|
|
26
|
+
LAYOUT.canonical.rulesDir,
|
|
27
|
+
...LAYOUT.compatible.rulesDirs,
|
|
28
|
+
...LAYOUT.legacy.rulesDirs
|
|
29
|
+
];
|
|
30
|
+
return firstExistingPath(baseDir, candidates) || LAYOUT.canonical.rulesDir;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function resolveSkillsDir(baseDir) {
|
|
34
|
+
const candidates = [
|
|
35
|
+
LAYOUT.canonical.skillsDir,
|
|
36
|
+
...LAYOUT.compatible.skillsDirs,
|
|
37
|
+
...LAYOUT.legacy.skillsDirs
|
|
38
|
+
];
|
|
39
|
+
return firstExistingPath(baseDir, candidates) || LAYOUT.canonical.skillsDir;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function hasAnyLayout(baseDir) {
|
|
43
|
+
const checks = [
|
|
44
|
+
path.join(baseDir, LAYOUT.canonical.rulesDir),
|
|
45
|
+
path.join(baseDir, LAYOUT.canonical.skillsDir),
|
|
46
|
+
...LAYOUT.compatible.rulesDirs.map((relPath) => path.join(baseDir, relPath)),
|
|
47
|
+
...LAYOUT.compatible.skillsDirs.map((relPath) => path.join(baseDir, relPath)),
|
|
48
|
+
...LAYOUT.legacy.rulesDirs.map((relPath) => path.join(baseDir, relPath)),
|
|
49
|
+
...LAYOUT.legacy.skillsDirs.map((relPath) => path.join(baseDir, relPath))
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
for (const checkPath of checks) {
|
|
53
|
+
if (await fs.pathExists(checkPath)) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function getLegacyMigrationPlan(baseDir) {
|
|
62
|
+
const plan = [];
|
|
63
|
+
|
|
64
|
+
for (const relPath of LAYOUT.legacy.rulesDirs) {
|
|
65
|
+
const from = path.join(baseDir, relPath);
|
|
66
|
+
if (await fs.pathExists(from)) {
|
|
67
|
+
plan.push({
|
|
68
|
+
type: 'directory',
|
|
69
|
+
source: relPath,
|
|
70
|
+
target: LAYOUT.canonical.rulesDir
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
for (const relPath of LAYOUT.legacy.skillsDirs) {
|
|
76
|
+
const from = path.join(baseDir, relPath);
|
|
77
|
+
if (await fs.pathExists(from)) {
|
|
78
|
+
plan.push({
|
|
79
|
+
type: 'directory',
|
|
80
|
+
source: relPath,
|
|
81
|
+
target: LAYOUT.canonical.skillsDir
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return plan;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = {
|
|
90
|
+
LAYOUT,
|
|
91
|
+
resolveRulesDir,
|
|
92
|
+
resolveSkillsDir,
|
|
93
|
+
hasAnyLayout,
|
|
94
|
+
getLegacyMigrationPlan
|
|
95
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agents-templated",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.11",
|
|
4
4
|
"description": "Technology-agnostic development template with multi-AI agent support (Cursor, Copilot, VSCode, Gemini), security-first patterns, and comprehensive testing guidelines",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"files": [
|
|
38
38
|
"bin",
|
|
39
|
+
"lib",
|
|
39
40
|
"templates",
|
|
40
41
|
"templates/presets",
|
|
41
42
|
"index.js",
|
package/templates/README.md
CHANGED
|
@@ -101,11 +101,11 @@ Agents Templated automatically configures 4 major AI coding assistants:
|
|
|
101
101
|
| AI Agent | Config File | Auto-Discovery |
|
|
102
102
|
|----------|-------------|----------------|
|
|
103
103
|
| **Cursor** | `.cursorrules` | ✅ Auto-loads in Cursor IDE |
|
|
104
|
-
| **GitHub Copilot** | `.github/copilot-instructions.md` | ✅ Auto-loads in VS Code |
|
|
105
|
-
| **Claude** | `CLAUDE.md` | ✅
|
|
106
|
-
| **Gemini** | `GEMINI.md` | ✅
|
|
104
|
+
| **GitHub Copilot** | `.github/instructions/copilot-instructions.md` (+ shim `.github/copilot-instructions.md`) | ✅ Auto-loads in VS Code |
|
|
105
|
+
| **Claude** | `.github/instructions/CLAUDE.md` (+ shim `CLAUDE.md`) | ✅ Compatible |
|
|
106
|
+
| **Gemini** | `.github/instructions/GEMINI.md` (+ shim `GEMINI.md`) | ✅ Compatible |
|
|
107
107
|
|
|
108
|
-
**
|
|
108
|
+
**Single source of truth:** `instructions/source/core.md` drives generated tool-compatible instruction files.
|
|
109
109
|
|
|
110
110
|
---
|
|
111
111
|
|
|
@@ -115,20 +115,36 @@ When you run `agents-templated init`, you get:
|
|
|
115
115
|
|
|
116
116
|
```
|
|
117
117
|
your-project/
|
|
118
|
+
├── instructions/
|
|
119
|
+
│ └── source/
|
|
120
|
+
│ └── core.md # Canonical instruction source of truth
|
|
121
|
+
│
|
|
118
122
|
├── agent-docs/ # 📚 Comprehensive documentation
|
|
119
|
-
│ ├── AGENTS.MD # AI assistant guide
|
|
120
123
|
│ ├── ARCHITECTURE.md # Project architecture & tech stack
|
|
121
124
|
│ └── README.md # Human-readable setup guide
|
|
122
125
|
│
|
|
123
|
-
├──
|
|
124
|
-
│ ├──
|
|
126
|
+
├── .github/
|
|
127
|
+
│ ├── instructions/ # Canonical generated instructions
|
|
128
|
+
│ │ ├── AGENTS.md
|
|
129
|
+
│ │ ├── copilot-instructions.md
|
|
130
|
+
│ │ ├── CLAUDE.md
|
|
131
|
+
│ │ ├── GEMINI.md
|
|
132
|
+
│ │ └── rules/
|
|
125
133
|
│ │ ├── core.mdc # Core development principles
|
|
126
134
|
│ │ ├── security.mdc # Security patterns (CRITICAL)
|
|
127
135
|
│ │ ├── testing.mdc # Testing strategy
|
|
128
136
|
│ │ ├── frontend.mdc # Frontend patterns
|
|
129
137
|
│ │ ├── database.mdc # Database patterns
|
|
130
138
|
│ │ └── style.mdc # Code style guidelines
|
|
131
|
-
│ ├──
|
|
139
|
+
│ ├── skills/
|
|
140
|
+
│ │ ├── find-skills/ # Skill discovery helper
|
|
141
|
+
│ │ ├── ui-ux-pro-max/ # Advanced UI/UX design implementation skill
|
|
142
|
+
│ │ ├── README.md # Guide for creating custom skills
|
|
143
|
+
│ │ └── [your-custom-skills]/ # Your project-specific skills
|
|
144
|
+
│ └── copilot-instructions.md # Compatibility shim for Copilot
|
|
145
|
+
│
|
|
146
|
+
├── agents/ # 🤖 Deterministic command contracts
|
|
147
|
+
│ └── commands/
|
|
132
148
|
│ │ ├── SCHEMA.md # Global slash-command response schema
|
|
133
149
|
│ │ ├── plan.md # /plan contract
|
|
134
150
|
│ │ ├── fix.md # /fix contract
|
|
@@ -136,17 +152,10 @@ your-project/
|
|
|
136
152
|
│ │ ├── release.md # /release contract
|
|
137
153
|
│ │ ├── ... # Other command contracts
|
|
138
154
|
│ │ └── README.md # Commands directory guide
|
|
139
|
-
│ └── skills/
|
|
140
|
-
│ ├── find-skills/ # Skill discovery helper
|
|
141
|
-
│ ├── ui-ux-pro-max/ # Advanced UI/UX design implementation skill
|
|
142
|
-
│ ├── README.md # Guide for creating custom skills
|
|
143
|
-
│ └── [your-custom-skills]/ # Your project-specific skills
|
|
144
|
-
│
|
|
145
|
-
├── .github/
|
|
146
|
-
│ └── copilot-instructions.md # GitHub Copilot config
|
|
147
155
|
│
|
|
148
|
-
├──
|
|
149
|
-
├──
|
|
156
|
+
├── AGENTS.MD # Compatibility shim for generic agents
|
|
157
|
+
├── CLAUDE.md # Compatibility shim for Claude tooling
|
|
158
|
+
├── GEMINI.md # Compatibility shim for Gemini tooling
|
|
150
159
|
├── .cursorrules # Cursor IDE config
|
|
151
160
|
├── .gitignore # Pre-configured Git ignore
|
|
152
161
|
└── README.md # Project documentation
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Canonical AI Instructions
|
|
2
|
+
|
|
3
|
+
This file is the single source of truth for project AI guidance.
|
|
4
|
+
|
|
5
|
+
## Always Apply
|
|
6
|
+
|
|
7
|
+
1. Security-first implementation:
|
|
8
|
+
- Validate all external inputs at boundaries.
|
|
9
|
+
- Enforce authentication and authorization on protected operations.
|
|
10
|
+
- Rate limit public endpoints.
|
|
11
|
+
- Never expose sensitive values in logs, errors, or responses.
|
|
12
|
+
|
|
13
|
+
2. Testing discipline:
|
|
14
|
+
- Add tests for changed business logic.
|
|
15
|
+
- Prefer focused unit tests first, then integration tests for boundaries.
|
|
16
|
+
- Keep critical workflows covered before release.
|
|
17
|
+
|
|
18
|
+
3. Type safety and reliability:
|
|
19
|
+
- Use strong typing where available.
|
|
20
|
+
- Add runtime validation at trust boundaries.
|
|
21
|
+
- Keep changes minimal, deterministic, and scoped to the requested task.
|
|
22
|
+
|
|
23
|
+
## Project References
|
|
24
|
+
|
|
25
|
+
- `agent-docs/ARCHITECTURE.md`
|
|
26
|
+
- `agents/rules/security.mdc`
|
|
27
|
+
- `agents/rules/testing.mdc`
|
|
28
|
+
- `agents/rules/core.mdc`
|
|
29
|
+
|
|
30
|
+
## Operational Rules
|
|
31
|
+
|
|
32
|
+
- Prefer smallest safe change set.
|
|
33
|
+
- Preserve existing APIs unless the task requires breaking changes.
|
|
34
|
+
- Do not commit secrets or credentials.
|
|
35
|
+
- Do not disable security controls to make tests pass.
|
|
36
|
+
|