forgedev 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/README.md +57 -10
  2. package/bin/chainproof.js +126 -0
  3. package/package.json +25 -7
  4. package/src/chainproof-bridge.js +330 -0
  5. package/src/ci-mode.js +85 -0
  6. package/src/claude-configurator.js +86 -49
  7. package/src/cli.js +30 -7
  8. package/src/composer.js +159 -34
  9. package/src/doctor-checks-chainproof.js +106 -0
  10. package/src/doctor-checks.js +39 -20
  11. package/src/doctor-prompts.js +9 -9
  12. package/src/doctor.js +37 -4
  13. package/src/guided.js +3 -3
  14. package/src/index.js +31 -10
  15. package/src/init-mode.js +64 -11
  16. package/src/menu.js +178 -0
  17. package/src/prompts.js +5 -12
  18. package/src/recommender.js +134 -10
  19. package/src/scanner.js +57 -2
  20. package/src/uat-generator.js +204 -189
  21. package/src/update-check.js +9 -4
  22. package/src/update.js +1 -1
  23. package/src/utils.js +64 -5
  24. package/templates/ai/guardrails-py/backend/app/ai/__init__.py +29 -0
  25. package/templates/ai/guardrails-py/backend/app/ai/audit_log.py +133 -0
  26. package/templates/ai/guardrails-py/backend/app/ai/client.py.template +323 -0
  27. package/templates/ai/guardrails-py/backend/app/ai/health.py.template +157 -0
  28. package/templates/ai/guardrails-py/backend/app/ai/input_guard.py +98 -0
  29. package/templates/ai/guardrails-ts/src/lib/ai/audit-log.ts.template +164 -0
  30. package/templates/ai/guardrails-ts/src/lib/ai/client.ts.template +403 -0
  31. package/templates/ai/guardrails-ts/src/lib/ai/health.ts.template +165 -0
  32. package/templates/ai/guardrails-ts/src/lib/ai/index.ts.template +17 -0
  33. package/templates/ai/guardrails-ts/src/lib/ai/input-guard.ts.template +124 -0
  34. package/templates/auth/nextauth/src/lib/auth.ts.template +12 -7
  35. package/templates/backend/express/Dockerfile.template +18 -0
  36. package/templates/backend/express/package.json.template +33 -0
  37. package/templates/backend/express/src/index.ts.template +34 -0
  38. package/templates/backend/express/src/routes/health.ts.template +27 -0
  39. package/templates/backend/express/tsconfig.json +17 -0
  40. package/templates/backend/fastapi/backend/Dockerfile.template +5 -0
  41. package/templates/backend/fastapi/backend/app/api/health.py.template +1 -1
  42. package/templates/backend/fastapi/backend/app/core/config.py.template +1 -1
  43. package/templates/backend/fastapi/backend/app/core/errors.py +1 -1
  44. package/templates/backend/fastapi/backend/app/main.py.template +3 -1
  45. package/templates/backend/fastapi/backend/requirements.txt.template +2 -0
  46. package/templates/backend/hono/Dockerfile.template +18 -0
  47. package/templates/backend/hono/package.json.template +31 -0
  48. package/templates/backend/hono/src/index.ts.template +32 -0
  49. package/templates/backend/hono/src/routes/health.ts.template +27 -0
  50. package/templates/backend/hono/tsconfig.json +18 -0
  51. package/templates/base/docs/uat/UAT_TEMPLATE.md.template +1 -1
  52. package/templates/chainproof/base/.chainproof/config.json.template +11 -0
  53. package/templates/chainproof/base/.chainproof/mcp-server.mjs +310 -0
  54. package/templates/chainproof/base/.mcp.json +9 -0
  55. package/templates/chainproof/fastapi/.chainproof/middleware.json.template +14 -0
  56. package/templates/chainproof/nextjs/.chainproof/hooks.json.template +19 -0
  57. package/templates/chainproof/polyglot/.chainproof/config.json.template +21 -0
  58. package/templates/claude-code/agents/architect.md +25 -11
  59. package/templates/claude-code/agents/build-error-resolver.md +19 -5
  60. package/templates/claude-code/agents/chief-of-staff.md +42 -8
  61. package/templates/claude-code/agents/code-quality-reviewer.md +14 -0
  62. package/templates/claude-code/agents/database-reviewer.md +15 -1
  63. package/templates/claude-code/agents/deep-reviewer.md +191 -0
  64. package/templates/claude-code/agents/doc-updater.md +19 -5
  65. package/templates/claude-code/agents/docs-lookup.md +19 -5
  66. package/templates/claude-code/agents/e2e-runner.md +26 -12
  67. package/templates/claude-code/agents/enforcement-gate.md +102 -0
  68. package/templates/claude-code/agents/frontend-builder.md +188 -0
  69. package/templates/claude-code/agents/harness-optimizer.md +36 -1
  70. package/templates/claude-code/agents/loop-operator.md +27 -13
  71. package/templates/claude-code/agents/planner.md +21 -7
  72. package/templates/claude-code/agents/product-strategist.md +24 -10
  73. package/templates/claude-code/agents/production-readiness.md +14 -0
  74. package/templates/claude-code/agents/prompt-auditor.md +115 -0
  75. package/templates/claude-code/agents/refactor-cleaner.md +22 -8
  76. package/templates/claude-code/agents/security-reviewer.md +14 -0
  77. package/templates/claude-code/agents/spec-validator.md +15 -1
  78. package/templates/claude-code/agents/tdd-guide.md +21 -7
  79. package/templates/claude-code/agents/uat-validator.md +14 -0
  80. package/templates/claude-code/claude-md/base.md +14 -7
  81. package/templates/claude-code/claude-md/fastapi.md +8 -8
  82. package/templates/claude-code/claude-md/fullstack.md +6 -6
  83. package/templates/claude-code/claude-md/hono.md +18 -0
  84. package/templates/claude-code/claude-md/nextjs.md +5 -5
  85. package/templates/claude-code/claude-md/remix.md +18 -0
  86. package/templates/claude-code/commands/audit-security.md +14 -0
  87. package/templates/claude-code/commands/audit-spec.md +14 -0
  88. package/templates/claude-code/commands/audit-wiring.md +14 -0
  89. package/templates/claude-code/commands/build-fix.md +28 -0
  90. package/templates/claude-code/commands/build-ui.md +59 -0
  91. package/templates/claude-code/commands/code-review.md +53 -31
  92. package/templates/claude-code/commands/fix-loop.md +211 -0
  93. package/templates/claude-code/commands/full-audit.md +36 -8
  94. package/templates/claude-code/commands/generate-prd.md +1 -1
  95. package/templates/claude-code/commands/generate-sdd.md +74 -0
  96. package/templates/claude-code/commands/generate-uat.md +107 -35
  97. package/templates/claude-code/commands/help.md +68 -0
  98. package/templates/claude-code/commands/live-uat.md +268 -0
  99. package/templates/claude-code/commands/optimize-claude-md.md +15 -1
  100. package/templates/claude-code/commands/plan.md +3 -3
  101. package/templates/claude-code/commands/pre-pr.md +57 -19
  102. package/templates/claude-code/commands/product-strategist.md +21 -0
  103. package/templates/claude-code/commands/resume-session.md +10 -10
  104. package/templates/claude-code/commands/run-uat.md +59 -2
  105. package/templates/claude-code/commands/save-session.md +10 -10
  106. package/templates/claude-code/commands/simplify.md +36 -0
  107. package/templates/claude-code/commands/tdd.md +17 -18
  108. package/templates/claude-code/commands/verify-all.md +24 -0
  109. package/templates/claude-code/commands/verify-intent.md +55 -0
  110. package/templates/claude-code/commands/workflows.md +52 -40
  111. package/templates/claude-code/hooks/polyglot.json +10 -1
  112. package/templates/claude-code/hooks/python.json +10 -1
  113. package/templates/claude-code/hooks/scripts/autofix-polyglot.mjs +2 -2
  114. package/templates/claude-code/hooks/scripts/autofix-python.mjs +1 -1
  115. package/templates/claude-code/hooks/scripts/autofix-typescript.mjs +1 -1
  116. package/templates/claude-code/hooks/scripts/code-hygiene.mjs +293 -0
  117. package/templates/claude-code/hooks/scripts/pre-commit-gate.mjs +207 -0
  118. package/templates/claude-code/hooks/typescript.json +10 -1
  119. package/templates/claude-code/skills/ai-prompts/SKILL.md +119 -41
  120. package/templates/claude-code/skills/git-workflow/SKILL.md +5 -5
  121. package/templates/claude-code/skills/nextjs/SKILL.md +1 -1
  122. package/templates/claude-code/skills/playwright/SKILL.md +5 -5
  123. package/templates/claude-code/skills/security-api/SKILL.md +1 -1
  124. package/templates/claude-code/skills/security-web/SKILL.md +1 -1
  125. package/templates/claude-code/skills/testing-patterns/SKILL.md +9 -9
  126. package/templates/database/prisma-postgres/{.env.example → .env.example.template} +1 -0
  127. package/templates/database/sqlalchemy-postgres/{.env.example → .env.example.template} +1 -0
  128. package/templates/docs-portal/fastapi/backend/app/portal/__pycache__/docs_reader.cpython-314.pyc +0 -0
  129. package/templates/docs-portal/fastapi/backend/app/portal/docs_reader.py +201 -0
  130. package/templates/docs-portal/fastapi/backend/app/portal/html_renderer.py +229 -0
  131. package/templates/docs-portal/fastapi/backend/app/portal/router.py.template +35 -0
  132. package/templates/docs-portal/nextjs/src/app/portal/[category]/[slug]/page.tsx +81 -0
  133. package/templates/docs-portal/nextjs/src/app/portal/[category]/page.tsx +65 -0
  134. package/templates/docs-portal/nextjs/src/app/portal/layout.tsx.template +54 -0
  135. package/templates/docs-portal/nextjs/src/app/portal/page.tsx +85 -0
  136. package/templates/docs-portal/nextjs/src/components/portal/markdown-renderer.tsx +101 -0
  137. package/templates/docs-portal/nextjs/src/components/portal/mobile-portal-nav.tsx +81 -0
  138. package/templates/docs-portal/nextjs/src/components/portal/portal-nav.tsx +86 -0
  139. package/templates/docs-portal/nextjs/src/lib/docs.ts +139 -0
  140. package/templates/frontend/nextjs/package.json.template +3 -1
  141. package/templates/frontend/react/index.html.template +12 -0
  142. package/templates/frontend/react/package.json.template +34 -0
  143. package/templates/frontend/react/src/App.tsx.template +10 -0
  144. package/templates/frontend/react/src/index.css +1 -0
  145. package/templates/frontend/react/src/main.tsx +10 -0
  146. package/templates/frontend/react/tsconfig.json +17 -0
  147. package/templates/frontend/react/vite.config.ts.template +15 -0
  148. package/templates/frontend/react/vitest.config.ts +9 -0
  149. package/templates/frontend/remix/app/root.tsx.template +31 -0
  150. package/templates/frontend/remix/app/routes/_index.tsx.template +19 -0
  151. package/templates/frontend/remix/app/routes/api.health.ts.template +10 -0
  152. package/templates/frontend/remix/app/tailwind.css +1 -0
  153. package/templates/frontend/remix/package.json.template +39 -0
  154. package/templates/frontend/remix/tsconfig.json +18 -0
  155. package/templates/frontend/remix/vite.config.ts.template +7 -0
  156. package/templates/infra/github-actions/.github/workflows/ci.yml.template +3 -0
  157. package/docs/00-README.md +0 -310
  158. package/docs/01-universal-prompt-library.md +0 -1049
  159. package/docs/02-claude-code-mastery-playbook.md +0 -283
  160. package/docs/03-multi-agent-verification.md +0 -565
  161. package/docs/04-errata-and-verification-checklist.md +0 -284
  162. package/docs/05-universal-scaffolder-vision.md +0 -452
  163. package/docs/06-confidence-assessment-and-repo-prompt.md +0 -407
  164. package/docs/errata.md +0 -58
  165. package/docs/multi-agent-verification.md +0 -66
  166. package/docs/playbook.md +0 -95
  167. package/docs/prompt-library.md +0 -160
  168. package/docs/uat/UAT_CHECKLIST.csv +0 -9
  169. package/docs/uat/UAT_TEMPLATE.md +0 -163
  170. package/templates/claude-code/commands/done.md +0 -19
  171. /package/{docs/plans/.gitkeep → templates/docs-portal/fastapi/backend/app/portal/__init__.py} +0 -0
