forgedev 1.2.0 → 1.4.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 +57 -10
- package/bin/chainproof.js +126 -0
- package/bin/devforge.js +1 -1
- package/package.json +25 -7
- package/src/chainproof-bridge.js +330 -0
- package/src/ci-mode.js +85 -0
- package/src/claude-configurator.js +171 -78
- package/src/cli.js +30 -7
- package/src/composer.js +242 -214
- package/src/doctor-checks-chainproof.js +106 -0
- package/src/doctor-checks.js +39 -20
- package/src/doctor-prompts.js +9 -9
- package/src/doctor.js +37 -4
- package/src/guided.js +3 -3
- package/src/index.js +31 -10
- package/src/init-mode.js +76 -12
- package/src/menu.js +178 -0
- package/src/prompts.js +5 -12
- package/src/recommender.js +163 -30
- package/src/scanner.js +57 -2
- package/src/uat-generator.js +204 -189
- package/src/update-check.js +9 -4
- package/src/update.js +57 -13
- package/src/utils.js +162 -5
- package/templates/ai/guardrails-py/backend/app/ai/__init__.py +29 -0
- package/templates/ai/guardrails-py/backend/app/ai/audit_log.py +133 -0
- package/templates/ai/guardrails-py/backend/app/ai/client.py.template +323 -0
- package/templates/ai/guardrails-py/backend/app/ai/health.py.template +157 -0
- package/templates/ai/guardrails-py/backend/app/ai/input_guard.py +98 -0
- package/templates/ai/guardrails-ts/src/lib/ai/audit-log.ts.template +164 -0
- package/templates/ai/guardrails-ts/src/lib/ai/client.ts.template +403 -0
- package/templates/ai/guardrails-ts/src/lib/ai/health.ts.template +165 -0
- package/templates/ai/guardrails-ts/src/lib/ai/index.ts.template +17 -0
- package/templates/ai/guardrails-ts/src/lib/ai/input-guard.ts.template +124 -0
- package/templates/auth/nextauth/src/lib/auth.ts.template +12 -7
- package/templates/backend/express/Dockerfile.template +18 -0
- package/templates/backend/express/package.json.template +33 -0
- package/templates/backend/express/src/index.ts.template +34 -0
- package/templates/backend/express/src/routes/health.ts.template +27 -0
- package/templates/backend/express/tsconfig.json +17 -0
- package/templates/backend/fastapi/backend/Dockerfile.template +5 -0
- package/templates/backend/fastapi/backend/app/api/health.py.template +1 -1
- package/templates/backend/fastapi/backend/app/core/config.py.template +1 -1
- package/templates/backend/fastapi/backend/app/core/errors.py +1 -1
- package/templates/backend/fastapi/backend/app/main.py.template +3 -1
- package/templates/backend/fastapi/backend/requirements.txt.template +2 -0
- package/templates/backend/hono/Dockerfile.template +18 -0
- package/templates/backend/hono/package.json.template +31 -0
- package/templates/backend/hono/src/index.ts.template +32 -0
- package/templates/backend/hono/src/routes/health.ts.template +27 -0
- package/templates/backend/hono/tsconfig.json +18 -0
- package/templates/base/.gitignore.template +3 -0
- package/templates/base/docs/uat/UAT_TEMPLATE.md.template +1 -1
- package/templates/chainproof/base/.chainproof/config.json.template +11 -0
- package/templates/chainproof/base/.chainproof/mcp-server.mjs +310 -0
- package/templates/chainproof/base/.mcp.json +9 -0
- package/templates/chainproof/fastapi/.chainproof/middleware.json.template +14 -0
- package/templates/chainproof/nextjs/.chainproof/hooks.json.template +19 -0
- package/templates/chainproof/polyglot/.chainproof/config.json.template +21 -0
- package/templates/claude-code/agents/architect.md +25 -11
- package/templates/claude-code/agents/build-error-resolver.md +19 -5
- package/templates/claude-code/agents/chief-of-staff.md +42 -8
- package/templates/claude-code/agents/code-quality-reviewer.md +14 -0
- package/templates/claude-code/agents/database-reviewer.md +15 -1
- package/templates/claude-code/agents/deep-reviewer.md +191 -0
- package/templates/claude-code/agents/doc-updater.md +19 -5
- package/templates/claude-code/agents/docs-lookup.md +19 -5
- package/templates/claude-code/agents/e2e-runner.md +26 -12
- package/templates/claude-code/agents/enforcement-gate.md +102 -0
- package/templates/claude-code/agents/frontend-builder.md +188 -0
- package/templates/claude-code/agents/harness-optimizer.md +36 -1
- package/templates/claude-code/agents/loop-operator.md +27 -13
- package/templates/claude-code/agents/planner.md +21 -7
- package/templates/claude-code/agents/product-strategist.md +24 -10
- package/templates/claude-code/agents/production-readiness.md +14 -0
- package/templates/claude-code/agents/prompt-auditor.md +115 -0
- package/templates/claude-code/agents/refactor-cleaner.md +22 -8
- package/templates/claude-code/agents/security-reviewer.md +14 -0
- package/templates/claude-code/agents/spec-validator.md +15 -1
- package/templates/claude-code/agents/tdd-guide.md +21 -7
- package/templates/claude-code/agents/uat-validator.md +14 -0
- package/templates/claude-code/claude-md/base.md +14 -7
- package/templates/claude-code/claude-md/fastapi.md +8 -8
- package/templates/claude-code/claude-md/fullstack.md +6 -6
- package/templates/claude-code/claude-md/hono.md +18 -0
- package/templates/claude-code/claude-md/nextjs.md +5 -5
- package/templates/claude-code/claude-md/remix.md +18 -0
- package/templates/claude-code/commands/audit-security.md +14 -0
- package/templates/claude-code/commands/audit-spec.md +14 -0
- package/templates/claude-code/commands/audit-wiring.md +14 -0
- package/templates/claude-code/commands/build-fix.md +28 -0
- package/templates/claude-code/commands/build-ui.md +59 -0
- package/templates/claude-code/commands/code-review.md +53 -31
- package/templates/claude-code/commands/fix-loop.md +211 -0
- package/templates/claude-code/commands/full-audit.md +36 -8
- package/templates/claude-code/commands/generate-prd.md +1 -1
- package/templates/claude-code/commands/generate-sdd.md +74 -0
- package/templates/claude-code/commands/generate-uat.md +107 -35
- package/templates/claude-code/commands/help.md +68 -0
- package/templates/claude-code/commands/live-uat.md +268 -0
- package/templates/claude-code/commands/optimize-claude-md.md +15 -1
- package/templates/claude-code/commands/plan.md +3 -3
- package/templates/claude-code/commands/pre-pr.md +57 -19
- package/templates/claude-code/commands/product-strategist.md +21 -0
- package/templates/claude-code/commands/resume-session.md +10 -10
- package/templates/claude-code/commands/run-uat.md +59 -2
- package/templates/claude-code/commands/save-session.md +10 -10
- package/templates/claude-code/commands/simplify.md +36 -0
- package/templates/claude-code/commands/tdd.md +17 -18
- package/templates/claude-code/commands/verify-all.md +24 -0
- package/templates/claude-code/commands/verify-intent.md +55 -0
- package/templates/claude-code/commands/workflows.md +52 -40
- package/templates/claude-code/hooks/polyglot.json +10 -1
- package/templates/claude-code/hooks/python.json +10 -1
- package/templates/claude-code/hooks/scripts/autofix-polyglot.mjs +2 -2
- package/templates/claude-code/hooks/scripts/autofix-python.mjs +1 -1
- package/templates/claude-code/hooks/scripts/autofix-typescript.mjs +1 -1
- package/templates/claude-code/hooks/scripts/code-hygiene.mjs +293 -0
- package/templates/claude-code/hooks/scripts/pre-commit-gate.mjs +207 -0
- package/templates/claude-code/hooks/typescript.json +10 -1
- package/templates/claude-code/skills/ai-prompts/SKILL.md +119 -41
- package/templates/claude-code/skills/git-workflow/SKILL.md +5 -5
- package/templates/claude-code/skills/nextjs/SKILL.md +1 -1
- package/templates/claude-code/skills/playwright/SKILL.md +5 -5
- package/templates/claude-code/skills/security-api/SKILL.md +1 -1
- package/templates/claude-code/skills/security-web/SKILL.md +1 -1
- package/templates/claude-code/skills/testing-patterns/SKILL.md +9 -9
- package/templates/database/prisma-postgres/{.env.example → .env.example.template} +1 -0
- package/templates/database/sqlalchemy-postgres/{.env.example → .env.example.template} +1 -0
- package/templates/docs-portal/fastapi/backend/app/portal/__pycache__/docs_reader.cpython-314.pyc +0 -0
- package/templates/docs-portal/fastapi/backend/app/portal/docs_reader.py +201 -0
- package/templates/docs-portal/fastapi/backend/app/portal/html_renderer.py +229 -0
- package/templates/docs-portal/fastapi/backend/app/portal/router.py.template +35 -0
- package/templates/docs-portal/nextjs/src/app/portal/[category]/[slug]/page.tsx +81 -0
- package/templates/docs-portal/nextjs/src/app/portal/[category]/page.tsx +65 -0
- package/templates/docs-portal/nextjs/src/app/portal/layout.tsx.template +54 -0
- package/templates/docs-portal/nextjs/src/app/portal/page.tsx +85 -0
- package/templates/docs-portal/nextjs/src/components/portal/markdown-renderer.tsx +101 -0
- package/templates/docs-portal/nextjs/src/components/portal/mobile-portal-nav.tsx +81 -0
- package/templates/docs-portal/nextjs/src/components/portal/portal-nav.tsx +86 -0
- package/templates/docs-portal/nextjs/src/lib/docs.ts +139 -0
- package/templates/frontend/nextjs/package.json.template +3 -1
- package/templates/frontend/react/index.html.template +12 -0
- package/templates/frontend/react/package.json.template +34 -0
- package/templates/frontend/react/src/App.tsx.template +10 -0
- package/templates/frontend/react/src/index.css +1 -0
- package/templates/frontend/react/src/main.tsx +10 -0
- package/templates/frontend/react/tsconfig.json +17 -0
- package/templates/frontend/react/vite.config.ts.template +15 -0
- package/templates/frontend/react/vitest.config.ts +9 -0
- package/templates/frontend/remix/app/root.tsx.template +31 -0
- package/templates/frontend/remix/app/routes/_index.tsx.template +19 -0
- package/templates/frontend/remix/app/routes/api.health.ts.template +10 -0
- package/templates/frontend/remix/app/tailwind.css +1 -0
- package/templates/frontend/remix/package.json.template +39 -0
- package/templates/frontend/remix/tsconfig.json +18 -0
- package/templates/frontend/remix/vite.config.ts.template +7 -0
- package/templates/infra/github-actions/.github/workflows/ci.yml.template +3 -0
- package/templates/infra/k8s/k8s/deployment.yml.template +70 -0
- package/templates/infra/k8s/k8s/hpa.yml.template +24 -0
- package/templates/infra/k8s/k8s/ingress.yml.template +26 -0
- package/templates/infra/k8s/k8s/kustomization.yml.template +13 -0
- package/templates/infra/k8s/k8s/namespace.yml.template +4 -0
- package/templates/infra/k8s/k8s/networkpolicy.yml.template +41 -0
- package/templates/infra/k8s/k8s/secrets.yml.template +10 -0
- package/templates/infra/k8s/k8s/service.yml.template +15 -0
- package/templates/testing/load/k6/README.md.template +48 -0
- package/templates/testing/load/k6/load-test.js.template +57 -0
- package/docs/00-README.md +0 -310
- package/docs/01-universal-prompt-library.md +0 -1049
- package/docs/02-claude-code-mastery-playbook.md +0 -283
- package/docs/03-multi-agent-verification.md +0 -565
- package/docs/04-errata-and-verification-checklist.md +0 -284
- package/docs/05-universal-scaffolder-vision.md +0 -452
- package/docs/06-confidence-assessment-and-repo-prompt.md +0 -407
- package/docs/errata.md +0 -58
- package/docs/multi-agent-verification.md +0 -66
- package/docs/playbook.md +0 -95
- package/docs/prompt-library.md +0 -160
- package/docs/uat/UAT_CHECKLIST.csv +0 -9
- package/docs/uat/UAT_TEMPLATE.md +0 -163
- package/templates/claude-code/commands/done.md +0 -19
- /package/{docs/plans/.gitkeep → templates/docs-portal/fastapi/backend/app/portal/__init__.py} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { ROOT_DIR, ensureDir, writeFile, readTemplate, replaceVars,
|
|
3
|
+
import { ROOT_DIR, ensureDir, writeFile, readTemplate, replaceVars, getStackCommands, toPascalCase } from './utils.js';
|
|
4
4
|
|
|
5
5
|
const CLAUDE_TEMPLATES_DIR = path.join(ROOT_DIR, 'templates', 'claude-code');
|
|
6
6
|
const DOCS_DIR = path.join(ROOT_DIR, 'docs');
|
|
@@ -13,79 +13,108 @@ export async function generateClaudeConfig(outputDir, stackConfig, options = {})
|
|
|
13
13
|
}
|
|
14
14
|
generateHooks(outputDir, stackConfig, { merge: options.mergeSettings });
|
|
15
15
|
generateSkills(outputDir, stackConfig);
|
|
16
|
-
generateAgents(outputDir, stackConfig, vars);
|
|
17
|
-
generateCommands(outputDir, stackConfig, vars);
|
|
16
|
+
const agents = generateAgents(outputDir, stackConfig, vars);
|
|
17
|
+
const commands = generateCommands(outputDir, stackConfig, vars);
|
|
18
18
|
copyPromptLibrary(outputDir);
|
|
19
|
+
stampVersion(outputDir);
|
|
20
|
+
|
|
21
|
+
return { agents, commands };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function stampVersion(outputDir) {
|
|
25
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT_DIR, 'package.json'), 'utf-8'));
|
|
26
|
+
writeFile(path.join(outputDir, '.claude', '.devforge-version'), JSON.stringify({
|
|
27
|
+
version: pkg.version,
|
|
28
|
+
generatedAt: new Date().toISOString(),
|
|
29
|
+
}, null, 2) + '\n');
|
|
19
30
|
}
|
|
20
31
|
|
|
21
32
|
function buildClaudeVars(config) {
|
|
22
33
|
const vars = {
|
|
23
34
|
PROJECT_NAME: config.projectName,
|
|
24
|
-
PROJECT_NAME_PASCAL: config.projectName
|
|
35
|
+
PROJECT_NAME_PASCAL: toPascalCase(config.projectName),
|
|
25
36
|
};
|
|
26
37
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
- src/
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
- backend/
|
|
48
|
-
- backend/
|
|
49
|
-
|
|
50
|
-
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
-
|
|
65
|
-
- backend/
|
|
66
|
-
- backend/
|
|
67
|
-
- backend/
|
|
68
|
-
-
|
|
38
|
+
// Commands vary by stack (shared with composer)
|
|
39
|
+
Object.assign(vars, getStackCommands(config.stackId));
|
|
40
|
+
|
|
41
|
+
const claudeVars = {
|
|
42
|
+
'nextjs-fullstack': {
|
|
43
|
+
STACK_SUMMARY: 'Next.js 15 (App Router) + TypeScript + Tailwind CSS + Prisma + PostgreSQL',
|
|
44
|
+
DIR_MAP: `- src/app/ - Next.js App Router pages and API routes
|
|
45
|
+
- src/lib/ - Shared utilities, database client, error handling
|
|
46
|
+
- src/components/ - React components
|
|
47
|
+
- prisma/ - Database schema and migrations
|
|
48
|
+
- e2e/ - Playwright E2E tests`,
|
|
49
|
+
},
|
|
50
|
+
'fastapi-backend': {
|
|
51
|
+
STACK_SUMMARY: 'FastAPI + Python + SQLAlchemy 2.0 + PostgreSQL + Alembic',
|
|
52
|
+
DIR_MAP: `- backend/app/ - FastAPI application
|
|
53
|
+
- backend/app/api/ - API route handlers
|
|
54
|
+
- backend/app/core/ - Config, security, error handling
|
|
55
|
+
- backend/app/db/ - Database session and models
|
|
56
|
+
- backend/app/models/ - SQLAlchemy models
|
|
57
|
+
- backend/app/schemas/ - Pydantic schemas
|
|
58
|
+
- backend/tests/ - Pytest tests
|
|
59
|
+
- backend/alembic/ - Database migrations`,
|
|
60
|
+
},
|
|
61
|
+
'polyglot-fullstack': {
|
|
62
|
+
STACK_SUMMARY: 'Next.js 15 (frontend) + FastAPI (backend) + PostgreSQL',
|
|
63
|
+
DIR_MAP: `- frontend/ - Next.js 15 App Router application
|
|
64
|
+
- frontend/src/app/ - Pages and API routes
|
|
65
|
+
- frontend/src/lib/ - Shared utilities
|
|
66
|
+
- backend/ - FastAPI application
|
|
67
|
+
- backend/app/api/ - API route handlers
|
|
68
|
+
- backend/app/core/ - Config, security, error handling
|
|
69
|
+
- backend/app/db/ - Database session and models
|
|
70
|
+
- e2e/ - Playwright E2E tests`,
|
|
71
|
+
},
|
|
72
|
+
'react-express': {
|
|
73
|
+
STACK_SUMMARY: 'React (Vite) + Express + TypeScript + Prisma + PostgreSQL',
|
|
74
|
+
DIR_MAP: `- frontend/ - React (Vite) SPA
|
|
75
|
+
- frontend/src/ - React components and pages
|
|
76
|
+
- backend/ - Express API server
|
|
77
|
+
- backend/src/ - Express routes and middleware
|
|
78
|
+
- backend/src/routes/ - API route handlers
|
|
79
|
+
- backend/prisma/ - Database schema and migrations`,
|
|
80
|
+
},
|
|
81
|
+
'remix-fullstack': {
|
|
82
|
+
STACK_SUMMARY: 'Remix + Vite + TypeScript + Tailwind CSS + Prisma + PostgreSQL',
|
|
83
|
+
DIR_MAP: `- app/ - Remix application
|
|
84
|
+
- app/routes/ - File-based routes and API resource routes
|
|
85
|
+
- app/routes/api.health.ts - Health check endpoint
|
|
86
|
+
- prisma/ - Database schema and migrations`,
|
|
87
|
+
},
|
|
88
|
+
'hono-api': {
|
|
89
|
+
STACK_SUMMARY: 'Hono + TypeScript + Prisma + PostgreSQL',
|
|
90
|
+
DIR_MAP: `- src/ - Hono application
|
|
91
|
+
- src/routes/ - API route handlers
|
|
92
|
+
- src/routes/health.ts - Health check endpoints
|
|
93
|
+
- prisma/ - Database schema and migrations`,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
if (claudeVars[config.stackId]) {
|
|
97
|
+
Object.assign(vars, claudeVars[config.stackId]);
|
|
69
98
|
}
|
|
70
99
|
|
|
71
100
|
// Build skills list for CLAUDE.md reference
|
|
72
101
|
const skillsList = [
|
|
73
|
-
'- `@.claude/skills/git-workflow
|
|
74
|
-
'- `@.claude/skills/testing-patterns
|
|
102
|
+
'- `@.claude/skills/git-workflow/`: Git branching, commits, and PR workflow',
|
|
103
|
+
'- `@.claude/skills/testing-patterns/`: Test pyramid, AAA pattern, mocking strategies',
|
|
75
104
|
];
|
|
76
105
|
if (config.frontend?.framework === 'nextjs') {
|
|
77
|
-
skillsList.push('- `@.claude/skills/nextjs
|
|
78
|
-
skillsList.push('- `@.claude/skills/security-web
|
|
106
|
+
skillsList.push('- `@.claude/skills/nextjs/`: Next.js patterns and conventions');
|
|
107
|
+
skillsList.push('- `@.claude/skills/security-web/`: Frontend security practices');
|
|
79
108
|
}
|
|
80
109
|
if (config.backend?.framework === 'fastapi') {
|
|
81
|
-
skillsList.push('- `@.claude/skills/fastapi
|
|
82
|
-
skillsList.push('- `@.claude/skills/security-api
|
|
110
|
+
skillsList.push('- `@.claude/skills/fastapi/`: FastAPI patterns and conventions');
|
|
111
|
+
skillsList.push('- `@.claude/skills/security-api/`: API security practices');
|
|
83
112
|
}
|
|
84
113
|
if (config.testing?.e2e === 'playwright') {
|
|
85
|
-
skillsList.push('- `@.claude/skills/playwright
|
|
114
|
+
skillsList.push('- `@.claude/skills/playwright/`: E2E testing patterns');
|
|
86
115
|
}
|
|
87
116
|
if (config.ai) {
|
|
88
|
-
skillsList.push('- `@.claude/skills/ai-prompts
|
|
117
|
+
skillsList.push('- `@.claude/skills/ai-prompts/`: AI/LLM prompt patterns');
|
|
89
118
|
}
|
|
90
119
|
vars.SKILLS_LIST = skillsList.length > 0 ? skillsList.join('\n') : '- (none generated)';
|
|
91
120
|
|
|
@@ -98,14 +127,18 @@ function generateClaudeMd(outputDir, config, vars) {
|
|
|
98
127
|
let content = readTemplate(basePath);
|
|
99
128
|
|
|
100
129
|
// Read stack-specific section
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
130
|
+
const claudeMdTemplates = {
|
|
131
|
+
'nextjs-fullstack': 'nextjs.md',
|
|
132
|
+
'fastapi-backend': 'fastapi.md',
|
|
133
|
+
'polyglot-fullstack': 'fullstack.md',
|
|
134
|
+
'react-express': 'fullstack.md',
|
|
135
|
+
'remix-fullstack': 'remix.md',
|
|
136
|
+
'hono-api': 'hono.md',
|
|
137
|
+
};
|
|
138
|
+
const mdFile = claudeMdTemplates[config.stackId];
|
|
139
|
+
const stackSection = mdFile
|
|
140
|
+
? readTemplate(path.join(CLAUDE_TEMPLATES_DIR, 'claude-md', mdFile))
|
|
141
|
+
: '';
|
|
109
142
|
|
|
110
143
|
content = content.replace('{{STACK_SPECIFIC_RULES}}', stackSection);
|
|
111
144
|
content = replaceVars(content, vars);
|
|
@@ -114,18 +147,19 @@ function generateClaudeMd(outputDir, config, vars) {
|
|
|
114
147
|
}
|
|
115
148
|
|
|
116
149
|
function generateHooks(outputDir, config, options = {}) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
150
|
+
const hookConfig = {
|
|
151
|
+
'nextjs-fullstack': { file: 'typescript.json', autofix: 'autofix-typescript.mjs' },
|
|
152
|
+
'fastapi-backend': { file: 'python.json', autofix: 'autofix-python.mjs' },
|
|
153
|
+
'polyglot-fullstack': { file: 'polyglot.json', autofix: 'autofix-polyglot.mjs' },
|
|
154
|
+
'react-express': { file: 'typescript.json', autofix: 'autofix-typescript.mjs' },
|
|
155
|
+
'remix-fullstack': { file: 'typescript.json', autofix: 'autofix-typescript.mjs' },
|
|
156
|
+
'hono-api': { file: 'typescript.json', autofix: 'autofix-typescript.mjs' },
|
|
157
|
+
};
|
|
158
|
+
const hook = hookConfig[config.stackId];
|
|
159
|
+
const hookFile = hook?.file;
|
|
160
|
+
const scriptFiles = ['guard-protected-files.mjs', 'code-hygiene.mjs', 'pre-commit-gate.mjs'];
|
|
161
|
+
if (hook?.autofix) {
|
|
162
|
+
scriptFiles.push(hook.autofix);
|
|
129
163
|
}
|
|
130
164
|
|
|
131
165
|
const settingsPath = path.join(outputDir, '.claude', 'settings.json');
|
|
@@ -134,7 +168,7 @@ function generateHooks(outputDir, config, options = {}) {
|
|
|
134
168
|
// Merge hooks into existing settings.json
|
|
135
169
|
const existing = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
136
170
|
const incoming = JSON.parse(readTemplate(path.join(CLAUDE_TEMPLATES_DIR, 'hooks', hookFile)));
|
|
137
|
-
const merged =
|
|
171
|
+
const merged = mergeHooksIntoSettings(existing, incoming);
|
|
138
172
|
writeFile(settingsPath, JSON.stringify(merged, null, 2));
|
|
139
173
|
} else {
|
|
140
174
|
const hookPath = path.join(CLAUDE_TEMPLATES_DIR, 'hooks', hookFile);
|
|
@@ -153,7 +187,7 @@ function generateHooks(outputDir, config, options = {}) {
|
|
|
153
187
|
}
|
|
154
188
|
}
|
|
155
189
|
|
|
156
|
-
function
|
|
190
|
+
function mergeHooksIntoSettings(existing, incoming) {
|
|
157
191
|
const merged = { ...existing };
|
|
158
192
|
if (incoming.hooks) {
|
|
159
193
|
merged.hooks = merged.hooks || {};
|
|
@@ -178,7 +212,7 @@ function deepMergeSettings(existing, incoming) {
|
|
|
178
212
|
}
|
|
179
213
|
|
|
180
214
|
function generateSkills(outputDir, config) {
|
|
181
|
-
// Universal skills
|
|
215
|
+
// Universal skills, always included
|
|
182
216
|
const skillsToInclude = ['git-workflow', 'testing-patterns'];
|
|
183
217
|
|
|
184
218
|
if (config.frontend?.framework === 'nextjs') {
|
|
@@ -227,6 +261,10 @@ function generateAgents(outputDir, config, vars) {
|
|
|
227
261
|
'loop-operator.md',
|
|
228
262
|
'harness-optimizer.md',
|
|
229
263
|
'product-strategist.md',
|
|
264
|
+
'prompt-auditor.md',
|
|
265
|
+
'frontend-builder.md',
|
|
266
|
+
'enforcement-gate.md',
|
|
267
|
+
'deep-reviewer.md',
|
|
230
268
|
];
|
|
231
269
|
|
|
232
270
|
for (const agent of agents) {
|
|
@@ -234,9 +272,14 @@ function generateAgents(outputDir, config, vars) {
|
|
|
234
272
|
if (fs.existsSync(srcPath)) {
|
|
235
273
|
let content = readTemplate(srcPath);
|
|
236
274
|
content = replaceVars(content, vars);
|
|
275
|
+
content = stampManaged(content);
|
|
237
276
|
writeFile(path.join(outputDir, '.claude', 'agents', agent), content);
|
|
238
277
|
}
|
|
239
278
|
}
|
|
279
|
+
|
|
280
|
+
// Clean up orphaned agents from older versions
|
|
281
|
+
const removed = cleanOrphans(path.join(outputDir, '.claude', 'agents'), agents);
|
|
282
|
+
return { count: agents.length, removed };
|
|
240
283
|
}
|
|
241
284
|
|
|
242
285
|
function generateCommands(outputDir, config, vars) {
|
|
@@ -245,7 +288,6 @@ function generateCommands(outputDir, config, vars) {
|
|
|
245
288
|
'workflows.md',
|
|
246
289
|
'status.md',
|
|
247
290
|
'next.md',
|
|
248
|
-
'done.md',
|
|
249
291
|
'verify-all.md',
|
|
250
292
|
'audit-spec.md',
|
|
251
293
|
'audit-wiring.md',
|
|
@@ -257,11 +299,19 @@ function generateCommands(outputDir, config, vars) {
|
|
|
257
299
|
'optimize-claude-md.md',
|
|
258
300
|
'plan.md',
|
|
259
301
|
'build-fix.md',
|
|
302
|
+
'fix-loop.md',
|
|
260
303
|
'code-review.md',
|
|
261
304
|
'tdd.md',
|
|
262
305
|
'save-session.md',
|
|
263
306
|
'resume-session.md',
|
|
264
307
|
'full-audit.md',
|
|
308
|
+
'verify-intent.md',
|
|
309
|
+
'build-ui.md',
|
|
310
|
+
'help.md',
|
|
311
|
+
'live-uat.md',
|
|
312
|
+
'simplify.md',
|
|
313
|
+
'generate-sdd.md',
|
|
314
|
+
'product-strategist.md',
|
|
265
315
|
];
|
|
266
316
|
|
|
267
317
|
for (const cmd of commands) {
|
|
@@ -269,9 +319,52 @@ function generateCommands(outputDir, config, vars) {
|
|
|
269
319
|
if (fs.existsSync(srcPath)) {
|
|
270
320
|
let content = readTemplate(srcPath);
|
|
271
321
|
content = replaceVars(content, vars);
|
|
322
|
+
content = stampManaged(content);
|
|
272
323
|
writeFile(path.join(outputDir, '.claude', 'commands', cmd), content);
|
|
273
324
|
}
|
|
274
325
|
}
|
|
326
|
+
|
|
327
|
+
// Clean up orphaned commands from older versions
|
|
328
|
+
const removed = cleanOrphans(path.join(outputDir, '.claude', 'commands'), commands);
|
|
329
|
+
return { count: commands.length, removed };
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const MANAGED_MARKER = '<!-- Generated by DevForge -->';
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Append the DevForge managed marker to generated file content.
|
|
336
|
+
*/
|
|
337
|
+
function stampManaged(content) {
|
|
338
|
+
return content.trimEnd() + '\n\n' + MANAGED_MARKER + '\n';
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Remove DevForge-managed .md files that are no longer in the current manifest.
|
|
343
|
+
* Only deletes files stamped with the DevForge marker. User-created custom
|
|
344
|
+
* files are left untouched. Returns array of removed filenames.
|
|
345
|
+
*/
|
|
346
|
+
function cleanOrphans(dir, expectedFiles) {
|
|
347
|
+
const removed = [];
|
|
348
|
+
if (!fs.existsSync(dir)) return removed;
|
|
349
|
+
|
|
350
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
351
|
+
const existing = entries
|
|
352
|
+
.filter(e => e.isFile() && !e.isSymbolicLink() && e.name.endsWith('.md'))
|
|
353
|
+
.map(e => e.name);
|
|
354
|
+
const expectedSet = new Set(expectedFiles);
|
|
355
|
+
for (const file of existing) {
|
|
356
|
+
if (!expectedSet.has(file)) {
|
|
357
|
+
const filePath = path.join(dir, file);
|
|
358
|
+
const stat = fs.statSync(filePath);
|
|
359
|
+
if (stat.size > 1_000_000) continue; // skip unexpectedly large files
|
|
360
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
361
|
+
if (content.includes(MANAGED_MARKER)) {
|
|
362
|
+
fs.unlinkSync(filePath);
|
|
363
|
+
removed.push(file);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return removed;
|
|
275
368
|
}
|
|
276
369
|
|
|
277
370
|
function copyPromptLibrary(outputDir) {
|
package/src/cli.js
CHANGED
|
@@ -5,7 +5,13 @@ import { log } from './utils.js';
|
|
|
5
5
|
|
|
6
6
|
export async function parseCommand(args) {
|
|
7
7
|
if (args.includes('--version') || args.includes('-v')) {
|
|
8
|
-
|
|
8
|
+
let pkg;
|
|
9
|
+
try {
|
|
10
|
+
pkg = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));
|
|
11
|
+
} catch (err) {
|
|
12
|
+
console.error(`Could not read package.json: ${err.message}`);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
9
15
|
console.log(pkg.version);
|
|
10
16
|
process.exit(0);
|
|
11
17
|
}
|
|
@@ -18,8 +24,10 @@ export async function parseCommand(args) {
|
|
|
18
24
|
const command = args[0];
|
|
19
25
|
|
|
20
26
|
if (!command) {
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
const { showInteractiveMenu, handleMenuSelection } = await import('./menu.js');
|
|
28
|
+
const selected = await showInteractiveMenu();
|
|
29
|
+
await handleMenuSelection(selected);
|
|
30
|
+
return;
|
|
23
31
|
}
|
|
24
32
|
|
|
25
33
|
if (command === 'new') {
|
|
@@ -46,6 +54,12 @@ export async function parseCommand(args) {
|
|
|
46
54
|
return;
|
|
47
55
|
}
|
|
48
56
|
|
|
57
|
+
if (command === 'ci') {
|
|
58
|
+
const { runCI } = await import('./ci-mode.js');
|
|
59
|
+
await runCI(process.cwd());
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
49
63
|
if (command === 'update') {
|
|
50
64
|
const { runUpdate } = await import('./update.js');
|
|
51
65
|
await runUpdate();
|
|
@@ -73,12 +87,13 @@ export async function parseCommand(args) {
|
|
|
73
87
|
|
|
74
88
|
function showUsage() {
|
|
75
89
|
console.log(`
|
|
76
|
-
${chalk.bold.cyan('DevForge')}
|
|
90
|
+
${chalk.bold.cyan('DevForge')} AI-first project scaffolding
|
|
77
91
|
|
|
78
92
|
${chalk.bold('Usage:')}
|
|
79
93
|
devforge new <name> Create a new project
|
|
80
94
|
devforge init Add dev guardrails to current project
|
|
81
95
|
devforge doctor Diagnose and optimize current project
|
|
96
|
+
devforge ci Run health checks for CI/CD (non-interactive)
|
|
82
97
|
devforge update Check for newer version
|
|
83
98
|
devforge <name> Shorthand for ${chalk.dim('devforge new <name>')}
|
|
84
99
|
|
|
@@ -92,14 +107,14 @@ function showUsage() {
|
|
|
92
107
|
|
|
93
108
|
function showHelp() {
|
|
94
109
|
console.log(`
|
|
95
|
-
${chalk.bold.cyan('DevForge')}
|
|
110
|
+
${chalk.bold.cyan('DevForge')} Universal, AI-first project scaffolding
|
|
96
111
|
|
|
97
112
|
${chalk.bold('Commands:')}
|
|
98
113
|
|
|
99
114
|
${chalk.bold('devforge new <name>')}
|
|
100
115
|
Create a new project. Choose between:
|
|
101
|
-
${chalk.dim('• Guided mode
|
|
102
|
-
${chalk.dim('• Developer mode
|
|
116
|
+
${chalk.dim('• Guided mode: describe what you want in plain English')}
|
|
117
|
+
${chalk.dim('• Developer mode: pick your stack directly')}
|
|
103
118
|
|
|
104
119
|
${chalk.bold('devforge init')}
|
|
105
120
|
Add Claude Code infrastructure to an existing project.
|
|
@@ -112,6 +127,11 @@ function showHelp() {
|
|
|
112
127
|
flaky tests, dead code, duplicate code, and more.
|
|
113
128
|
Generates Claude Code prompts to fix each issue.
|
|
114
129
|
|
|
130
|
+
${chalk.bold('devforge ci')}
|
|
131
|
+
Run project health checks non-interactively for CI/CD.
|
|
132
|
+
Exits with code 1 if critical issues found.
|
|
133
|
+
Saves report to docs/ci-report.md if docs/ exists.
|
|
134
|
+
|
|
115
135
|
${chalk.bold('devforge update')}
|
|
116
136
|
Check if a newer version of DevForge is available.
|
|
117
137
|
Shows upgrade instructions if an update exists.
|
|
@@ -120,6 +140,9 @@ function showHelp() {
|
|
|
120
140
|
- Next.js full-stack (TypeScript + Prisma + PostgreSQL)
|
|
121
141
|
- FastAPI backend (Python + SQLAlchemy + PostgreSQL)
|
|
122
142
|
- Polyglot full-stack (Next.js + FastAPI monorepo)
|
|
143
|
+
- React + Express (Vite + Express + Prisma + PostgreSQL)
|
|
144
|
+
- Remix full-stack (Vite + Tailwind + Prisma + PostgreSQL)
|
|
145
|
+
- Hono API (TypeScript + Prisma + PostgreSQL)
|
|
123
146
|
|
|
124
147
|
${chalk.bold('Examples:')}
|
|
125
148
|
devforge new my-app
|