forgedev 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +246 -246
  2. package/bin/devforge.js +4 -4
  3. package/package.json +33 -33
  4. package/src/claude-configurator.js +260 -260
  5. package/src/cli.js +119 -119
  6. package/src/composer.js +214 -214
  7. package/src/doctor-checks.js +743 -743
  8. package/src/doctor-prompts.js +295 -295
  9. package/src/doctor.js +281 -281
  10. package/src/guided.js +315 -315
  11. package/src/index.js +148 -148
  12. package/src/init-mode.js +138 -134
  13. package/src/prompts.js +155 -155
  14. package/src/scanner.js +368 -368
  15. package/templates/claude-code/agents/code-quality-reviewer.md +41 -41
  16. package/templates/claude-code/agents/production-readiness.md +55 -55
  17. package/templates/claude-code/agents/security-reviewer.md +41 -41
  18. package/templates/claude-code/agents/spec-validator.md +34 -34
  19. package/templates/claude-code/agents/uat-validator.md +37 -37
  20. package/templates/claude-code/claude-md/base.md +33 -33
  21. package/templates/claude-code/commands/done.md +19 -19
  22. package/templates/claude-code/commands/generate-prd.md +45 -45
  23. package/templates/claude-code/commands/generate-uat.md +35 -35
  24. package/templates/claude-code/commands/help.md +26 -26
  25. package/templates/claude-code/commands/next.md +20 -20
  26. package/templates/claude-code/commands/optimize-claude-md.md +31 -31
  27. package/templates/claude-code/commands/status.md +24 -24
  28. package/templates/claude-code/hooks/polyglot.json +36 -36
  29. package/templates/claude-code/hooks/python.json +36 -36
  30. package/templates/claude-code/hooks/scripts/autofix-polyglot.sh +16 -16
  31. package/templates/claude-code/hooks/scripts/autofix-python.sh +14 -14
  32. package/templates/claude-code/hooks/scripts/autofix-typescript.sh +14 -14
  33. package/templates/claude-code/hooks/scripts/guard-protected-files.sh +21 -21
  34. package/templates/claude-code/hooks/typescript.json +36 -36