@@ -1,7 +1,7 @@
1
1
  const PROMPT_TEMPLATES = {
2
2
  CLAUDE_MD_TOO_LONG: (issue) => {
3
3
  const lines = issue.title.match(/(\d+) lines/)?.[1] || '?';
4
- return `Read CLAUDE.md. It's ${lines} lines too long for Claude Code to follow reliably (target: <150).
4
+ return `Read CLAUDE.md. It's ${lines} lines, too long for Claude Code to follow reliably (target: <150).
5
5
 
6
6
  Propose a split:
7
7
  - What stays in CLAUDE.md (universal rules, commands, pitfalls)
@@ -25,7 +25,7 @@ Write a test for each endpoint confirming 401 without auth.`;
25
25
 
26
26
  FLAKY_TESTS: (issue) => {
27
27
  const fileList = (issue.files || []).map(f => `- ${f}`).join('\n');
28
- return `These tests use waitForTimeout() or hardcoded delays they will fail randomly:
28
+ return `These tests use waitForTimeout() or hardcoded delays. They will fail randomly:
29
29
  ${fileList}
30
30
 
31
31
  For each:
@@ -55,10 +55,10 @@ ${fileList}
55
55
  For each: replace with a specific exception type.
56
56
  - If catching expected errors: except ValueError, except KeyError, etc.
57
57
  - If catching any exception for logging: except Exception as e: with logging
58
- - Never use bare except: it hides bugs`;
58
+ - Never use bare except: because it hides bugs`;
59
59
  },
60
60
 
61
- MISSING_HEALTH: (issue) => {
61
+ MISSING_HEALTH: (_issue) => {
62
62
  return `Add a health check endpoint to the application.
63
63
 
64
64
  For FastAPI: Add a /health endpoint that returns {"status": "ok"} and optionally checks database connectivity.
@@ -68,7 +68,7 @@ For both: Include database connectivity check if a database is configured.
68
68
  This endpoint is used by load balancers and monitoring to verify the app is running.`;
69
69
  },
70
70
 
71
- MISSING_SHUTDOWN: (issue) => {
71
+ MISSING_SHUTDOWN: (_issue) => {
72
72
  return `Add graceful shutdown handling to the application.
73
73
 
74
74
  For FastAPI: Use the lifespan context manager to handle startup/shutdown events.
@@ -114,7 +114,7 @@ For each:
114
114
  3. Update imports
115
115
  4. Run tests after each extraction
116
116
 
117
- Don't refactor all at once one file per session.`;
117
+ Don't refactor all at once. Do one file per session.`;
118
118
  },
119
119
 
120
120
  MISSING_SCOPED_CLAUDE_MD: (issue) => {
@@ -177,7 +177,7 @@ ${fileList}
177
177
  Extract all prompts into a dedicated prompts file (e.g., src/lib/prompts.ts or prompts/). This makes prompts easier to version, A/B test, and iterate on without changing application code.`;
178
178
  },
179
179
 
180
- DEVFORGE_UPDATE: (issue) => {
180
+ DEVFORGE_UPDATE: (_issue) => {
181
181
  return `A newer version of DevForge is available.
182
182
 
183
183
  Run: npm install -g forgedev@latest
@@ -221,7 +221,7 @@ ${prompt}
221
221
  sessionNum++;
222
222
  }
223
223
 
224
- return `# Fix Prompts Run These in Claude Code (In Order)
224
+ return `# Fix Prompts - Run These in Claude Code (In Order)
225
225
 
226
226
  ${sections.join('\n')}
227
227
  Run each as a separate Claude Code session. /clear between sessions.
@@ -232,7 +232,7 @@ export function generateReport(issues, projectName) {
232
232
  const critical = issues.filter(i => i.severity === 'critical');
233
233
  const warnings = issues.filter(i => i.severity === 'warning');
234
234
  const info = issues.filter(i => i.severity === 'info');
235
- let report = `# DevForge Doctor Report ${projectName}
235
+ let report = `# DevForge Doctor Report - ${projectName}
236
236
  Generated: ${new Date().toISOString().split('T')[0]}
237
237
 
238
238
  ## Summary
package/src/doctor.js CHANGED
@@ -9,7 +9,7 @@ import { askDoctorAction } from './prompts.js';
9
9
 
10
10
  export async function runDoctor(projectDir) {
11
11
  console.log('');
12
- console.log(chalk.bold.cyan(' 🔨 DevForge Doctor') + chalk.dim('Project Health Check'));
12
+ console.log(chalk.bold.cyan(' 🔨 DevForge Doctor') + chalk.dim(' Project Health Check'));
13
13
  console.log('');
14
14
  console.log(' Scanning...');
15
15
  console.log('');
@@ -45,7 +45,7 @@ export async function runDoctor(projectDir) {
45
45
  });
46
46
  }
47
47
  } catch {
48
- // Silently ignore network issues shouldn't block doctor
48
+ // Silently ignore. Network issues shouldn't block doctor
49
49
  }
