@anhth2/spec-driven-dev-plugin 0.6.0 → 0.8.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 (86) hide show
  1. package/bin/index.js +285 -11
  2. package/commands/debug.md +233 -11
  3. package/commands/debug.tmpl +170 -6
  4. package/commands/define-product.md +68 -6
  5. package/commands/define-product.tmpl +5 -1
  6. package/commands/fix-bug.md +111 -11
  7. package/commands/fix-bug.tmpl +48 -6
  8. package/commands/generate-bdd.md +86 -9
  9. package/commands/generate-bdd.tmpl +23 -4
  10. package/commands/generate-code.md +146 -19
  11. package/commands/generate-code.tmpl +83 -14
  12. package/commands/generate-design-spec.md +754 -0
  13. package/commands/generate-design-spec.tmpl +399 -0
  14. package/commands/generate-prd.md +91 -7
  15. package/commands/generate-prd.tmpl +28 -2
  16. package/commands/generate-spec-manifest.md +519 -0
  17. package/commands/generate-spec-manifest.tmpl +164 -0
  18. package/commands/generate-tech-docs.md +122 -9
  19. package/commands/generate-tech-docs.tmpl +59 -4
  20. package/commands/generate-tests.md +491 -37
  21. package/commands/generate-tests.tmpl +428 -32
  22. package/commands/refine-prd.md +76 -8
  23. package/commands/refine-prd.tmpl +13 -3
  24. package/commands/review-code.md +94 -6
  25. package/commands/review-code.tmpl +31 -1
  26. package/commands/review-context.md +118 -12
  27. package/commands/review-context.tmpl +55 -7
  28. package/commands/review-tech-docs.md +76 -9
  29. package/commands/review-tech-docs.tmpl +13 -4
  30. package/commands/run-tests.md +196 -18
  31. package/commands/run-tests.tmpl +133 -13
  32. package/commands/setup-ai-first.md +192 -6
  33. package/commands/setup-ai-first.tmpl +136 -5
  34. package/commands/smoke-test.md +228 -22
  35. package/commands/smoke-test.tmpl +165 -17
  36. package/commands/validate-traces.md +77 -8
  37. package/commands/validate-traces.tmpl +14 -3
  38. package/core/FRAMEWORK_VERSION +1 -1
  39. package/core/commands/debug.md +233 -11
  40. package/core/commands/define-product.md +68 -6
  41. package/core/commands/fix-bug.md +111 -11
  42. package/core/commands/generate-bdd.md +86 -9
  43. package/core/commands/generate-code.md +146 -19
  44. package/core/commands/generate-design-spec.md +754 -0
  45. package/core/commands/generate-prd.md +91 -7
  46. package/core/commands/generate-spec-manifest.md +519 -0
  47. package/core/commands/generate-tech-docs.md +122 -9
  48. package/core/commands/generate-tests.md +491 -37
  49. package/core/commands/refine-prd.md +76 -8
  50. package/core/commands/review-code.md +94 -6
  51. package/core/commands/review-context.md +118 -12
  52. package/core/commands/review-tech-docs.md +76 -9
  53. package/core/commands/run-tests.md +196 -18
  54. package/core/commands/setup-ai-first.md +192 -6
  55. package/core/commands/smoke-test.md +228 -22
  56. package/core/commands/validate-traces.md +77 -8
  57. package/core/skills/code/SKILL.md +68 -8
  58. package/core/skills/debug/SKILL.md +72 -10
  59. package/core/skills/design-spec/SKILL.md +450 -0
  60. package/core/skills/discovery/SKILL.md +62 -4
  61. package/core/skills/prd/SKILL.md +12 -8
  62. package/core/skills/setup-ai-first/SKILL.md +5 -3
  63. package/core/skills/spec/SKILL.md +11 -7
  64. package/core/skills/test/SKILL.md +130 -12
  65. package/core/steps/context-loader.md +57 -1
  66. package/core/steps/gate.md +1 -1
  67. package/core/steps/report-footer.md +5 -3
  68. package/core/steps/spawn-agent.md +3 -1
  69. package/core/templates/design-spec.template.md +209 -0
  70. package/core/templates/project-context.yaml +29 -0
  71. package/package.json +1 -1
  72. package/skills/code/SKILL.md +68 -8
  73. package/skills/debug/SKILL.md +72 -10
  74. package/skills/design-spec/SKILL.md +450 -0
  75. package/skills/design-spec/SKILL.tmpl +95 -0
  76. package/skills/discovery/SKILL.md +62 -4
  77. package/skills/prd/SKILL.md +12 -8
  78. package/skills/setup-ai-first/SKILL.md +5 -3
  79. package/skills/spec/SKILL.md +11 -7
  80. package/skills/test/SKILL.md +130 -12
  81. package/steps/context-loader.md +57 -1
  82. package/steps/gate.md +1 -1
  83. package/steps/report-footer.md +5 -3
  84. package/steps/spawn-agent.md +3 -1
  85. package/templates/design-spec.template.md +209 -0
  86. package/templates/project-context.yaml +29 -0
package/bin/index.js CHANGED
@@ -19,30 +19,77 @@ const showHelp = args.includes('--help') || args.includes('-h');
19
19
  const moduleIdx = args.indexOf('--module');
20
20
  const moduleName = moduleIdx !== -1 ? args[moduleIdx + 1] : null;