@@ -1,260 +1,260 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { ROOT_DIR, ensureDir, writeFile, readTemplate, replaceVars, log } from './utils.js';
4
-
5
- const CLAUDE_TEMPLATES_DIR = path.join(ROOT_DIR, 'templates', 'claude-code');
6
- const DOCS_DIR = path.join(ROOT_DIR, 'docs');
7
-
8
- export async function generateClaudeConfig(outputDir, stackConfig, options = {}) {
9
- const vars = buildClaudeVars(stackConfig);
10
-
11
- if (!options.skipClaudeMd) {
12
- generateClaudeMd(outputDir, stackConfig, vars);
13
- }
14
- generateHooks(outputDir, stackConfig, { merge: options.mergeSettings });
15
- generateSkills(outputDir, stackConfig);
16
- generateAgents(outputDir, stackConfig, vars);
17
- generateCommands(outputDir, stackConfig, vars);
18
- copyPromptLibrary(outputDir);
19
- }
20
-
21
- function buildClaudeVars(config) {
22
- const vars = {
23
- PROJECT_NAME: config.projectName,
24
- PROJECT_NAME_PASCAL: config.projectName.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(''),
25
- };
26
-
27
- if (config.stackId === 'nextjs-fullstack') {
28
- vars.STACK_SUMMARY = 'Next.js 15 (App Router) + TypeScript + Tailwind CSS + Prisma + PostgreSQL';
29
- vars.LINT_COMMAND = 'npx eslint .';
30
- vars.TYPE_CHECK_COMMAND = 'npx tsc --noEmit';
31
- vars.TEST_COMMAND = 'npx vitest run';
32
- vars.BUILD_COMMAND = 'npm run build';
33
- vars.DEV_COMMAND = 'npm run dev';
34
- vars.DIR_MAP = `- src/app/ — Next.js App Router pages and API routes
35
- - src/lib/ — Shared utilities, database client, error handling
36
- - src/components/ — React components
37
- - prisma/ — Database schema and migrations
38
- - e2e/ — Playwright E2E tests`;
39
- } else if (config.stackId === 'fastapi-backend') {
40
- vars.STACK_SUMMARY = 'FastAPI + Python + SQLAlchemy 2.0 + PostgreSQL + Alembic';
41
- vars.LINT_COMMAND = 'ruff check .';
42
- vars.TYPE_CHECK_COMMAND = 'pyright';
43
- vars.TEST_COMMAND = 'pytest';
44
- vars.BUILD_COMMAND = 'docker build -t app .';
45
- vars.DEV_COMMAND = 'uvicorn app.main:app --reload';
46
- vars.DIR_MAP = `- backend/app/ — FastAPI application
47
- - backend/app/api/ — API route handlers
48
- - backend/app/core/ — Config, security, error handling
49
- - backend/app/db/ — Database session and models
50
- - backend/app/models/ — SQLAlchemy models
51
- - backend/app/schemas/ — Pydantic schemas
52
- - backend/tests/ — Pytest tests
53
- - backend/alembic/ — Database migrations`;
54
- } else if (config.stackId === 'polyglot-fullstack') {
55
- vars.STACK_SUMMARY = 'Next.js 15 (frontend) + FastAPI (backend) + PostgreSQL';
56
- vars.LINT_COMMAND = 'cd frontend && npx eslint . && cd ../backend && ruff check .';
57
- vars.TYPE_CHECK_COMMAND = 'cd frontend && npx tsc --noEmit';
58
- vars.TEST_COMMAND = 'cd frontend && npx vitest run && cd ../backend && pytest';
59
- vars.BUILD_COMMAND = 'docker compose build';
60
- vars.DEV_COMMAND = 'docker compose up';
61
- vars.DIR_MAP = `- frontend/ — Next.js 15 App Router application
62
- - frontend/src/app/ — Pages and API routes
63
- - frontend/src/lib/ — Shared utilities
64
- - backend/ — FastAPI application
65
- - backend/app/api/ — API route handlers
66
- - backend/app/core/ — Config, security, error handling
67
- - backend/app/db/ — Database session and models
68
- - e2e/ — Playwright E2E tests`;
69
- }
70
-
71
- // Build skills list for CLAUDE.md reference
72
- const skillsList = [];
73
- if (config.frontend?.framework === 'nextjs') {
74
- skillsList.push('- `@.claude/skills/nextjs/` — Next.js patterns and conventions');
75
- skillsList.push('- `@.claude/skills/security-web/` — Frontend security practices');
76
- }
77
- if (config.backend?.framework === 'fastapi') {
78
- skillsList.push('- `@.claude/skills/fastapi/` — FastAPI patterns and conventions');
79
- skillsList.push('- `@.claude/skills/security-api/` — API security practices');
80
- }
81
- if (config.testing?.e2e === 'playwright') {
82
- skillsList.push('- `@.claude/skills/playwright/` — E2E testing patterns');
83
- }
84
- if (config.ai) {
85
- skillsList.push('- `@.claude/skills/ai-prompts/` — AI/LLM prompt patterns');
86
- }
87
- vars.SKILLS_LIST = skillsList.length > 0 ? skillsList.join('\n') : '- (none generated)';
88
-
89
- return vars;
90
- }
91
-
92
- function generateClaudeMd(outputDir, config, vars) {
93
- // Read base template
94
- const basePath = path.join(CLAUDE_TEMPLATES_DIR, 'claude-md', 'base.md');
95
- let content = readTemplate(basePath);
96
-
97
- // Read stack-specific section
98
- let stackSection = '';
99
- if (config.stackId === 'nextjs-fullstack') {
100
- stackSection = readTemplate(path.join(CLAUDE_TEMPLATES_DIR, 'claude-md', 'nextjs.md'));
101
- } else if (config.stackId === 'fastapi-backend') {
102
- stackSection = readTemplate(path.join(CLAUDE_TEMPLATES_DIR, 'claude-md', 'fastapi.md'));
103
- } else if (config.stackId === 'polyglot-fullstack') {
104
- stackSection = readTemplate(path.join(CLAUDE_TEMPLATES_DIR, 'claude-md', 'fullstack.md'));
105
- }
106
-
107
- content = content.replace('{{STACK_SPECIFIC_RULES}}', stackSection);
108
- content = replaceVars(content, vars);
109
-
110
- writeFile(path.join(outputDir, 'CLAUDE.md'), content);
111
- }
112
-
113
- function generateHooks(outputDir, config, options = {}) {
114
- let hookFile;
115
- let scriptFiles = ['guard-protected-files.sh'];
116
-
117
- if (config.stackId === 'nextjs-fullstack') {
118
- hookFile = 'typescript.json';
119
- scriptFiles.push('autofix-typescript.sh');
120
- } else if (config.stackId === 'fastapi-backend') {
121
- hookFile = 'python.json';
122
- scriptFiles.push('autofix-python.sh');
123
- } else if (config.stackId === 'polyglot-fullstack') {
124
- hookFile = 'polyglot.json';
125
- scriptFiles.push('autofix-polyglot.sh');
126
- }
127
-
128
- const settingsPath = path.join(outputDir, '.claude', 'settings.json');
129
-
130
- if (options.merge && fs.existsSync(settingsPath)) {
131
- // Merge hooks into existing settings.json
132
- const existing = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
133
- const incoming = JSON.parse(readTemplate(path.join(CLAUDE_TEMPLATES_DIR, 'hooks', hookFile)));
134
- const merged = deepMergeSettings(existing, incoming);
135
- writeFile(settingsPath, JSON.stringify(merged, null, 2));
136
- } else {
137
- const hookPath = path.join(CLAUDE_TEMPLATES_DIR, 'hooks', hookFile);
138
- const content = readTemplate(hookPath);
139
- writeFile(settingsPath, content);
140
- }
141
-
142
- // Copy hook scripts
143
- const hooksDir = path.join(outputDir, '.claude', 'hooks');
144
- ensureDir(hooksDir);
145
- for (const script of scriptFiles) {
146
- const srcPath = path.join(CLAUDE_TEMPLATES_DIR, 'hooks', 'scripts', script);
147
- if (fs.existsSync(srcPath)) {
148
- fs.copyFileSync(srcPath, path.join(hooksDir, script));
149
- }
150
- }
151
- }
152
-
153
- function deepMergeSettings(existing, incoming) {
154
- const merged = { ...existing };
155
- if (incoming.hooks) {
156
- merged.hooks = merged.hooks || {};
157
- for (const [hookType, hookArr] of Object.entries(incoming.hooks)) {
158
- if (!merged.hooks[hookType]) {
159
- merged.hooks[hookType] = hookArr;
160
- } else {
161
- // Append incoming hooks that don't already exist (by command)
162
- const existingCommands = new Set(
163
- merged.hooks[hookType].flatMap(h => (h.hooks || []).map(hk => hk.command))
164
- );
165
- for (const hook of hookArr) {
166
- const isNew = (hook.hooks || []).some(hk => !existingCommands.has(hk.command));
167
- if (isNew) {
168
- merged.hooks[hookType].push(hook);
169
- }
170
- }
171
- }
172
- }
173
- }
174
- return merged;
175
- }
176
-
177
- function generateSkills(outputDir, config) {
178
- const skillsToInclude = [];
179
-
180
- if (config.frontend?.framework === 'nextjs') {
181
- skillsToInclude.push('nextjs');
182
- skillsToInclude.push('security-web');
183
- }
184
- if (config.backend?.framework === 'fastapi') {
185
- skillsToInclude.push('fastapi');
186
- skillsToInclude.push('security-api');
187
- }
188
- if (config.testing?.e2e === 'playwright') {
189
- skillsToInclude.push('playwright');
190
- }
191
- if (config.ai) {
192
- skillsToInclude.push('ai-prompts');
193
- }
194
-
195
- for (const skill of skillsToInclude) {
196
- const srcPath = path.join(CLAUDE_TEMPLATES_DIR, 'skills', skill, 'SKILL.md');
197
- if (fs.existsSync(srcPath)) {
198
- const destPath = path.join(outputDir, '.claude', 'skills', skill, 'SKILL.md');
199
- ensureDir(path.dirname(destPath));
200
- fs.copyFileSync(srcPath, destPath);
201
- }
202
- }
203
- }
204
-
205
- function generateAgents(outputDir, config, vars) {
206
- const agentsDir = path.join(CLAUDE_TEMPLATES_DIR, 'agents');
207
- const agents = [
208
- 'code-quality-reviewer.md',
209
- 'security-reviewer.md',
210
- 'spec-validator.md',
211
- 'production-readiness.md',
212
- 'uat-validator.md',
213
- ];
214
-
215
- for (const agent of agents) {
216
- const srcPath = path.join(agentsDir, agent);
217
- if (fs.existsSync(srcPath)) {
218
- let content = readTemplate(srcPath);
219
- content = replaceVars(content, vars);
220
- writeFile(path.join(outputDir, '.claude', 'agents', agent), content);
221
- }
222
- }
223
- }
224
-
225
- function generateCommands(outputDir, config, vars) {
226
- const commandsDir = path.join(CLAUDE_TEMPLATES_DIR, 'commands');
227
- const commands = [
228
- 'help.md',
229
- 'status.md',
230
- 'next.md',
231
- 'done.md',
232
- 'verify-all.md',
233
- 'audit-spec.md',
234
- 'audit-wiring.md',
235
- 'audit-security.md',
236
- 'pre-pr.md',
237
- 'run-uat.md',
238
- 'generate-prd.md',
239
- 'generate-uat.md',
240
- 'optimize-claude-md.md',
241
- ];
242
-
243
- for (const cmd of commands) {
244
- const srcPath = path.join(commandsDir, cmd);
245
- if (fs.existsSync(srcPath)) {
246
- let content = readTemplate(srcPath);
247
- content = replaceVars(content, vars);
248
- writeFile(path.join(outputDir, '.claude', 'commands', cmd), content);
249
- }
250
- }
251
- }
252
-
253
- function copyPromptLibrary(outputDir) {
254
- const srcPath = path.join(DOCS_DIR, '01-universal-prompt-library.md');
255
- if (fs.existsSync(srcPath)) {
256
- const destPath = path.join(outputDir, 'docs', 'prompt-library.md');
257
- ensureDir(path.dirname(destPath));
258
- fs.copyFileSync(srcPath, destPath);
259
- }
260
- }
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { ROOT_DIR, ensureDir, writeFile, readTemplate, replaceVars, log } from './utils.js';
4
+
5
+ const CLAUDE_TEMPLATES_DIR = path.join(ROOT_DIR, 'templates', 'claude-code');
6
+ const DOCS_DIR = path.join(ROOT_DIR, 'docs');
7
+
8
+ export async function generateClaudeConfig(outputDir, stackConfig, options = {}) {
9
+ const vars = buildClaudeVars(stackConfig);
10
+
11
+ if (!options.skipClaudeMd) {
12
+ generateClaudeMd(outputDir, stackConfig, vars);
13
+ }
14
+ generateHooks(outputDir, stackConfig, { merge: options.mergeSettings });
15
+ generateSkills(outputDir, stackConfig);
16
+ generateAgents(outputDir, stackConfig, vars);
17
+ generateCommands(outputDir, stackConfig, vars);
18
+ copyPromptLibrary(outputDir);
19
+ }
20
+
21
+ function buildClaudeVars(config) {
22
+ const vars = {
23
+ PROJECT_NAME: config.projectName,
24
+ PROJECT_NAME_PASCAL: config.projectName.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(''),
25
+ };
26
+
27
+ if (config.stackId === 'nextjs-fullstack') {
28
+ vars.STACK_SUMMARY = 'Next.js 15 (App Router) + TypeScript + Tailwind CSS + Prisma + PostgreSQL';
29
+ vars.LINT_COMMAND = 'npx eslint .';
30
+ vars.TYPE_CHECK_COMMAND = 'npx tsc --noEmit';
31
+ vars.TEST_COMMAND = 'npx vitest run';
32
+ vars.BUILD_COMMAND = 'npm run build';
33
+ vars.DEV_COMMAND = 'npm run dev';
34
+ vars.DIR_MAP = `- src/app/ — Next.js App Router pages and API routes
35
+ - src/lib/ — Shared utilities, database client, error handling
36
+ - src/components/ — React components
37
+ - prisma/ — Database schema and migrations
38
+ - e2e/ — Playwright E2E tests`;
39
+ } else if (config.stackId === 'fastapi-backend') {
40
+ vars.STACK_SUMMARY = 'FastAPI + Python + SQLAlchemy 2.0 + PostgreSQL + Alembic';
41
+ vars.LINT_COMMAND = 'ruff check .';
42
+ vars.TYPE_CHECK_COMMAND = 'pyright';
43
+ vars.TEST_COMMAND = 'pytest';
44
+ vars.BUILD_COMMAND = 'docker build -t app .';
45
+ vars.DEV_COMMAND = 'uvicorn app.main:app --reload';
46
+ vars.DIR_MAP = `- backend/app/ — FastAPI application
47
+ - backend/app/api/ — API route handlers
48
+ - backend/app/core/ — Config, security, error handling
49
+ - backend/app/db/ — Database session and models
50
+ - backend/app/models/ — SQLAlchemy models
51
+ - backend/app/schemas/ — Pydantic schemas
52
+ - backend/tests/ — Pytest tests
53
+ - backend/alembic/ — Database migrations`;
54
+ } else if (config.stackId === 'polyglot-fullstack') {
55
+ vars.STACK_SUMMARY = 'Next.js 15 (frontend) + FastAPI (backend) + PostgreSQL';
56
+ vars.LINT_COMMAND = 'cd frontend && npx eslint . && cd ../backend && ruff check .';
57
+ vars.TYPE_CHECK_COMMAND = 'cd frontend && npx tsc --noEmit';
58
+ vars.TEST_COMMAND = 'cd frontend && npx vitest run && cd ../backend && pytest';
59
+ vars.BUILD_COMMAND = 'docker compose build';
60
+ vars.DEV_COMMAND = 'docker compose up';
61
+ vars.DIR_MAP = `- frontend/ — Next.js 15 App Router application
62
+ - frontend/src/app/ — Pages and API routes
63
+ - frontend/src/lib/ — Shared utilities
64
+ - backend/ — FastAPI application
65
+ - backend/app/api/ — API route handlers
66
+ - backend/app/core/ — Config, security, error handling
67
+ - backend/app/db/ — Database session and models
68
+ - e2e/ — Playwright E2E tests`;
69
+ }
70
+
71
+ // Build skills list for CLAUDE.md reference
72
+ const skillsList = [];
73
+ if (config.frontend?.framework === 'nextjs') {
74
+ skillsList.push('- `@.claude/skills/nextjs/` — Next.js patterns and conventions');
75
+ skillsList.push('- `@.claude/skills/security-web/` — Frontend security practices');
76
+ }
77
+ if (config.backend?.framework === 'fastapi') {
78
+ skillsList.push('- `@.claude/skills/fastapi/` — FastAPI patterns and conventions');
79
+ skillsList.push('- `@.claude/skills/security-api/` — API security practices');
80
+ }
81
+ if (config.testing?.e2e === 'playwright') {
82
+ skillsList.push('- `@.claude/skills/playwright/` — E2E testing patterns');
83
+ }
84
+ if (config.ai) {
85
+ skillsList.push('- `@.claude/skills/ai-prompts/` — AI/LLM prompt patterns');
86
+ }
87
+ vars.SKILLS_LIST = skillsList.length > 0 ? skillsList.join('\n') : '- (none generated)';
88
+
89
+ return vars;
90
+ }
91
+
92
+ function generateClaudeMd(outputDir, config, vars) {
93
+ // Read base template
94
+ const basePath = path.join(CLAUDE_TEMPLATES_DIR, 'claude-md', 'base.md');
95
+ let content = readTemplate(basePath);
96
+
97
+ // Read stack-specific section
98
+ let stackSection = '';
99
+ if (config.stackId === 'nextjs-fullstack') {
100
+ stackSection = readTemplate(path.join(CLAUDE_TEMPLATES_DIR, 'claude-md', 'nextjs.md'));
101
+ } else if (config.stackId === 'fastapi-backend') {
102
+ stackSection = readTemplate(path.join(CLAUDE_TEMPLATES_DIR, 'claude-md', 'fastapi.md'));
103
+ } else if (config.stackId === 'polyglot-fullstack') {
104
+ stackSection = readTemplate(path.join(CLAUDE_TEMPLATES_DIR, 'claude-md', 'fullstack.md'));
105
+ }
106
+
107
+ content = content.replace('{{STACK_SPECIFIC_RULES}}', stackSection);
108
+ content = replaceVars(content, vars);
109
+
110
+ writeFile(path.join(outputDir, 'CLAUDE.md'), content);
111
+ }
112
+
113
+ function generateHooks(outputDir, config, options = {}) {
114
+ let hookFile;
115
+ let scriptFiles = ['guard-protected-files.sh'];
116
+
117
+ if (config.stackId === 'nextjs-fullstack') {
118
+ hookFile = 'typescript.json';
119
+ scriptFiles.push('autofix-typescript.sh');
120
+ } else if (config.stackId === 'fastapi-backend') {
121
+ hookFile = 'python.json';
122
+ scriptFiles.push('autofix-python.sh');
123
+ } else if (config.stackId === 'polyglot-fullstack') {
124
+ hookFile = 'polyglot.json';
125
+ scriptFiles.push('autofix-polyglot.sh');
126
+ }
127
+
128
+ const settingsPath = path.join(outputDir, '.claude', 'settings.json');
129
+
130
+ if (options.merge && fs.existsSync(settingsPath)) {
131
+ // Merge hooks into existing settings.json
132
+ const existing = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
133
+ const incoming = JSON.parse(readTemplate(path.join(CLAUDE_TEMPLATES_DIR, 'hooks', hookFile)));
134
+ const merged = deepMergeSettings(existing, incoming);
135
+ writeFile(settingsPath, JSON.stringify(merged, null, 2));
136
+ } else {
137
+ const hookPath = path.join(CLAUDE_TEMPLATES_DIR, 'hooks', hookFile);
138
+ const content = readTemplate(hookPath);
139
+ writeFile(settingsPath, content);
140
+ }
141
+
142
+ // Copy hook scripts
143
+ const hooksDir = path.join(outputDir, '.claude', 'hooks');
144
+ ensureDir(hooksDir);
145
+ for (const script of scriptFiles) {
146
+ const srcPath = path.join(CLAUDE_TEMPLATES_DIR, 'hooks', 'scripts', script);
147
+ if (fs.existsSync(srcPath)) {
148
+ fs.copyFileSync(srcPath, path.join(hooksDir, script));
149
+ }
150
+ }
151
+ }
152
+
153
+ function deepMergeSettings(existing, incoming) {
154
+ const merged = { ...existing };
155
+ if (incoming.hooks) {
156
+ merged.hooks = merged.hooks || {};
157
+ for (const [hookType, hookArr] of Object.entries(incoming.hooks)) {
158
+ if (!merged.hooks[hookType]) {
159
+ merged.hooks[hookType] = hookArr;
160
+ } else {
161
+ // Append incoming hooks that don't already exist (by command)
162
+ const existingCommands = new Set(
163
+ merged.hooks[hookType].flatMap(h => (h.hooks || []).map(hk => hk.command))
164
+ );
165
+ for (const hook of hookArr) {
166
+ const isNew = (hook.hooks || []).some(hk => !existingCommands.has(hk.command));
167
+ if (isNew) {
168
+ merged.hooks[hookType].push(hook);
169
+ }
170
+ }
171
+ }
172
+ }
173
+ }
174
+ return merged;
175
+ }
176
+
177
+ function generateSkills(outputDir, config) {
178
+ const skillsToInclude = [];
179
+
180
+ if (config.frontend?.framework === 'nextjs') {
181
+ skillsToInclude.push('nextjs');
182
+ skillsToInclude.push('security-web');
183
+ }
184
+ if (config.backend?.framework === 'fastapi') {
185
+ skillsToInclude.push('fastapi');
186
+ skillsToInclude.push('security-api');
187
+ }
188
+ if (config.testing?.e2e === 'playwright') {
189
+ skillsToInclude.push('playwright');
190
+ }
191
+ if (config.ai) {
192
+ skillsToInclude.push('ai-prompts');
193
+ }
194
+
195
+ for (const skill of skillsToInclude) {
196
+ const srcPath = path.join(CLAUDE_TEMPLATES_DIR, 'skills', skill, 'SKILL.md');
197
+ if (fs.existsSync(srcPath)) {
198
+ const destPath = path.join(outputDir, '.claude', 'skills', skill, 'SKILL.md');
199
+ ensureDir(path.dirname(destPath));
200
+ fs.copyFileSync(srcPath, destPath);
201
+ }
202
+ }
203
+ }
204
+
205
+ function generateAgents(outputDir, config, vars) {
206
+ const agentsDir = path.join(CLAUDE_TEMPLATES_DIR, 'agents');
207
+ const agents = [
208
+ 'code-quality-reviewer.md',
209
+ 'security-reviewer.md',
210
+ 'spec-validator.md',
211
+ 'production-readiness.md',
212
+ 'uat-validator.md',
213
+ ];
214
+
215
+ for (const agent of agents) {
216
+ const srcPath = path.join(agentsDir, agent);
217
+ if (fs.existsSync(srcPath)) {
218
+ let content = readTemplate(srcPath);
219
+ content = replaceVars(content, vars);
220
+ writeFile(path.join(outputDir, '.claude', 'agents', agent), content);
221
+ }
222
+ }
223
+ }
224
+
225
+ function generateCommands(outputDir, config, vars) {
226
+ const commandsDir = path.join(CLAUDE_TEMPLATES_DIR, 'commands');
227
+ const commands = [
228
+ 'help.md',
229
+ 'status.md',
230
+ 'next.md',
231
+ 'done.md',
232
+ 'verify-all.md',
233
+ 'audit-spec.md',
234
+ 'audit-wiring.md',
235
+ 'audit-security.md',
236
+ 'pre-pr.md',
237
+ 'run-uat.md',
238
+ 'generate-prd.md',
239
+ 'generate-uat.md',
240
+ 'optimize-claude-md.md',
241
+ ];
242
+
243
+ for (const cmd of commands) {
244
+ const srcPath = path.join(commandsDir, cmd);
245
+ if (fs.existsSync(srcPath)) {
246
+ let content = readTemplate(srcPath);
247
+ content = replaceVars(content, vars);
248
+ writeFile(path.join(outputDir, '.claude', 'commands', cmd), content);
249
+ }
250
+ }
251
+ }
252
+
253
+ function copyPromptLibrary(outputDir) {
254
+ const srcPath = path.join(DOCS_DIR, '01-universal-prompt-library.md');
255
+ if (fs.existsSync(srcPath)) {
256
+ const destPath = path.join(outputDir, 'docs', 'prompt-library.md');
257
+ ensureDir(path.dirname(destPath));
258
+ fs.copyFileSync(srcPath, destPath);
259
+ }
260
+ }