50
50
 
51
51
  if (issues.length === 0) {
@@ -247,8 +247,7 @@ async function autoFixSafe(issues, projectDir) {
247
247
  fs.chmodSync(hookPath, '755');
248
248
  console.log(chalk.green(` ✓ Fixed ${path.join('.claude/hooks', hookFile)} permissions (chmod +x)`));
249
249
  fixed++;
250
- } catch {
251
- // Skip
250
+ } catch { // Permission change failed on this hook, continue
252
251
  }
253
252
  }
254
253
  }
@@ -262,6 +261,8 @@ async function autoFixSafe(issues, projectDir) {
262
261
 
263
262
  if (!content.includes('.claude/todos')) entriesToAdd.push('.claude/todos');
264
263
  if (!content.includes('.claude/plans')) entriesToAdd.push('.claude/plans');
264
+ if (!content.includes('.env.local')) entriesToAdd.push('.env.local');
265
+ if (!content.includes('.env.*.local')) entriesToAdd.push('.env.*.local');
265
266
 
266
267
  if (entriesToAdd.length > 0) {
267
268
  const addition = '\n# Claude Code temp files\n' + entriesToAdd.join('\n') + '\n';
@@ -271,6 +272,38 @@ async function autoFixSafe(issues, projectDir) {
271
272
  }
272
273
  }
273
274
 
275
+ // Create missing .env.example if .env exists but no example
276
+ const envPath = path.join(projectDir, '.env');
277
+ const envExamplePath = path.join(projectDir, '.env.example');
278
+ if (fs.existsSync(envPath) && !fs.existsSync(envExamplePath)) {
279
+ const envContent = fs.readFileSync(envPath, 'utf-8');
280
+ const exampleContent = envContent
281
+ .split('\n')
282
+ .map(line => {
283
+ if (!line.includes('=') || line.startsWith('#')) return line;
284
+ const key = line.split('=')[0];
285
+ return `${key}=`;
286
+ })
287
+ .join('\n');
288
+ fs.writeFileSync(envExamplePath, exampleContent, 'utf-8');
289
+ console.log(chalk.green(' ✓ Created .env.example from .env (values stripped)'));
290
+ fixed++;
291
+ }
292
+
293
+ // Create missing CLAUDE.md for subdirectories in polyglot projects
294
+ const frontendDir = path.join(projectDir, 'frontend');
295
+ const backendDir = path.join(projectDir, 'backend');
296
+ if (fs.existsSync(frontendDir) && fs.existsSync(backendDir)) {
297
+ for (const subdir of ['frontend', 'backend']) {
298
+ const subdirClaudeMd = path.join(projectDir, subdir, 'CLAUDE.md');
299
+ if (!fs.existsSync(subdirClaudeMd)) {
300
+ fs.writeFileSync(subdirClaudeMd, `# ${subdir}\n\nSee root CLAUDE.md for project-level rules. This file contains ${subdir}-specific overrides.\n`, 'utf-8');
301
+ console.log(chalk.green(` ✓ Created ${subdir}/CLAUDE.md (scoped)`));
302
+ fixed++;
303
+ }
304
+ }
305
+ }
306
+
274
307
  const skipped = issues.filter(i => !i.autoFixable).length;
275
308
 
276
309
  if (fixed === 0) {
package/src/guided.js CHANGED
@@ -91,7 +91,7 @@ export function analyzeDescription(text) {
91
91
  suggestedServiceType = 'web_app';
92
92
  }
93
93
 
94
- // AI service override if AI is the primary focus
94
+ // AI service override: if AI is the primary focus
95
95
  if (features.includes('ai') && !features.includes('dashboard') && !features.includes('payments')) {
96
96
  suggestedServiceType = 'ai_service';
97
97
  }
@@ -114,7 +114,7 @@ export function mapFeaturesToStack(analysis) {
114
114
  return stackConfig;
115
115
  }
116
116
 
117
- export function buildGuidedPreview(analysis, stackConfig) {
117
+ export function buildGuidedPreview(analysis, _stackConfig) {
118
118
  const lines = [];
119
119
 
120
120
  lines.push(chalk.bold(' What you\'ll get:'));
@@ -208,7 +208,7 @@ npm run dev`;
208
208
 
209
209
  DevForge created a starter version of your app with all the basic
210
210
  building blocks in place. Think of it like getting a house with the
211
- foundation, walls, and plumbing already done you just need to
211
+ foundation, walls, and plumbing already done. You just need to
212
212
  furnish and decorate it.
213
213
 
214
214
  ## Your Project Structure (Plain English)
package/src/index.js CHANGED
@@ -2,7 +2,7 @@ import path from 'node:path';
2
2
  import fs from 'node:fs';
3
3
  import { execSync } from 'node:child_process';
4
4
  import chalk from 'chalk';
5
- import { log, toKebabCase } from './utils.js';
5
+ import { log, toKebabCase, copyEnvCmd } from './utils.js';
6
6
  import { askServiceType, askRefinements, askNewMode, confirmStack } from './prompts.js';
7
7
  import { recommend } from './recommender.js';
8
8
  import { compose } from './composer.js';
@@ -12,9 +12,9 @@ import { generateUAT } from './uat-generator.js';
12
12
  export async function runNew(projectName) {
13
13
  const safeName = toKebabCase(projectName);
14
14
 
15
- // Validate project name must be a clean kebab-case identifier
16
- if (!/^[a-z0-9][a-z0-9-]*$/.test(safeName)) {
17
- log.error('Project name must start with a letter or number and contain only lowercase letters, numbers, and hyphens.');
15
+ // Validate project name. Must be a clean kebab-case identifier
16
+ if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(safeName)) {
17
+ log.error('Project name must start with a letter, contain only lowercase letters, numbers, and hyphens, and not have consecutive or trailing hyphens.');
18
18
  process.exit(1);
19
19
  }
20
20
 
@@ -26,7 +26,7 @@ export async function runNew(projectName) {
26
26
  }
27
27
 
28
28
  console.log('');
29
- console.log(chalk.bold.cyan(' 🔨 DevForge') + chalk.dim('Let\'s build something.'));
29
+ console.log(chalk.bold.cyan(' 🔨 DevForge') + chalk.dim(' Let\'s build something.'));
30
30
  console.log('');
31
31
 
32
32
  const mode = await askNewMode();
@@ -62,7 +62,7 @@ async function runDeveloperFlow(safeName, outputDir) {
62
62
 
63
63
  export async function scaffold(outputDir, stackConfig) {
64
64
  console.log('');
65
- const totalSteps = stackConfig.claudeCode ? 4 : 3;
65
+ const totalSteps = stackConfig.claudeCode ? 5 : 4;
66
66
  let step = 0;
67
67
 
68
68
  step++;
@@ -79,6 +79,11 @@ export async function scaffold(outputDir, stackConfig) {
79
79
  log.step(step, totalSteps, 'Generating UAT templates...');
80
80
  await generateUAT(outputDir, stackConfig);
81
81
 
82
+ step++;
83
+ log.step(step, totalSteps, 'Initializing ChainProof trust chain...');
84
+ const { initChainproof } = await import('./chainproof-bridge.js');
85
+ initChainproof(outputDir);
86
+
82
87
  step++;
83
88
  log.step(step, totalSteps, 'Initializing git repository...');
84
89
  initGit(outputDir);
@@ -93,7 +98,7 @@ function initGit(outputDir) {
93
98
  try {
94
99
  execSync('git init', { cwd: outputDir, stdio: 'ignore' });
95
100
  } catch {
96
- // git not available skip silently
101
+ log.dim(' git not available, skipping repository initialization');
97
102
  }
98
103
  }
99
104
 
@@ -109,7 +114,7 @@ export function printNextSteps(projectName, config, isGuided = false) {
109
114
  console.log(chalk.bold(' 📖 New to coding? Here\'s what to do next:'));
110
115
  console.log(' 1. Open this folder in VS Code (or any code editor)');
111
116
  console.log(' 2. If you have Claude Code installed, type /workflows');
112
- console.log(' it will guide you step by step');
117
+ console.log(' It will guide you step by step');
113
118
  console.log(' 3. Or read docs/getting-started.md for a beginner-friendly walkthrough');
114
119
  console.log('');
115
120
  return;
@@ -120,7 +125,7 @@ export function printNextSteps(projectName, config, isGuided = false) {
120
125
 
121
126
  if (config.stackId === 'nextjs-fullstack') {
122
127
  console.log(' npm install');
123
- console.log(' cp .env.example .env');
128
+ console.log(` ${copyEnvCmd()}`);
124
129
  console.log(' npx prisma db push');
125
130
  console.log(' npm run dev');
126
131
  } else if (config.stackId === 'fastapi-backend') {
@@ -128,7 +133,7 @@ export function printNextSteps(projectName, config, isGuided = false) {
128
133
  console.log(' python -m venv venv');
129
134
  console.log(' source venv/bin/activate # or venv\\Scripts\\activate on Windows');
130
135
  console.log(' pip install -r requirements.txt');
131
- console.log(' cp .env.example .env');
136
+ console.log(` ${copyEnvCmd()}`);
132
137
  console.log(' uvicorn app.main:app --reload');
133
138
  } else if (config.stackId === 'polyglot-fullstack') {
134
139
  console.log(' docker compose up -d postgres');
@@ -136,6 +141,21 @@ export function printNextSteps(projectName, config, isGuided = false) {
136
141
  console.log(' cd frontend && npm install && npm run dev');
137
142
  console.log(' # Backend:');
138
143
  console.log(' cd backend && pip install -r requirements.txt && uvicorn app.main:app --reload');
144
+ } else if (config.stackId === 'react-express') {
145
+ console.log(' # Frontend:');
146
+ console.log(' cd frontend && npm install && npm run dev');
147
+ console.log(' # Backend (in a separate terminal):');
148
+ console.log(` cd backend && npm install && ${copyEnvCmd()} && npx prisma db push && npm run dev`);
149
+ } else if (config.stackId === 'remix-fullstack') {
150
+ console.log(' npm install');
151
+ console.log(` ${copyEnvCmd()}`);
152
+ console.log(' npx prisma db push');
153
+ console.log(' npm run dev');
154
+ } else if (config.stackId === 'hono-api') {
155
+ console.log(' npm install');
156
+ console.log(` ${copyEnvCmd()}`);
157
+ console.log(' npx prisma db push');
158
+ console.log(' npm run dev');
139
159
  }
140
160
 
141
161
  if (config.claudeCode) {
@@ -149,6 +169,7 @@ export function printNextSteps(projectName, config, isGuided = false) {
149
169
  console.log(chalk.dim(' - .claude/commands/ (audit, verify, pre-pr)'));
150
170
  console.log(chalk.dim(' - docs/prompt-library.md'));
151
171
  console.log(chalk.dim(' - docs/uat/ (acceptance testing)'));
172
+ console.log(chalk.dim(' - .chainproof/ (trust chain tracking)'));
152
173
  }
153
174
 
154
175
  console.log('');
package/src/init-mode.js CHANGED
@@ -1,13 +1,15 @@
1
1
  import path from 'node:path';
2
+ import fs from 'node:fs';
2
3
  import chalk from 'chalk';
3
- import { log } from './utils.js';
4
+ import { log, ROOT_DIR, ensureDir } from './utils.js';
4
5
  import { scanProject } from './scanner.js';
5
6
  import { generateClaudeConfig } from './claude-configurator.js';
6
7
  import { generateUAT } from './uat-generator.js';
8
+ import { initChainproof } from './chainproof-bridge.js';
7
9
 
8
10
  export async function runInit(projectDir) {
9
11
  console.log('');
10
- console.log(chalk.bold.cyan(' 🔨 DevForge') + chalk.dim('Adding dev guardrails'));
12
+ console.log(chalk.bold.cyan(' 🔨 DevForge') + chalk.dim(' Adding dev guardrails'));
11
13
  console.log('');
12
14
  console.log(' Scanning...');
13
15
  console.log('');
@@ -54,35 +56,47 @@ export async function runInit(projectDir) {
54
56
  await generateClaudeConfig(projectDir, stackConfig, { skipClaudeMd, mergeSettings });
55
57
 
56
58
  if (!skipClaudeMd) {
57
- console.log(chalk.green(' ✓ ') + 'CLAUDE.md project context + rules');
59
+ console.log(chalk.green(' ✓ ') + 'CLAUDE.md (project context + rules)');
58
60
  } else {
59
- console.log(chalk.yellow(' ⊘ ') + `CLAUDE.md exists (tip: run ${chalk.cyan('/optimize-claude-md')} to slim it)`);
61
+ console.log(chalk.yellow(' ⊘ ') + `CLAUDE.md already exists (tip: run ${chalk.cyan('/optimize-claude-md')} to slim it)`);
60
62
  }
61
63
 
62
64
  if (!scan.infrastructure.hasHooks) {
63
- console.log(chalk.green(' ✓ ') + '.claude/hooks/ auto-lint, quality gate, file protection');
65
+ console.log(chalk.green(' ✓ ') + '.claude/hooks/ (auto-lint, quality gate, file protection)');
64
66
  } else {
65
- console.log(chalk.yellow(' ⊘ ') + '.claude/hooks/ already configured');
67
+ console.log(chalk.yellow(' ⊘ ') + '.claude/hooks/ already configured');
66
68
  }
67
69
 
68
70
  if (!scan.infrastructure.hasAgents) {
69
- console.log(chalk.green(' ✓ ') + '.claude/agents/ code quality, security, spec validator');
71
+ console.log(chalk.green(' ✓ ') + '.claude/agents/ (code quality, security, spec validator)');
70
72
  }
71
73
  if (!scan.infrastructure.hasCommands) {
72
- console.log(chalk.green(' ✓ ') + '.claude/commands/ help, status, next, done, audit, pre-pr');
74
+ console.log(chalk.green(' ✓ ') + '.claude/commands/ (help, status, next, done, audit, pre-pr)');
73
75
  }
74
76
  if (!scan.infrastructure.hasSkills) {
75
- console.log(chalk.green(' ✓ ') + '.claude/skills/ framework-specific knowledge');
77
+ console.log(chalk.green(' ✓ ') + '.claude/skills/ (framework-specific knowledge)');
76
78
  }
77
79
 
78
80
  // Generate UAT templates
79
81
  if (!scan.infrastructure.hasUAT) {
80
82
  await generateUAT(projectDir, stackConfig);
81
- console.log(chalk.green(' ✓ ') + 'docs/uat/ acceptance test templates');
83
+ console.log(chalk.green(' ✓ ') + 'docs/uat/ (acceptance test templates)');
82
84
  } else {
83
- console.log(chalk.yellow(' ⊘ ') + 'docs/uat/ already exists');
85
+ console.log(chalk.yellow(' ⊘ ') + 'docs/uat/ already exists');
84
86
  }
85
87
 
88
+ // Initialize ChainProof trust chain
89
+ const chainproofDir = path.join(projectDir, '.chainproof');
90
+ if (!fs.existsSync(chainproofDir)) {
91
+ initChainproof(projectDir);
92
+ console.log(chalk.green(' ✓ ') + '.chainproof/ (trust chain initialized)');
93
+ } else {
94
+ console.log(chalk.yellow(' ⊘ ') + '.chainproof/ already exists');
95
+ }
96
+
97
+ // Install ChainProof MCP server and configure .mcp.json
98
+ setupChainproofMcp(projectDir);
99
+
86
100
  console.log('');
87
101
  log.success(' Done. Your project is now configured for Claude Code.');
88
102
  console.log('');
@@ -136,3 +150,42 @@ export function buildConfigFromScan(scan) {
136
150
 
137
151
  return config;
138
152
  }
153
+
154
+ export function setupChainproofMcp(projectDir) {
155
+ // Copy MCP server script into .chainproof/
156
+ const serverSrc = path.join(ROOT_DIR, 'templates', 'chainproof', 'base', '.chainproof', 'mcp-server.mjs');
157
+ const serverDest = path.join(projectDir, '.chainproof', 'mcp-server.mjs');
158
+ if (fs.existsSync(serverSrc) && !fs.existsSync(serverDest)) {
159
+ ensureDir(path.dirname(serverDest));
160
+ fs.copyFileSync(serverSrc, serverDest);
161
+ }
162
+
163
+ // Configure .mcp.json (merge if exists)
164
+ const mcpPath = path.join(projectDir, '.mcp.json');
165
+ const chainproofMcpEntry = {
166
+ command: 'node',
167
+ args: ['.chainproof/mcp-server.mjs'],
168
+ env: {},
169
+ };
170
+
171
+ if (fs.existsSync(mcpPath)) {
172
+ try {
173
+ const existing = JSON.parse(fs.readFileSync(mcpPath, 'utf-8'));
174
+ existing.mcpServers = existing.mcpServers || {};
175
+ if (!existing.mcpServers.chainproof) {
176
+ existing.mcpServers.chainproof = chainproofMcpEntry;
177
+ fs.writeFileSync(mcpPath, JSON.stringify(existing, null, 2) + '\n', 'utf-8');
178
+ console.log(chalk.green(' ✓ ') + '.mcp.json (added ChainProof server)');
179
+ } else {
180
+ console.log(chalk.yellow(' ⊘ ') + '.mcp.json already has ChainProof configured');
181
+ }
182
+ } catch {
183
+ // Corrupted .mcp.json, don't touch it
184
+ console.log(chalk.yellow(' ⊘ ') + '.mcp.json exists but could not be parsed');
185
+ }
186
+ } else {
187
+ const mcpConfig = { mcpServers: { chainproof: chainproofMcpEntry } };
188
+ fs.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2) + '\n', 'utf-8');
189
+ console.log(chalk.green(' ✓ ') + '.mcp.json (ChainProof MCP auto-configured)');
190
+ }
191
+ }
package/src/menu.js ADDED
@@ -0,0 +1,178 @@
1
+ import { select, input, Separator } from '@inquirer/prompts';
2
+ import chalk from 'chalk';
3
+ import fs from 'node:fs';
4
+
5
+ const COMMAND_GROUPS = [
6
+ {
7
+ name: 'Getting Started',
8
+ commands: [
9
+ { value: 'new', label: 'Create a new project', description: 'Scaffold a full project with AI-powered stack selection' },
10
+ { value: 'init', label: 'Add guardrails to existing project', description: 'Install hooks, agents, and commands into your current codebase' },
11
+ ],
12
+ },
13
+ {
14
+ name: 'Daily Workflow',
15
+ commands: [
16
+ { value: 'status', label: '/status', description: 'Run all checks and show a project dashboard' },
17
+ { value: 'next', label: '/next', description: 'Figure out what to work on next' },
18
+ { value: 'done', label: '/done', description: 'Verify current task is actually complete before moving on' },
19
+ { value: 'help', label: '/help', description: 'Not sure what to do? Get guided to the right workflow' },
20
+ ],
21
+ },
22
+ {
23
+ name: 'Development',
24
+ commands: [
25
+ { value: 'plan', label: '/plan', description: 'Create a comprehensive implementation plan before writing code' },
26
+ { value: 'tdd', label: '/tdd', description: 'Write failing tests first, then implement (test-driven)' },
27
+ { value: 'build-fix', label: '/build-fix', description: 'Fix build, lint, and type errors with minimal changes' },
28
+ { value: 'build-ui', label: '/build-ui', description: 'Build frontend UI components with AI-powered generation' },
29
+ { value: 'code-review', label: '/code-review', description: 'Review uncommitted changes for issues and quality' },
30
+ { value: 'simplify', label: '/simplify', description: 'Find duplicate code, long files, and extract shared utilities' },
31
+ ],
32
+ },
33
+ {
34
+ name: 'Verification',
35
+ commands: [
36
+ { value: 'verify-all', label: '/verify-all', description: 'Run the full verification chain on current changes' },
37
+ { value: 'full-audit', label: '/full-audit', description: 'Run every audit and review agent in a single pass' },
38
+ { value: 'audit-spec', label: '/audit-spec', description: 'Validate code against the specification' },
39
+ { value: 'audit-wiring', label: '/audit-wiring', description: 'Verify API endpoints are wired between frontend and backend' },
40
+ { value: 'audit-security', label: '/audit-security', description: 'Run a focused security audit on changed files' },
41
+ { value: 'verify-intent', label: '/verify-intent', description: 'Check Intent Verification Protocol compliance' },
42
+ ],
43
+ },
44
+ {
45
+ name: 'Release',
46
+ commands: [
47
+ { value: 'pre-pr', label: '/pre-pr', description: 'Run the complete pre-PR checklist before creating a pull request' },
48
+ { value: 'run-uat', label: '/run-uat', description: 'Execute UAT verification against test scenarios' },
49
+ { value: 'live-uat', label: '/live-uat', description: 'Run live UAT by interacting with the running application' },
50
+ ],
51
+ },
52
+ {
53
+ name: 'Generation',
54
+ commands: [
55
+ { value: 'generate-prd', label: '/generate-prd', description: 'Generate a Product Requirements Document' },
56
+ { value: 'generate-sdd', label: '/generate-sdd', description: 'Generate a Software Design Document from the codebase' },
57
+ { value: 'generate-uat', label: '/generate-uat', description: 'Generate UAT test scenarios for the project' },
58
+ { value: 'optimize-claude-md', label: '/optimize-claude-md', description: 'Analyze and optimize the CLAUDE.md file' },
59
+ ],
60
+ },
61
+ {
62
+ name: 'Session',
63
+ commands: [
64
+ { value: 'save-session', label: '/save-session', description: 'Save current session state for later' },
65
+ { value: 'resume-session', label: '/resume-session', description: 'Load a saved session and pick up where you left off' },
66
+ ],
67
+ },
68
+ {
69
+ name: 'Project Tools',
70
+ commands: [
71
+ { value: 'doctor', label: 'Diagnose project', description: 'Find and fix issues like dead code, flaky tests, oversized files' },
72
+ { value: 'ci', label: 'CI health check', description: 'Non-interactive health checks for CI/CD pipelines' },
73
+ { value: 'update', label: 'Check for updates', description: 'See if a newer version of DevForge is available' },
74
+ ],
75
+ },
76
+ ];
77
+
78
+ // Commands that are Claude Code slash commands (run inside Claude Code, not the terminal)
79
+ const CLAUDE_COMMANDS = new Set([
80
+ 'status', 'next', 'done', 'help', 'plan', 'tdd', 'build-fix', 'build-ui',
81
+ 'code-review', 'simplify', 'verify-all', 'full-audit', 'audit-spec', 'generate-sdd',
82
+ 'audit-wiring', 'audit-security', 'verify-intent', 'pre-pr', 'run-uat',
83
+ 'live-uat', 'generate-prd', 'generate-uat', 'optimize-claude-md',
84
+ 'save-session', 'resume-session',
85
+ ]);
86
+
87
+ export async function showInteractiveMenu() {
88
+ console.log('');
89
+ console.log(` ${chalk.bold.cyan('DevForge')} ${chalk.dim('AI-first project scaffolding')}`);
90
+ console.log('');
91
+
92
+ // Build choices with separators for groups
93
+ const choices = [];
94
+ let recommendation = null;
95
+
96
+ // Detect recommendation based on project state
97
+ try {
98
+ if (!fs.existsSync('package.json') && !fs.existsSync('pyproject.toml')) {
99
+ recommendation = 'new';
100
+ } else if (!fs.existsSync('.claude')) {
101
+ recommendation = 'init';
102
+ }
103
+ } catch { /* ignore */ }
104
+
105
+ for (const group of COMMAND_GROUPS) {
106
+ choices.push(new Separator(chalk.dim(` ${group.name}`)));
107
+ for (const cmd of group.commands) {
108
+ const isRecommended = cmd.value === recommendation;
109
+ const badge = isRecommended ? chalk.green(' recommended') : '';
110
+ const label = CLAUDE_COMMANDS.has(cmd.value)
111
+ ? chalk.cyan(cmd.label)
112
+ : chalk.bold(cmd.label);
113
+
114
+ choices.push({
115
+ value: cmd.value,
116
+ name: `${label}${badge} ${chalk.dim(cmd.description)}`,
117
+ });
118
+ }
119
+ }
120
+
121
+ const selected = await select({
122
+ message: 'What would you like to do?',
123
+ choices,
124
+ pageSize: 15,
125
+ loop: false,
126
+ });
127
+
128
+ return selected;
129
+ }
130
+
131
+ export async function handleMenuSelection(selected) {
132
+ // CLI commands — execute directly
133
+ if (selected === 'new') {
134
+ const name = await input({ message: 'Project name:' });
135
+ if (!name) return;
136
+ const { runNew } = await import('./index.js');
137
+ await runNew(name);
138
+ return;
139
+ }
140
+
141
+ if (selected === 'init') {
142
+ const { runInit } = await import('./init-mode.js');
143
+ await runInit(process.cwd());
144
+ return;
145
+ }
146
+
147
+ if (selected === 'doctor') {
148
+ const { runDoctor } = await import('./doctor.js');
149
+ await runDoctor(process.cwd());
150
+ return;
151
+ }
152
+
153
+ if (selected === 'ci') {
154
+ const { runCI } = await import('./ci-mode.js');
155
+ await runCI(process.cwd());
156
+ return;
157
+ }
158
+
159
+ if (selected === 'update') {
160
+ const { runUpdate } = await import('./update.js');
161
+ await runUpdate();
162
+ return;
163
+ }
164
+
165
+ // Claude Code commands — show how to run them
166
+ if (CLAUDE_COMMANDS.has(selected)) {
167
+ console.log('');
168
+ console.log(` ${chalk.cyan('/')}${chalk.bold.cyan(selected)} is a Claude Code command.`);
169
+ console.log('');
170
+ console.log(` To use it, open ${chalk.bold('Claude Code')} in your project and type:`);
171
+ console.log(` ${chalk.green(`/${selected}`)}`);
172
+ console.log('');
173
+ console.log(` ${chalk.dim('These commands work inside Claude Code (the AI assistant).')}`);
174
+ console.log(` ${chalk.dim('They run agents that analyze, review, and modify your code.')}`);
175
+ console.log('');
176
+ return;
177
+ }
178
+ }
package/src/prompts.js CHANGED
@@ -21,19 +21,12 @@ const LANGUAGE_CHOICES = [
21
21
  { value: 'rust', name: 'Rust' },
22
22
  ];
23
23
 
24
- const DEPLOYMENT_CHOICES = [
25
- { value: 'docker', name: 'Docker / Docker Compose' },
26
- { value: 'vercel', name: 'Vercel' },
27
- { value: 'aws', name: 'AWS' },
28
- { value: 'gcp', name: 'Google Cloud' },
29
- ];
30
-
31
24
  export async function askNewMode() {
32
25
  const mode = await select({
33
26
  message: 'How would you like to start?',
34
27
  choices: [
35
28
  { value: 'guided', name: '💬 Describe what you want to build (recommended for beginners)' },
36
- { value: 'developer', name: '⚡ I know my stack let me pick (for developers)' },
29
+ { value: 'developer', name: '⚡ I know my stack, let me pick (for developers)' },
37
30
  ],
38
31
  });
39
32
  return mode;
@@ -41,7 +34,7 @@ export async function askNewMode() {
41
34
 
42
35
  export async function askDescription() {
43
36
  const description = await input({
44
- message: 'Tell me about what you want to build.\n Don\'t worry about technical details just describe it like you\'re explaining it to a friend.\n\n ',
37
+ message: 'Tell me about what you want to build.\n Don\'t worry about technical details. Just describe it like you\'re explaining it to a friend.\n\n ',
45
38
  });
46
39
  return description;
47
40
  }
@@ -57,8 +50,8 @@ export async function askGuidedConfirm() {
57
50
  const choice = await select({
58
51
  message: 'Sound right?',
59
52
  choices: [
60
- { value: 'yes', name: 'Yes create it!' },
61
- { value: 'adjust', name: 'No let me adjust' },
53
+ { value: 'yes', name: 'Yes, create it!' },
54
+ { value: 'adjust', name: 'No, let me adjust' },
62
55
  ],
63
56
  });
64
57
  return choice;
@@ -88,7 +81,7 @@ export async function askServiceType() {
88
81
  export async function askRefinements(serviceType) {
89
82
  const refinements = {};
90
83
 
91
- // Q1: Language preference adapt choices based on service type
84
+ // Q1: Language preference. Adapt choices based on service type
92
85
  if (['api_service', 'cli_tool', 'microservice'].includes(serviceType)) {
93
86
  refinements.language = await select({
94
87
  message: 'Language preference?',