21
21
 
22
+ // --services backend:java-spring,web-admin:react,app-mobile:flutter
23
+ const servicesIdx = args.indexOf('--services');
24
+ const servicesRaw = servicesIdx !== -1 ? args[servicesIdx + 1] : null;
25
+ const serviceList = servicesRaw
26
+ ? servicesRaw.split(',').map(s => {
27
+ const [name, mod] = s.trim().split(':');
28
+ return { name: name.trim(), module: (mod || '').trim() };
29
+ }).filter(s => s.name)
30
+ : [];
31
+
32
+ const isUmbrella = args.includes('--umbrella');
33
+ const specSourceIdx = args.indexOf('--spec-source');
34
+ const specSource = specSourceIdx !== -1 ? args[specSourceIdx + 1] : null;
35
+
22
36
  const AVAILABLE_MODULES = [
23
37
  'java-spring', 'angular', 'react', 'nextjs',
24
38
  'dotnet', 'golang', 'php-laravel', 'context-engineering',
39
+ 'flutter', 'react-native', 'ios-swiftui', 'android-compose',
25
40
  ];
26
41
 
42
+ // Language/framework labels per module (for generated project-context.yaml)
43
+ const MODULE_STACK = {
44
+ 'java-spring': { language: 'Java 17', framework: 'Spring Boot 3.x' },
45
+ 'golang': { language: 'Go', framework: 'Gin / Echo' },
46
+ 'dotnet': { language: 'C#', framework: '.NET / ASP.NET Core' },
47
+ 'php-laravel': { language: 'PHP', framework: 'Laravel' },
48
+ 'context-engineering': { language: 'TypeScript', framework: 'Node.js' },
49
+ 'react': { language: 'TypeScript', framework: 'React' },
50
+ 'nextjs': { language: 'TypeScript', framework: 'Next.js' },
51
+ 'vue': { language: 'TypeScript', framework: 'Vue 3' },
52
+ 'nuxt': { language: 'TypeScript', framework: 'Nuxt 3' },
53
+ 'angular': { language: 'TypeScript', framework: 'Angular' },
54
+ 'flutter': { language: 'Dart', framework: 'Flutter' },
55
+ 'react-native': { language: 'TypeScript', framework: 'React Native / Expo' },
56
+ 'ios-swiftui': { language: 'Swift', framework: 'SwiftUI' },
57
+ 'android-compose': { language: 'Kotlin', framework: 'Jetpack Compose' },
58
+ };
59
+
27
60
  if (showHelp) {
28
61
  console.log('Usage: npx @anhth2/spec-driven-dev-plugin [options]');
29
62
  console.log('');
30
63
  console.log('Install modes:');
31
- console.log(' --init NEW: Install framework to .agent/ + create shortcuts in .claude/commands/');
32
- console.log(' Recommended for new projects. Run upgrade.sh to upgrade later.');
33
- console.log(' --project Install full commands to ./.claude/commands/ (project-scoped, legacy)');
34
- console.log(' (no flag) Install full commands to ~/.claude/commands/ (global, legacy)');
64
+ console.log(' --init Install framework to .agent/ + create shortcuts in .claude/commands/');
65
+ console.log(' Recommended for new projects. Run upgrade.sh to upgrade later.');
66
+ console.log(' --init --umbrella Umbrella repo setup: installs framework at umbrella level only.');
67
+ console.log(' Generates project-context.yaml with services routing.');
68
+ console.log(' Does NOT install into service submodule directories.');
69
+ console.log(' --project Install full commands to ./.claude/commands/ (project-scoped, legacy)');
70
+ console.log(' (no flag) Install full commands to ~/.claude/commands/ (global, legacy)');
35
71
  console.log('');
36
72
  console.log('Options:');
37
- console.log(' --module <name> Copy stack module to .agent/modules/<name>/');
38
- console.log(' --hooks Install data-guard hook to .claude/hooks/ + update .claude/settings.json');
73
+ console.log(' --module <name> Copy stack module to .agent/modules/<name>/');
74
+ console.log(' --services <list> Monorepo setup: install into each service subfolder in one command.');
75
+ console.log(' Format: name:module,name:module,...');
76
+ console.log(' Example: backend:java-spring,web-admin:react,app-mobile:flutter');
77
+ console.log(' --umbrella Umbrella mode (use with --init). See above.');
78
+ console.log(' --spec-source <path> Path to PO spec submodule (e.g. free-trial-specs).');
79
+ console.log(' Auto-configures prd_dir, design_spec_dir, domain_knowledge_dir.');
80
+ console.log(' --hooks Install data-guard hook to .claude/hooks/ + update .claude/settings.json');
39
81
  console.log('');
40
82
  console.log('Available modules:');
41
83
  AVAILABLE_MODULES.forEach(m => console.log(` ${m}`));
42
84
  console.log('');
43
85
  console.log('Examples:');
44
- console.log(' npx @anhth2/spec-driven-dev-plugin --init # new project setup');
86
+ console.log(' npx @anhth2/spec-driven-dev-plugin --init # single-service project');
45
87
  console.log(' npx @anhth2/spec-driven-dev-plugin --init --module java-spring # with stack module');
88
+ console.log(' npx @anhth2/spec-driven-dev-plugin --init \\');
89
+ console.log(' --services backend:java-spring,web-admin:react,app-mobile:flutter # monorepo setup');
90
+ console.log(' npx @anhth2/spec-driven-dev-plugin --init --umbrella \\');
91
+ console.log(' --spec-source free-trial-specs \\');
92
+ console.log(' --services mass-product-web:nextjs # umbrella with one FE service');
46
93
  console.log(' npx @anhth2/spec-driven-dev-plugin --project --hooks # legacy project install');
47
94
  process.exit(0);
48
95
  }
