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,6 +1,9 @@
1
1
  import chalk from 'chalk';
2
2
 
3
- export const SUPPORTED_STACKS = ['nextjs-fullstack', 'fastapi-backend', 'polyglot-fullstack'];
3
+ export const SUPPORTED_STACKS = [
4
+ 'nextjs-fullstack', 'fastapi-backend', 'polyglot-fullstack',
5
+ 'react-express', 'remix-fullstack', 'hono-api',
6
+ ];
4
7
 
5
8
  export function recommend(serviceType, refinements) {
6
9
  const config = {
@@ -24,19 +27,25 @@ export function recommend(serviceType, refinements) {
24
27
 
25
28
  switch (serviceType) {
26
29
  case 'web_app':
30
+ if (refinements.framework === 'remix') {
31
+ return buildRemixFullstack(config, refinements);
32
+ }
27
33
  return buildNextjsFullstack(config, refinements);
28
34
 
29
35
  case 'full_stack':
30
36
  if (lang === 'python') {
31
37
  return buildPolyglotFullstack(config, refinements);
32
38
  }
39
+ if (refinements.backend === 'express') {
40
+ return buildReactExpress(config, refinements);
41
+ }
33
42
  return buildNextjsFullstack(config, refinements);
34
43
 
35
44
  case 'api_service':
36
45
  if (lang === 'python') {
37
46
  return buildFastapiBackend(config, refinements);
38
47
  }
39
- return unsupported(serviceType, lang, 'FastAPI (Python)');
48
+ return buildHonoApi(config, refinements);
40
49
 
41
50
  case 'ai_service':
42
51
  if (lang === 'python') {
@@ -50,10 +59,10 @@ export function recommend(serviceType, refinements) {
50
59
  case 'extension':
51
60
  case 'microservice':
52
61
  case 'describe':
53
- return unsupported(serviceType, lang);
62
+ return unsupported(serviceType);
54
63
 
55
64
  default:
56
- return unsupported(serviceType, lang);
65
+ return unsupported(serviceType);
57
66
  }
58
67
  }
59
68
 
@@ -71,6 +80,7 @@ function buildNextjsFullstack(config, refinements) {
71
80
  config.templateModules = [
72
81
  { path: 'base', prefix: '' },
73
82
  { path: 'frontend/nextjs', prefix: '' },
83
+ { path: 'docs-portal/nextjs', prefix: '' },
74
84
  { path: 'database/prisma-postgres', prefix: '' },
75
85
  { path: 'testing/vitest', prefix: '' },
76
86
  { path: 'testing/playwright', prefix: '' },
@@ -82,6 +92,13 @@ function buildNextjsFullstack(config, refinements) {
82
92
  config.templateModules.push({ path: 'auth/nextauth', prefix: '' });
83
93
  }
84
94
 
95
+ config.templateModules.push({ path: 'chainproof/base', prefix: '' });
96
+ config.templateModules.push({ path: 'chainproof/nextjs', prefix: '' });
97
+
98
+ if (refinements.ai) {
99
+ config.templateModules.push({ path: 'ai/guardrails-ts', prefix: '' });
100
+ }
101
+
85
102
  return config;
86
103
  }
87
104
 
@@ -99,6 +116,7 @@ function buildFastapiBackend(config, refinements) {
99
116
  config.templateModules = [
100
117
  { path: 'base', prefix: '' },
101
118
  { path: 'backend/fastapi', prefix: '' },
119
+ { path: 'docs-portal/fastapi', prefix: '' },
102
120
  { path: 'database/sqlalchemy-postgres', prefix: '' },
103
121
  { path: 'testing/pytest', prefix: '' },
104
122
  { path: 'infra/docker-compose', prefix: '' },
@@ -109,6 +127,13 @@ function buildFastapiBackend(config, refinements) {
109
127
  config.templateModules.push({ path: 'auth/jwt-custom', prefix: '' });
110
128
  }
111
129
 
130
+ config.templateModules.push({ path: 'chainproof/base', prefix: '' });
131
+ config.templateModules.push({ path: 'chainproof/fastapi', prefix: '' });
132
+
133
+ if (refinements.ai) {
134
+ config.templateModules.push({ path: 'ai/guardrails-py', prefix: '' });
135
+ }
136
+
112
137
  return config;
113
138
  }
114
139
 
@@ -127,6 +152,7 @@ function buildPolyglotFullstack(config, refinements) {
127
152
  config.templateModules = [
128
153
  { path: 'base', prefix: '' },
129
154
  { path: 'frontend/nextjs', prefix: 'frontend' },
155
+ { path: 'docs-portal/nextjs', prefix: 'frontend' },
130
156
  { path: 'backend/fastapi', prefix: '' },
131
157
  { path: 'database/prisma-postgres', prefix: 'frontend' },
132
158
  { path: 'database/sqlalchemy-postgres', prefix: '' },
@@ -142,14 +168,111 @@ function buildPolyglotFullstack(config, refinements) {
142
168
  config.templateModules.push({ path: 'auth/jwt-custom', prefix: '' });
143
169
  }
144
170
 
171
+ config.templateModules.push({ path: 'chainproof/base', prefix: '' });
172
+ config.templateModules.push({ path: 'chainproof/polyglot', prefix: '' });
173
+
174
+ if (refinements.ai) {
175
+ config.templateModules.push({ path: 'ai/guardrails-ts', prefix: 'frontend' });
176
+ config.templateModules.push({ path: 'ai/guardrails-py', prefix: '' });
177
+ }
178
+
179
+ return config;
180
+ }
181
+
182
+ function buildReactExpress(config, refinements) {
183
+ config.stackId = 'react-express';
184
+ config.frontend = { framework: 'react', language: 'typescript', styling: 'tailwind', ui: null };
185
+ config.backend = { framework: 'express', language: 'typescript', orm: 'prisma' };
186
+ config.database = { type: 'postgresql', orm: 'prisma' };
187
+ config.testing = { unit: 'vitest', e2e: null, backend: 'vitest' };
188
+
189
+ if (refinements.auth) {
190
+ config.auth = 'jwt-custom';
191
+ }
192
+
193
+ config.templateModules = [
194
+ { path: 'base', prefix: '' },
195
+ { path: 'frontend/react', prefix: 'frontend' },
196
+ { path: 'backend/express', prefix: 'backend' },
197
+ { path: 'database/prisma-postgres', prefix: 'backend' },
198
+ { path: 'testing/vitest', prefix: 'frontend' },
199
+ { path: 'testing/vitest', prefix: 'backend' },
200
+ { path: 'infra/docker-compose', prefix: '' },
201
+ { path: 'infra/github-actions', prefix: '' },
202
+ ];
203
+
204
+ config.templateModules.push({ path: 'chainproof/base', prefix: '' });
205
+
206
+ if (refinements.ai) {
207
+ config.templateModules.push({ path: 'ai/guardrails-ts', prefix: 'backend' });
208
+ }
209
+
210
+ return config;
211
+ }
212
+
213
+ function buildRemixFullstack(config, refinements) {
214
+ config.stackId = 'remix-fullstack';
215
+ config.frontend = { framework: 'remix', language: 'typescript', styling: 'tailwind', ui: null };
216
+ config.backend = { framework: 'remix', language: 'typescript', orm: 'prisma' };
217
+ config.database = { type: 'postgresql', orm: 'prisma' };
218
+ config.testing = { unit: 'vitest', e2e: null, backend: null };
219
+
220
+ if (refinements.auth) {
221
+ config.auth = 'nextauth';
222
+ }
223
+
224
+ config.templateModules = [
225
+ { path: 'base', prefix: '' },
226
+ { path: 'frontend/remix', prefix: '' },
227
+ { path: 'database/prisma-postgres', prefix: '' },
228
+ { path: 'testing/vitest', prefix: '' },
229
+ { path: 'infra/docker-compose', prefix: '' },
230
+ { path: 'infra/github-actions', prefix: '' },
231
+ ];
232
+
233
+ config.templateModules.push({ path: 'chainproof/base', prefix: '' });
234
+
235
+ if (refinements.ai) {
236
+ config.templateModules.push({ path: 'ai/guardrails-ts', prefix: '' });
237
+ }
238
+
239
+ return config;
240
+ }
241
+
242
+ function buildHonoApi(config, refinements) {
243
+ config.stackId = 'hono-api';
244
+ config.frontend = null;
245
+ config.backend = { framework: 'hono', language: 'typescript', orm: 'prisma' };
246
+ config.database = { type: 'postgresql', orm: 'prisma' };
247
+ config.testing = { unit: 'vitest', e2e: null, backend: 'vitest' };
248
+
249
+ if (refinements.auth) {
250
+ config.auth = 'jwt-custom';
251
+ }
252
+
253
+ config.templateModules = [
254
+ { path: 'base', prefix: '' },
255
+ { path: 'backend/hono', prefix: '' },
256
+ { path: 'database/prisma-postgres', prefix: '' },
257
+ { path: 'testing/vitest', prefix: '' },
258
+ { path: 'infra/docker-compose', prefix: '' },
259
+ { path: 'infra/github-actions', prefix: '' },
260
+ ];
261
+
262
+ config.templateModules.push({ path: 'chainproof/base', prefix: '' });
263
+
264
+ if (refinements.ai) {
265
+ config.templateModules.push({ path: 'ai/guardrails-ts', prefix: '' });
266
+ }
267
+
145
268
  return config;
146
269
  }
147
270
 
148
- function unsupported(serviceType, lang, suggestion) {
149
- const msg = suggestion
150
- ? `"${serviceType}" with "${lang}" is not yet supported. Try: ${suggestion}`
151
- : `"${serviceType}" is not yet supported in V1. Supported: web_app, api_service (Python), full_stack, ai_service`;
152
- return { supported: false, message: msg };
271
+ function unsupported(serviceType) {
272
+ return {
273
+ supported: false,
274
+ message: `"${serviceType}" is not yet supported in V1. Supported: web_app, full_stack, api_service, ai_service`,
275
+ };
153
276
  }
154
277
 
155
278
  export function formatStackSummary(config) {
@@ -160,7 +283,8 @@ export function formatStackSummary(config) {
160
283
  const lines = [];
161
284
 
162
285
  if (config.frontend) {
163
- lines.push(` ${chalk.bold('Frontend:')} ${config.frontend.framework} + ${config.frontend.language} + ${config.frontend.styling} + ${config.frontend.ui}`);
286
+ const uiPart = config.frontend.ui ? ` + ${config.frontend.ui}` : '';
287
+ lines.push(` ${chalk.bold('Frontend:')} ${config.frontend.framework} + ${config.frontend.language} + ${config.frontend.styling}${uiPart}`);
164
288
  }
165
289
  if (config.backend) {
166
290
  lines.push(` ${chalk.bold('Backend:')} ${config.backend.framework} + ${config.backend.language} + ${config.backend.orm}`);
package/src/scanner.js CHANGED
@@ -13,6 +13,7 @@ export function scanProject(projectDir) {
13
13
  testing: detectTesting(projectDir),
14
14
  deployment: detectDeployment(projectDir),
15
15
  ai: detectAI(projectDir),
16
+ monorepo: detectMonorepo(projectDir),
16
17
 
17
18
  infrastructure: {
18
19
  hasClaudeMd: false,
@@ -22,6 +23,7 @@ export function scanProject(projectDir) {
22
23
  hasCommands: false,
23
24
  hasSkills: false,
24
25
  hasUAT: false,
26
+ devforgeVersion: null,
25
27
  },
26
28
 
27
29
  files: {
@@ -88,7 +90,8 @@ function detectFrontend(projectDir) {
88
90
  result.detected = true;
89
91
  result.framework = 'nextjs';
90
92
  result.language = fileExists(projectDir, 'tsconfig.json') ? 'typescript' : 'javascript';
91
- result.directory = 'src';
93
+ // Next.js supports both src/app and app/ at root
94
+ result.directory = dirExists(projectDir, 'src') ? 'src' : '.';
92
95
  return result;
93
96
  }
94
97
 
@@ -307,6 +310,49 @@ function detectAI(projectDir) {
307
310
  return false;
308
311
  }
309
312
 
313
+ function detectMonorepo(projectDir) {
314
+ const result = { detected: false, type: null, workspaces: [] };
315
+
316
+ // npm/yarn workspaces
317
+ const pkg = readJsonSafe(projectDir, 'package.json');
318
+ if (pkg?.workspaces) {
319
+ result.detected = true;
320
+ result.type = 'npm-workspaces';
321
+ result.workspaces = Array.isArray(pkg.workspaces) ? pkg.workspaces : (pkg.workspaces.packages || []);
322
+ return result;
323
+ }
324
+
325
+ // pnpm workspaces
326
+ if (fileExists(projectDir, 'pnpm-workspace.yaml')) {
327
+ result.detected = true;
328
+ result.type = 'pnpm-workspaces';
329
+ return result;
330
+ }
331
+
332
+ // Turborepo
333
+ if (fileExists(projectDir, 'turbo.json')) {
334
+ result.detected = true;
335
+ result.type = 'turborepo';
336
+ return result;
337
+ }
338
+
339
+ // Nx
340
+ if (fileExists(projectDir, 'nx.json')) {
341
+ result.detected = true;
342
+ result.type = 'nx';
343
+ return result;
344
+ }
345
+
346
+ // Lerna
347
+ if (fileExists(projectDir, 'lerna.json')) {
348
+ result.detected = true;
349
+ result.type = 'lerna';
350
+ return result;
351
+ }
352
+
353
+ return result;
354
+ }
355
+
310
356
  function detectClaudeInfra(projectDir) {
311
357
  const infra = {
312
358
  hasClaudeMd: false,
@@ -316,6 +362,8 @@ function detectClaudeInfra(projectDir) {
316
362
  hasCommands: false,
317
363
  hasSkills: false,
318
364
  hasUAT: false,
365
+ hasChainproof: false,
366
+ devforgeVersion: null,
319
367
  };
320
368
 
321
369
  if (fileExists(projectDir, 'CLAUDE.md')) {
@@ -329,6 +377,13 @@ function detectClaudeInfra(projectDir) {
329
377
  infra.hasCommands = dirHasFiles(projectDir, '.claude/commands');
330
378
  infra.hasSkills = dirHasFiles(projectDir, '.claude/skills');
331
379
  infra.hasUAT = dirExists(projectDir, 'docs/uat');
380
+ infra.hasChainproof = dirExists(projectDir, '.chainproof');
381
+
382
+ // Check template version
383
+ const versionFile = readJsonSafe(projectDir, '.claude/.devforge-version');
384
+ if (versionFile?.version) {
385
+ infra.devforgeVersion = versionFile.version;
386
+ }
332
387
 
333
388
  return infra;
334
389
  }
@@ -362,7 +417,7 @@ function readJsonSafe(base, file) {
362
417
  if (!fs.existsSync(p)) return null;
363
418
  try {
364
419
  return JSON.parse(fs.readFileSync(p, 'utf-8'));
365
- } catch {
420
+ } catch { // Malformed JSON in config file
366
421
  return null;
367
422
  }
368
423
  }