@@ -144,7 +191,205 @@ if (isInit) {
144
191
  installDataGuardHook();
145
192
  }
146
193
 
147
- // 5. Summary
194
+ // 5-alt. Umbrella mode: generate umbrella project-context.yaml (do NOT install into submodules)
195
+ if (isUmbrella) {
196
+ const rootCtxPath = path.join(agentDir, 'project-context.yaml');
197
+ if (!fs.existsSync(rootCtxPath)) {
198
+ const specSrc = specSource || 'TODO-spec-submodule';
199
+
200
+ let servicesBlock = '';
201
+ if (serviceList.length > 0) {
202
+ servicesBlock = serviceList.map(s => [
203
+ ` ${s.name}:`,
204
+ ` path: "${s.name}"`,
205
+ ` module: "${s.module || 'TODO'}"`,
206
+ ` specs_dir: "${s.name}/specs/bdd"`,
207
+ ` tech_docs_dir: "${s.name}/specs/tech-docs"`,
208
+ ].join('\n')).join('\n');
209
+ } else {
210
+ servicesBlock = [
211
+ ` # {domain}:`,
212
+ ` # path: "{service-submodule-dir}"`,
213
+ ` # module: "{stack-module}"`,
214
+ ` # specs_dir: "{service-dir}/specs/bdd"`,
215
+ ` # tech_docs_dir: "{service-dir}/specs/tech-docs"`,
216
+ ].join('\n');
217
+ }
218
+
219
+ const domainsBlock = serviceList.length > 0
220
+ ? serviceList.map(s => ` - "${s.name}"`).join('\n')
221
+ : ` - "TODO"`;
222
+
223
+ const umbrellaYaml = [
224
+ `project:`,
225
+ ` name: "${path.basename(projectRoot)}"`,
226
+ ` description: ""`,
227
+ ``,
228
+ `setup:`,
229
+ ` mode: umbrella`,
230
+ ` spec_source: "${specSrc}"`,
231
+ ``,
232
+ `# Paths auto-derived from spec_source by context-loader.`,
233
+ `# Override here only if your structure differs.`,
234
+ `paths:`,
235
+ ` prd_dir: "${specSrc}/specs/prd"`,
236
+ ` design_spec_dir: "${specSrc}/specs/design-spec"`,
237
+ ` domain_knowledge_dir: "${specSrc}/specs/domain-knowledge"`,
238
+ ` business_dictionary: "${specSrc}/specs/domain-knowledge/business-dictionary.md"`,
239
+ ` core_entities: "${specSrc}/specs/domain-knowledge/core-entities.md"`,
240
+ ` product_definitions_dir: "${specSrc}/specs/product-definition"`,
241
+ ` refinement_dir: ".agent/review"`,
242
+ ` trace_dir: ".trace"`,
243
+ ``,
244
+ `# Domain → service submodule routing.`,
245
+ `# Each key must match @trace.domain in your PRD files.`,
246
+ `services:`,
247
+ servicesBlock,
248
+ ``,
249
+ `tech_stack:`,
250
+ ` language: multi-service`,
251
+ ` module: ""`,
252
+ ``,
253
+ `conventions:`,
254
+ ` ticket_prefix: FEAT`,
255
+ ``,
256
+ `domains:`,
257
+ domainsBlock,
258
+ ``,
259
+ ].join('\n');
260
+
261
+ fs.writeFileSync(rootCtxPath, umbrellaYaml, 'utf8');
262
+ console.log('');
263
+ console.log(' ✅ .agent/project-context.yaml (umbrella mode, generated)');
264
+ if (specSource) console.log(` spec_source : ${specSrc}`);
265
+ if (serviceList.length > 0) console.log(` services : ${serviceList.map(s => s.name).join(', ')}`);
266
+ } else {
267
+ console.log(' ℹ️ .agent/project-context.yaml (already exists — skipped)');
268
+ }
269
+ }
270
+
271
+ // 5. Monorepo: install into each service subfolder
272
+ if (serviceList.length > 0) {
273
+ console.log('');
274
+ console.log(`Setting up ${serviceList.length} service workspace(s)...`);
275
+
276
+ for (const svc of serviceList) {
277
+ const svcDir = path.join(projectRoot, svc.name);
278
+ const svcAgentDir = path.join(svcDir, '.agent');
279
+ const svcClaudeCmds = path.join(svcDir, '.claude', 'commands');
280
+
281
+ console.log('');
282
+ console.log(` ── ${svc.name} (${svc.module || 'no module'}) ──`);
283
+
284
+ // Create service dir if needed
285
+ fs.mkdirSync(svcDir, { recursive: true });
286
+
287
+ // Copy core → service/.agent/
288
+ copyDirRecursive(coreDir, svcAgentDir);
289
+ console.log(` ✅ ${svc.name}/.agent/`);
290
+
291
+ // Install module
292
+ if (svc.module) {
293
+ if (!AVAILABLE_MODULES.includes(svc.module)) {
294
+ console.log(` ⚠️ Unknown module "${svc.module}" — skipped`);
295
+ } else {
296
+ const srcMod = path.join(ROOT, 'modules', svc.module);
297
+ const destMod = path.join(svcAgentDir, 'modules', svc.module);
298
+ if (fs.existsSync(srcMod)) {
299
+ fs.mkdirSync(destMod, { recursive: true });
300
+ copyDirRecursive(srcMod, destMod);
301
+ console.log(` ✅ ${svc.name}/.agent/modules/${svc.module}/`);
302
+ }
303
+ }
304
+ }
305
+
306
+ // Create .claude/commands/ shortcuts
307
+ fs.mkdirSync(svcClaudeCmds, { recursive: true });
308
+ const svcCmdFiles = fs.readdirSync(path.join(svcAgentDir, 'commands')).filter(f => f.endsWith('.md'));
309
+ for (const cmdFile of svcCmdFiles) {
310
+ const cmdName = cmdFile.replace('.md', '');
311
+ const shortcut =
312
+ `# /${cmdName}\n\nRead the full command definition from \`.agent/commands/${cmdFile}\`` +
313
+ ` and execute it with arguments: $ARGUMENTS\n`;
314
+ fs.writeFileSync(path.join(svcClaudeCmds, cmdFile), shortcut, 'utf8');
315
+ }
316
+ console.log(` ✅ ${svc.name}/.claude/commands/ (${svcCmdFiles.length} shortcuts)`);
317
+
318
+ // Generate project-context.yaml (only if not already present)
319
+ const ctxPath = path.join(svcAgentDir, 'project-context.yaml');
320
+ if (!fs.existsSync(ctxPath)) {
321
+ const stack = MODULE_STACK[svc.module] || { language: 'TODO', framework: 'TODO' };
322
+ const yaml = [
323
+ `project:`,
324
+ ` name: ${path.basename(projectRoot)} — ${svc.name}`,
325
+ ``,
326
+ `tech_stack:`,
327
+ ` language: ${stack.language}`,
328
+ ` framework: ${stack.framework}`,
329
+ ` module: ${svc.module || 'TODO'}`,
330
+ ``,
331
+ `conventions:`,
332
+ ` ticket_prefix: FEAT`,
333
+ ``,
334
+ `paths:`,
335
+ ` specs_dir: ../specs/bdd`,
336
+ ` prd_dir: ../specs/prd`,
337
+ ` tech_docs_dir: ../specs/tech-docs`,
338
+ ` domain_knowledge_dir: ../specs/domain-knowledge`,
339
+ ` business_dictionary: ../specs/domain-knowledge/business-dictionary.md`,
340
+ ` core_entities: ../specs/domain-knowledge/core-entities.md`,
341
+ ` refinement_dir: ../specs/.review`,
342
+ ` product_definitions_dir: ../specs/product-definition`,
343
+ ` trace_dir: ../.trace`,
344
+ ``,
345
+ ].join('\n');
346
+ fs.writeFileSync(ctxPath, yaml, 'utf8');
347
+ console.log(` ✅ ${svc.name}/.agent/project-context.yaml (generated)`);
348
+ } else {
349
+ console.log(` ℹ️ ${svc.name}/.agent/project-context.yaml (already exists — skipped)`);
350
+ }
351
+ }
352
+
353
+ // Generate root project-context.yaml for PO/BA (only if not present)
354
+ const rootCtxPath = path.join(agentDir, 'project-context.yaml');
355
+ if (!fs.existsSync(rootCtxPath)) {
356
+ const svcYaml = serviceList.map(s => [
357
+ ` - name: ${s.name}`,
358
+ ` module: ${s.module || 'TODO'}`,
359
+ ` description: ""`,
360
+ ].join('\n')).join('\n');
361
+ const rootYaml = [
362
+ `project:`,
363
+ ` name: ${path.basename(projectRoot)}`,
364
+ ``,
365
+ `tech_stack:`,
366
+ ` language: multi-service`,
367
+ ``,
368
+ `services:`,
369
+ svcYaml,
370
+ ``,
371
+ `conventions:`,
372
+ ` ticket_prefix: FEAT`,
373
+ ``,
374
+ `paths:`,
375
+ ` specs_dir: specs/bdd`,
376
+ ` prd_dir: specs/prd`,
377
+ ` tech_docs_dir: specs/tech-docs`,
378
+ ` domain_knowledge_dir: specs/domain-knowledge`,
379
+ ` business_dictionary: specs/domain-knowledge/business-dictionary.md`,
380
+ ` core_entities: specs/domain-knowledge/core-entities.md`,
381
+ ` refinement_dir: specs/.review`,
382
+ ` product_definitions_dir: specs/product-definition`,
383
+ ` trace_dir: .trace`,
384
+ ``,
385
+ ].join('\n');
386
+ fs.writeFileSync(rootCtxPath, rootYaml, 'utf8');
387
+ console.log('');
388
+ console.log(' ✅ .agent/project-context.yaml (root — PO/BA workspace, generated)');
389
+ }
390
+ }
391
+
392
+ // 6. Summary
148
393
  console.log('');
149
394
  if (failedShortcuts.length > 0) {
150
395
  console.log(`⚠️ ${commandFiles.length - failedShortcuts.length}/${commandFiles.length} shortcuts created.`);
@@ -152,9 +397,38 @@ if (isInit) {
152
397
  }
153
398
  console.log(`✅ Framework v${frameworkVersion} installed!`);
154
399
  console.log('');
155
- console.log('Next steps:');
156
- console.log(' 1. Open Claude Code and run /setup-ai-first');
157
- console.log(' 2. Commit .agent/ to git so your whole team has the framework');
400
+ if (isUmbrella) {
401
+ console.log('Umbrella workspace ready:');
402
+ console.log(' Open Claude Code at project root (umbrella)');
403
+ console.log(' Agent reads specs from: ' + (specSource || 'spec_source in project-context.yaml'));
404
+ if (serviceList.length > 0) {
405
+ console.log(' Service submodules configured:');
406
+ serviceList.forEach(s => console.log(` ${s.name} (${s.module || 'no module'})`));
407
+ }
408
+ console.log('');
409
+ console.log('Next steps:');
410
+ console.log(' 1. Review .agent/project-context.yaml — update service paths + domain names');
411
+ console.log(' 2. Run /setup-ai-first in Claude Code');
412
+ console.log(' 3. Commit .agent/ to git');
413
+ console.log('');
414
+ } else
415
+ if (serviceList.length > 0) {
416
+ console.log('Monorepo workspaces ready:');
417
+ console.log(' PO/BA → open Claude Code at project root');
418
+ serviceList.forEach(s => {
419
+ console.log(` ${s.name.padEnd(10)} → open Claude Code at ./${s.name}/`);
420
+ });
421
+ console.log('');
422
+ console.log('Next steps:');
423
+ console.log(' 1. Fill in CLAUDE.md in each service subfolder (architecture + coding standards)');
424
+ console.log(' 2. Fill in .agent/project-context.yaml in each subfolder (review generated values)');
425
+ console.log(' 3. PO/BA: open root in Claude Code and run /setup-ai-first');
426
+ console.log(' 4. Commit everything to git so the whole team has the framework');
427
+ } else {
428
+ console.log('Next steps:');
429
+ console.log(' 1. Open Claude Code and run /setup-ai-first');
430
+ console.log(' 2. Commit .agent/ to git so your whole team has the framework');
431
+ }
158
432
  console.log('');
159
433
  console.log('To upgrade later: bash scripts/upgrade.sh');
160
434
  console.log(' (or: npx @anhth2/spec-driven-dev-plugin@latest --init)');
package/commands/debug.md CHANGED
@@ -34,7 +34,7 @@ Display and wait for response:
34
34
  ```
35
35
  ⚙️ MODEL CHECK
36
36
  ──────────────────────────────────────────────────────────────────
37
- Recommended : claude-opus-4-5 (or claude-opus-4)
37
+ Recommended : claude-opus-4 (or latest Opus model)
38
38
  Why needed : Spec analysis, architecture review, code generation
39
39
  require deep reasoning. Smaller models miss edge cases.
40
40
 
@@ -134,6 +134,7 @@ Read `.agent/project-context.yaml`. Extract and store:
134
134
  - `paths.core_entities` → path to core-entities.md
135
135
  - `paths.tech_docs_dir` → technical documentation root
136
136
  - `paths.trace_dir` → trace state directory
137
+ - `paths.design_spec_dir` → Design Spec documents root (FE/App only)
137
138
 
138
139
  If `paths` section is absent, use these defaults:
139
140
  - `specs_dir` = `specs/bdd`
@@ -143,13 +144,46 @@ If `paths` section is absent, use these defaults:
143
144
  - `domain_knowledge_dir` = `specs/domain-knowledge`
144
145
  - `business_dictionary` = `specs/domain-knowledge/business-dictionary.md`
145
146
  - `core_entities` = `specs/domain-knowledge/core-entities.md`
146
- - `tech_docs_dir` = `tech-docs`
147
+ - `tech_docs_dir` = `specs/tech-docs`
147
148
  - `trace_dir` = `.trace`
149
+ - `design_spec_dir` = `specs/design-spec`
148
150
 
149
151
  If `tech_stack.module` is set, also load `.agent/modules/{module}/stack-profile.yaml` if it exists.
150
152
 
151
153
  ---
152
154
 
155
+ ## Step 1.5 — [SERVICE ROUTING] Resolve service paths (umbrella mode)
156
+
157
+ *Skip this step entirely if `setup.mode` is not `"umbrella"` and `services` section is absent from project-context.yaml.*
158
+
159
+ If `services` section is present:
160
+
161
+ **1. Detect active domain** (in priority order):
162
+ - Read `@trace.domain` from target file frontmatter (if Gate loaded a target file)
163
+ - Extract from target file path: segment immediately after `prd_dir` base path
164
+ *(e.g., `specs/prd/user/FEAT-01.md` → domain = `user`)*
165
+ - If `$ARGUMENTS` contains a path, extract the segment after `prd_dir`
166
+
167
+ **2. Route to service** — if active domain matches a key in `services`:
168
+ - Override `paths.specs_dir` → `services.{domain}.specs_dir`
169
+ - Override `paths.tech_docs_dir` → `services.{domain}.tech_docs_dir`
170
+ - Store `active_service` = `services.{domain}.path`
171
+ - Store `active_service_module` = `services.{domain}.module`
172
+ - If service has its own `module` → use it as `active_module` (overrides `tech_stack.module`)
173
+
174
+ **3. Fallback** — if domain not detected or no matching service key:
175
+ - Keep default paths from Step 1
176
+ - Set `active_service = unresolved`
177
+
178
+ **4. Spec source auto-override** — if `setup.spec_source` is set AND the corresponding path was not already explicitly set in `paths:`:
179
+ - Override `paths.prd_dir` → `{spec_source}/specs/prd`
180
+ - Override `paths.design_spec_dir` → `{spec_source}/specs/design-spec`
181
+ - Override `paths.domain_knowledge_dir` → `{spec_source}/specs/domain-knowledge`
182
+ - Override `paths.business_dictionary` → `{spec_source}/specs/domain-knowledge/business-dictionary.md`
183
+ - Override `paths.core_entities` → `{spec_source}/specs/domain-knowledge/core-entities.md`
184
+
185
+ ---
186
+
153
187
  ## Step 2 — [PROJECT-CONFIG] Load module stack profile (conditional)
154
188
 
155
189
  If `tech_stack.module` is set, read `.agent/modules/{module}/stack-profile.yaml`.
@@ -220,6 +254,26 @@ If the file does not exist → skip silently.
220
254
 
221
255
  ---
222
256
 
257
+ ## Step 6.5 — [PLATFORM] Derive active_module and platform_type
258
+
259
+ Using `tech_stack.module` loaded in Step 1, derive and store two variables for use by all downstream commands:
260
+
261
+ ```
262
+ active_module = tech_stack.module (e.g. "java-spring", "react", "flutter")
263
+ ```
264
+
265
+ | `platform_type` | Modules |
266
+ |---|---|
267
+ | `backend` | `java-spring`, `golang`, `dotnet`, `php-laravel`, `context-engineering` |
268
+ | `web-frontend` | `react`, `nextjs`, `vue`, `nuxt`, `angular` |
269
+ | `mobile` | `flutter`, `react-native`, `ios-swiftui`, `android-compose` |
270
+
271
+ If `tech_stack.module` is blank or not recognized → set `platform_type = "unknown"` and flag as ⚠️ in the Step 7 recap.
272
+
273
+ These two variables (`active_module`, `platform_type`) are the canonical source for all branching logic in commands that need platform-specific behavior (generate-tests, debug, fix-bug, smoke-test).
274
+
275
+ ---
276
+
223
277
  ## Step 7 — [RECAP] Working Memory Recap (anti-lost-in-middle)
224
278
 
225
279
  After loading all context, synthesize and output a compact summary block.
@@ -230,10 +284,12 @@ Output exactly this block:
230
284
  ```
231
285
  [CTX LOADED]
232
286
  Stack : {language} / {framework} / {database}
287
+ Platform : {active_module} ({platform_type})
233
288
  Layers : {layer order from CLAUDE.md §2, e.g., Controller → Facade → Service → Repository}
234
289
  Ticket : {ticket_prefix}-
235
290
  Dict : {loaded — N canonical terms, M banned terms | missing}
236
291
  Entities : {loaded — EntityA, EntityB, EntityC | missing}
292
+ Service : {active_service} ({active_service_module}) | single-service
237
293
  Status : {FULL | PARTIAL — missing: CLAUDE.md / business-dict / core-entities | MINIMAL}
238
294
  ```
239
295
 
@@ -257,13 +313,112 @@ Proceed to the next step of the calling command.
257
313
 
258
314
  ---
259
315
 
260
- ## InputPaste one of:
261
- 1. Stack trace / error log
262
- 2. Test failure output
263
- 3. File path + description of unexpected behavior
264
- 4. A specific question about a code snippet
316
+ ## Step 1 Classify debug type
317
+
318
+ After loading context, display this prompt and wait for the user's choice:
319
+
320
+ ```
321
+ DEBUG SESSION
322
+ ──────────────────────────────────────────────────────────────
323
+ What's your situation?
324
+
325
+ 1 I already have a stack trace / error log → paste it
326
+ 2 I need to reproduce the error first → show me the run command
327
+ 3 A test is failing → I'll run tests, then paste output
328
+ 4 Code question (no runtime needed) → ask away
329
+ ──────────────────────────────────────────────────────────────
330
+ Enter 1 / 2 / 3 / 4:
331
+ ```
332
+
333
+ Wait for the user's choice, then follow the corresponding path below.
334
+
335
+ ---
336
+
337
+ ### Path 1 — Already have error output
338
+
339
+ Ask:
340
+ ```
341
+ Paste your stack trace / error log below:
342
+ ```
343
+
344
+ Wait for input, then proceed to [Stack Trace Analysis](#stack-trace-analysis).
345
+
346
+ ---
347
+
348
+ ### Path 2 — Need to reproduce first
349
+
350
+ Display the run command from `conventions.service_run` in `project-context.yaml`:
351
+
352
+ ```
353
+ Start your service first:
354
+
355
+ {conventions.service_run}
356
+
357
+ (If you use Docker: `docker compose up -d`, then verify with `docker compose ps`)
358
+
359
+ Once the service is running:
360
+ 1. Trigger the behavior that causes the error
361
+ 2. Copy the full stack trace or error log
362
+ 3. Paste it here
363
+
364
+ Waiting for your error output...
365
+ ```
366
+
367
+ Wait for the user to paste the error, then proceed to [Stack Trace Analysis](#stack-trace-analysis).
368
+
369
+ If `conventions.service_run` is not set → show:
370
+ ```
371
+ ⚠️ service_run is not configured in .agent/project-context.yaml.
372
+ Add it so this command can show the correct start command:
373
+
374
+ conventions:
375
+ service_run: "mvn spring-boot:run" # or: npm run dev / go run . / etc.
376
+ ```
377
+ Then ask the user to start the service manually and paste the error when ready.
378
+
379
+ ---
380
+
381
+ ### Path 3 — Test is failing
382
+
383
+ Display the test command from `conventions.test_command` in `project-context.yaml`:
384
+
385
+ ```
386
+ Run your tests first:
387
+
388
+ {conventions.test_command}
389
+
390
+ Once the run finishes, paste the full test failure output here.
391
+
392
+ Waiting...
393
+ ```
394
+
395
+ Wait for the user to paste the failure output, then proceed to [Test Failure Analysis](#test-failure-analysis).
396
+
397
+ If `conventions.test_command` is not set → show:
398
+ ```
399
+ ⚠️ test_command is not configured in .agent/project-context.yaml.
400
+ Add it so this command can show the correct test command:
401
+
402
+ conventions:
403
+ test_command: "mvn test" # or: npm test / go test ./... / etc.
404
+ ```
405
+ Then ask the user to run tests manually and paste the output when ready.
406
+
407
+ ---
408
+
409
+ ### Path 4 — Code question
410
+
411
+ Ask:
412
+ ```
413
+ Describe your question or paste the code snippet you're asking about:
414
+ ```
415
+
416
+ Wait for input, then answer directly using loaded project context (architecture rules, layer order, coding standards from CLAUDE.md).
417
+
418
+ ---
265
419
 
266
420
  ## Stack Trace Analysis
421
+
267
422
  Read from **bottom up** — `Caused by:` is the real root cause:
268
423
  ```
269
424
  Caused by: {RealException} ← start here
@@ -272,20 +427,83 @@ Caused by: {RealException} ← start here
272
427
 
273
428
  ## Common Error Patterns
274
429
 
430
+ Use `active_module` from context to select the relevant table.
431
+
432
+ ### If `platform_type = backend`
433
+
434
+ #### java-spring / golang / dotnet / php-laravel
435
+
275
436
  | Error | Likely Cause | Fix Direction |
276
437
  |-------|-------------|---------------|
277
438
  | NullPointerException | Null object access; Optional not handled | Check Optional.orElseThrow, null guards |
278
439
  | ClassCastException | Wrong type assumption | Check type at assignment/return |
279
440
  | OutOfMemoryError | Loading too much data | Add pagination |
280
441
  | StackOverflowError | Infinite recursion | Find recursive call with no base case |
281
- | Connection refused | Dependency not running | Check config URLs |
442
+ | Connection refused | Dependency not running | Check config URLs / start service |
282
443
  | 401 Unauthorized | Token expired, wrong config | Verify token, check auth config |
283
444
  | 403 Forbidden | Wrong role | Check auth annotations |
284
445
  | DB constraint violation | Duplicate key, null in NOT NULL | Check data and constraints |
285
446
  | Serialization error | Circular reference | Check DTO/mapper config |
286
447
  | Test assertion mismatch | Wrong mock or wrong expected | Re-read mock setup |
287
448
 
449
+ #### context-engineering (AI/LLM pipelines)
450
+
451
+ | Error | Likely Cause | Fix Direction |
452
+ |-------|-------------|---------------|
453
+ | `APIError` / `RateLimitError` | LLM quota exceeded or service down | Check API key, rate limits; add exponential backoff |
454
+ | `TokenLimitError` / `context_length_exceeded` | Input prompt too long | Truncate/chunk input; review prompt template size |
455
+ | `AuthenticationError` | API key invalid or expired | Check env var; rotate key |
456
+ | Response validation / schema mismatch | LLM output doesn't match expected format | Add output parser; retry with stricter prompt |
457
+ | `JSONDecodeError` on LLM output | Model returned non-JSON text | Add JSON extraction post-processing or stricter system prompt |
458
+ | Hanging / slow test | Real LLM called in test instead of mock | Verify `patch('...')` applied; add timeout guard |
459
+ | Flaky results across runs | Non-deterministic LLM response | Use fixed mock in tests; check temperature = 0 for determinism |
460
+
461
+ ### If `platform_type = web-frontend`
462
+
463
+ | Error | Likely Cause | Fix Direction |
464
+ |-------|-------------|---------------|
465
+ | `Cannot read properties of undefined` | Data not loaded yet | Add loading guard / optional chaining `?.` |
466
+ | `useEffect` infinite loop | Dependency array wrong | Review deps, use stable refs / `useCallback` |
467
+ | `Cannot update state on unmounted component` | Async resolves after unmount | Cancel in cleanup / use AbortController |
468
+ | CORS error | API not configured | Check backend CORS config or dev proxy setup |
469
+ | 401 Unauthorized | Token expired or missing | Refresh token / check Authorization header |
470
+ | White screen / no output | Unhandled render error | Check browser console, add ErrorBoundary |
471
+ | Type error (Zod / TypeScript) | API response shape mismatch | Compare actual response vs type definition |
472
+ | `act(...)` warning in test | Async state update | Wrap in `act(async () => {...})` |
473
+ | Module not found | Import path wrong | Check relative path / tsconfig alias |
474
+
475
+ ### If `platform_type = mobile`
476
+
477
+ #### Flutter
478
+ | Error | Likely Cause | Fix Direction |
479
+ |-------|-------------|---------------|
480
+ | `Null check operator on null value` | Nullable not guarded | Add `?` or null check before `!` |
481
+ | `pumpAndSettle timed out` | Async not completing in test | Use `pump(Duration(...))` |
482
+ | `setState called after dispose` | Async continues after widget removed | Cancel in `dispose()` |
483
+ | `RenderFlex overflow` | Widget too wide for screen | Wrap with `Flexible`, `Expanded`, or `SingleChildScrollView` |
484
+ | BLoC state not updating | Event not dispatched | Verify `bloc.add(Event())` is called |
485
+ | `MissingPluginException` | Native plugin not linked | Run `flutter clean && flutter pub get` |
486
+
487
+ #### React Native
488
+ | Error | Likely Cause | Fix Direction |
489
+ |-------|-------------|---------------|
490
+ | `undefined is not an object` | Null prop access | Add null check / optional chaining |
491
+ | Metro bundler error | Cache stale | `npx react-native start --reset-cache` |
492
+ | `VirtualizedLists nested` | FlatList inside ScrollView | Use `nestedScrollEnabled` or restructure |
493
+ | Navigation `undefined` | `useNavigation` outside navigator | Wrap component inside correct navigator |
494
+ | `act(...)` warning | Async state update in test | Wrap in `act(async () => {...})` |
495
+
496
+ #### iOS / Android
497
+ | Error | Likely Cause | Fix Direction |
498
+ |-------|-------------|---------------|
499
+ | `SIGABRT` / `EXC_BAD_ACCESS` (iOS) | Nil dereference | Add optional binding `if let` / `guard let` |
500
+ | `IllegalStateException` (Android) | Lifecycle violation | Check if fragment/activity still attached |
501
+ | `NetworkOnMainThreadException` | Network call on UI thread | Move to coroutine / background thread |
502
+ | Build fails after pod install | Pod cache stale | `pod deintegrate && pod install` |
503
+ | `Hilt injection failed` | Missing `@AndroidEntryPoint` | Add annotation to Activity/Fragment |
504
+
288
505
  ## Test Failure Analysis
506
+
289
507
  ```
290
508
  Expected: {value}
291
509
  Actual : {value}
@@ -293,6 +511,8 @@ Actual : {value}
293
511
  ```
294
512
  1. What is the gap? 2. Is mock setup correct? 3. Is assertion logically correct?
295
513
 
514
+ ---
515
+
296
516
  ## Output
297
517
 
298
518
  # Report Footer — Standard Command Output Format
@@ -323,21 +543,23 @@ Suggest the logical next command based on workflow phase:
323
543
 
324
544
  | Current command | Suggest next |
325
545
  |-------------------------|-----------------------------------------------|
546
+ | /setup-ai-first | `/define-product` to start your first feature |
326
547
  | /define-product | `/generate-prd {product-definition-file}` |
327
548
  | /generate-prd | `/refine-prd {prd-file}` then `/review-context {prd-file}` |
328
549
  | /refine-prd | Open Review Board → update PRD → `/review-context {prd-file}` |
329
- | /review-context (PRD) | `/generate-bdd {prd-file}` if APPROVED; fix PRD if NEEDS_FIX |
550
+ | /review-context (PRD) | FE/App: `/generate-design-spec {prd-file}` (then BDD after sign-off); BE: `/generate-bdd {prd-file}` directly; fix PRD if NEEDS_FIX |
551
+ | /generate-design-spec | Designer review → Figma links confirmed → PO + Designer sign-off → `/generate-bdd {prd-file}` |
330
552
  | /generate-bdd | `/review-context {feature-file}` to verify coverage |
331
553
  | /review-context (BDD) | `/generate-tech-docs {UC-ID}` if APPROVED; regenerate if NEEDS_FIX |
332
554
  | /generate-tech-docs | `/review-tech-docs {tech-design-file}` |
333
555
  | /review-tech-docs | `/generate-code {feature-file}` if APPROVED; fix doc if NEEDS_FIX |
334
- | /generate-code | `/generate-tests {UC-ID}` |
556
+ | /generate-code | First gen → `/review-code {UC-ID}`; re-gen → `/generate-tests {UC-ID}` |
335
557
  | /generate-tests | `/run-tests {UC-ID}` |
336
558
  | /run-tests (passing) | `/review-code {UC-ID}` |
337
559
  | /run-tests (failing) | `/fix-bug {ticket-id}` or `/debug {error}` |
338
560
  | /review-code | `/smoke-test {UC-ID}` or create PR |
339
561
  | /smoke-test | Create PR and link to ticket |
340
- | /validate-traces | `/generate-code {UC-ID}` for gaps |
562
+ | /validate-traces | DRIFT/UNTRACKED → `/generate-code {UC-ID}`; GAP → `/generate-tests {UC-ID}`; all OK → create PR |
341
563
  | /fix-bug | Create PR and link to ticket |
342
564
  | /debug | `/fix-bug {ticket-id}` if fix needed |
343
565