@polymorphism-tech/morph-spec 4.21.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/CLAUDE.md +57 -71
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +224 -491
  4. package/claude-plugin.json +1 -1
  5. package/docs/CHEATSHEET.md +1 -1
  6. package/docs/QUICKSTART.md +1 -1
  7. package/framework/CLAUDE.md +55 -38
  8. package/framework/MORPH.md +244 -0
  9. package/framework/agents.json +127 -1183
  10. package/framework/commands/morph-apply.md +56 -240
  11. package/framework/commands/morph-archive.md +48 -85
  12. package/framework/commands/morph-preflight.md +7 -7
  13. package/framework/commands/morph-proposal.md +35 -191
  14. package/framework/commands/morph-status.md +19 -50
  15. package/framework/commands/morph-troubleshoot.md +2 -2
  16. package/framework/evals/rubrics/README.md +44 -0
  17. package/framework/evals/rubrics/architecture.md +46 -0
  18. package/framework/evals/rubrics/code-quality.md +50 -0
  19. package/framework/evals/rubrics/contracts.md +47 -0
  20. package/framework/evals/rubrics/test-coverage.md +50 -0
  21. package/framework/evals/scenarios/dotnet-crud.yaml +42 -0
  22. package/framework/evals/scenarios/nextjs-auth.yaml +38 -0
  23. package/framework/evals/suites/standard.yaml +5 -0
  24. package/framework/evals/trace-schema.md +67 -0
  25. package/framework/hooks/claude-code/core/README.md +3 -0
  26. package/framework/hooks/claude-code/core/denylist.json +14 -0
  27. package/framework/hooks/claude-code/{pre-tool-use → core}/enforce-phase-writes.js +5 -5
  28. package/framework/hooks/claude-code/core/post-edit-typecheck.js +69 -0
  29. package/framework/hooks/claude-code/core/pre-bash-denylist.js +29 -0
  30. package/framework/hooks/claude-code/{pre-tool-use → core}/protect-spec-files.js +4 -6
  31. package/framework/hooks/claude-code/{stop → core}/validate-completion.js +13 -82
  32. package/framework/hooks/shared/phase-utils.js +20 -28
  33. package/framework/hooks/shared/state-reader.js +4 -5
  34. package/framework/{skills/level-0-meta/mcp-registry.json → mcp/registry.json} +254 -210
  35. package/framework/phases.json +40 -114
  36. package/framework/rubrics/eval-rubric.json +66 -0
  37. package/framework/skills/README.md +39 -64
  38. package/framework/skills/core/README.md +3 -0
  39. package/framework/skills/core/morph-brainstorming/SKILL.md +102 -0
  40. package/framework/skills/core/morph-eval/SKILL.md +57 -0
  41. package/framework/skills/core/morph-implement/SKILL.md +83 -0
  42. package/framework/skills/core/morph-plan/SKILL.md +64 -0
  43. package/framework/skills/core/morph-review/SKILL.md +62 -0
  44. package/framework/skills/core/morph-stack-scan/SKILL.md +109 -0
  45. package/framework/skills/core/morph-standards/SKILL.md +49 -0
  46. package/framework/standards/STANDARDS.json +32 -0
  47. package/framework/standards/frontend/design-system/ai-image-generation.md +244 -0
  48. package/framework/standards/integration/mcp/mcp-tools.md +48 -0
  49. package/framework/templates/CLAUDE.md.core.guide.md +29 -0
  50. package/framework/templates/CLAUDE.md.core.template +22 -0
  51. package/framework/templates/REGISTRY.json +4 -4
  52. package/framework/workflows/configs/fast-track.json +5 -10
  53. package/framework/workflows/configs/full.json +4 -8
  54. package/framework/workflows/configs/quick.json +32 -0
  55. package/framework/workflows/configs/standard.json +57 -64
  56. package/package.json +7 -6
  57. package/src/commands/mcp/mcp-setup.js +58 -31
  58. package/src/commands/project/doctor.js +69 -70
  59. package/src/commands/project/init.js +26 -5
  60. package/src/commands/project/status.js +18 -19
  61. package/src/commands/state/approve.js +1 -1
  62. package/src/commands/validation/validate-feature.js +18 -112
  63. package/src/core/paths/output-schema.js +133 -151
  64. package/src/core/state/phase-state-machine.js +10 -24
  65. package/src/core/state/state-manager.js +17 -17
  66. package/src/core/workflows/workflow-detector.js +214 -31
  67. package/src/core/workflows/workflow-router.js +204 -0
  68. package/src/dashboard/app/pages/AgentMap.jsx +32 -33
  69. package/src/dashboard/app/pages/LiveState.jsx +1 -1
  70. package/src/dashboard/app/pages/PhasePipeline.jsx +2 -4
  71. package/src/dashboard/app/pages/SkillsLibrary.jsx +2 -3
  72. package/src/dashboard/app/pages/Workflows.jsx +2 -2
  73. package/src/dashboard/scanner/agents-scanner.js +30 -35
  74. package/src/lib/agents/metrics-aggregator.js +1 -1
  75. package/src/lib/agents/output-collector.js +7 -8
  76. package/src/lib/context/context-builder.js +289 -0
  77. package/src/lib/detectors/claude-config-detector.js +19 -3
  78. package/src/lib/generators/context-generator.js +20 -42
  79. package/src/lib/generators/index.js +1 -0
  80. package/src/lib/generators/mandate-generator.js +512 -0
  81. package/src/lib/generators/metadata-extractor.js +1 -1
  82. package/src/lib/generators/recap-generator.js +3 -4
  83. package/src/lib/installers/mcp-installer.js +300 -92
  84. package/src/lib/phase-chain/phase-validator.js +36 -40
  85. package/src/lib/stack/stack-profile.js +74 -91
  86. package/src/lib/tasks/task-parser.js +1 -1
  87. package/src/lib/tasks/wave-resolver.js +353 -0
  88. package/src/lib/validators/checkpoint-persistence.js +2 -2
  89. package/src/lib/validators/content/cross-phase-validator.js +281 -0
  90. package/src/lib/validators/content/index.js +1 -0
  91. package/src/lib/validators/shared/emit-validator-dispatch.js +6 -56
  92. package/src/scripts/global-install.js +2 -27
  93. package/src/utils/agents-installer.js +224 -447
  94. package/src/utils/claude-settings-manager.js +57 -15
  95. package/src/utils/file-copier.js +1 -0
  96. package/src/utils/hooks-installer.js +15 -117
  97. package/src/utils/skills-installer.js +12 -18
  98. package/bin/detect-agents.js +0 -226
  99. package/bin/task-manager.js +0 -1036
  100. package/framework/agents/README.md +0 -13
  101. package/framework/agents/ai-agents/ai-system-architect.md +0 -194
  102. package/framework/agents/ai-agents/vector-search-expert.md +0 -247
  103. package/framework/agents/architecture/architecture-expert.md +0 -98
  104. package/framework/agents/architecture/morph-spec-architect.md +0 -66
  105. package/framework/agents/architecture/packages-validator.md +0 -89
  106. package/framework/agents/architecture/security-expert.md +0 -79
  107. package/framework/agents/architecture/seo-growth-hacker.md +0 -168
  108. package/framework/agents/architecture/standards-architect.md +0 -153
  109. package/framework/agents/backend/dotnet-senior.md +0 -163
  110. package/framework/agents/backend/ef-modeler.md +0 -119
  111. package/framework/agents/backend/hangfire-orchestrator.md +0 -133
  112. package/framework/agents/backend/ms-agent-expert.md +0 -55
  113. package/framework/agents/backend/neon-expert.md +0 -379
  114. package/framework/agents/frontend/css-specialist.md +0 -89
  115. package/framework/agents/frontend/design-system-validator.md +0 -100
  116. package/framework/agents/frontend/gsap-animator.md +0 -131
  117. package/framework/agents/frontend/nextjs-component-validator.md +0 -97
  118. package/framework/agents/frontend/nextjs-expert.md +0 -201
  119. package/framework/agents/frontend/ui-designer.md +0 -259
  120. package/framework/agents/infrastructure/infra-architect.md +0 -215
  121. package/framework/agents/integrations/asaas-financial.md +0 -138
  122. package/framework/agents/integrations/neon-auth.md +0 -151
  123. package/framework/agents/integrations/resend-email.md +0 -127
  124. package/framework/hooks/claude-code/post-tool-use/agent-complete.js +0 -122
  125. package/framework/hooks/claude-code/post-tool-use/dispatch.js +0 -363
  126. package/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +0 -78
  127. package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +0 -194
  128. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +0 -199
  129. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +0 -55
  130. package/framework/hooks/claude-code/session-start/inject-morph-context.js +0 -323
  131. package/framework/hooks/claude-code/session-start/post-compact-restore.js +0 -41
  132. package/framework/hooks/claude-code/session-start/resume-context.js +0 -77
  133. package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +0 -90
  134. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +0 -114
  135. package/framework/hooks/shared/compact-restore.js +0 -123
  136. package/framework/hooks/shared/dispatch-helpers.js +0 -147
  137. package/framework/hooks/shared/skill-reminder-helpers.js +0 -79
  138. package/framework/hooks/shared/stale-task-reset.js +0 -57
  139. package/framework/hooks/shared/worktree-helpers.js +0 -75
  140. package/framework/skills/level-0-meta/README.md +0 -7
  141. package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +0 -472
  142. package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +0 -174
  143. package/framework/skills/level-0-meta/morph-brainstorming/references/proposal-example.md +0 -138
  144. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +0 -145
  145. package/framework/skills/level-0-meta/morph-code-review/SKILL.md +0 -250
  146. package/framework/skills/level-0-meta/morph-code-review/references/review-example.md +0 -164
  147. package/framework/skills/level-0-meta/morph-code-review/references/review-guidelines.md +0 -100
  148. package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +0 -151
  149. package/framework/skills/level-0-meta/morph-code-review-nextjs/SKILL.md +0 -165
  150. package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +0 -254
  151. package/framework/skills/level-0-meta/morph-code-review-nextjs/scripts/scan-nextjs.mjs +0 -189
  152. package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +0 -344
  153. package/framework/skills/level-0-meta/morph-frontend-review/scripts/scan-accessibility.mjs +0 -376
  154. package/framework/skills/level-0-meta/morph-init/SKILL.md +0 -537
  155. package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +0 -225
  156. package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +0 -382
  157. package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-dev-server.mjs +0 -153
  158. package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-stack.mjs +0 -234
  159. package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +0 -106
  160. package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +0 -243
  161. package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +0 -169
  162. package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +0 -148
  163. package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +0 -110
  164. package/framework/skills/level-1-workflows/README.md +0 -7
  165. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +0 -269
  166. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +0 -453
  167. package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +0 -44
  168. package/framework/skills/level-1-workflows/morph-phase-design/references/spec-authoring-guide.md +0 -55
  169. package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +0 -253
  170. package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
  171. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +0 -493
  172. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +0 -50
  173. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +0 -46
  174. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +0 -47
  175. package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +0 -132
  176. package/framework/skills/level-1-workflows/morph-phase-implement/references/vsa-implementation-guide.md +0 -92
  177. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +0 -257
  178. package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
  179. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +0 -291
  180. package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -143
  181. package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +0 -404
  182. package/framework/skills/level-1-workflows/morph-phase-tasks/scripts/validate-tasks.mjs +0 -112
  183. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +0 -788
  184. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +0 -147
  185. package/framework/squad-templates/backend-only.json +0 -47
  186. package/framework/squad-templates/frontend-only.json +0 -56
  187. package/framework/squad-templates/full-stack.json +0 -74
  188. package/src/commands/agents/agent-metrics.js +0 -174
  189. package/src/commands/agents/audit-dispatch.js +0 -82
  190. package/src/commands/agents/detect-agents.js +0 -275
  191. package/src/commands/agents/dispatch-agents.js +0 -461
  192. package/src/commands/agents/index.js +0 -13
  193. package/src/commands/agents/manage-agents.js +0 -352
  194. package/src/commands/agents/project-metrics.js +0 -74
  195. package/src/commands/phase/phase-reset.js +0 -74
  196. package/src/commands/scope/escalate.js +0 -217
  197. package/src/commands/state/advance-phase.js +0 -600
  198. package/src/commands/state/phase-runner.js +0 -215
  199. package/src/commands/state/state.js +0 -444
  200. package/src/commands/task/expand.js +0 -100
  201. package/src/commands/tasks/index.js +0 -4
  202. package/src/commands/tasks/task.js +0 -105
  203. package/src/commands/templates/index.js +0 -5
  204. package/src/commands/templates/template-render.js +0 -93
  205. package/src/commands/trust/trust.js +0 -118
  206. package/src/lib/agents/agents-manager.js +0 -349
  207. package/src/lib/build/build-runner.js +0 -99
  208. package/src/lib/checkpoints/checkpoint-hooks.js +0 -111
  209. package/src/lib/contracts/sprint-contract-generator.js +0 -137
  210. package/src/lib/scope/impact-analyzer.js +0 -106
  211. package/src/lib/trust/trust-manager.js +0 -157
  212. package/src/utils/tier-helpers.js +0 -24
  213. /package/framework/hooks/claude-code/{post-tool-use → core}/loop-detect.js +0 -0
@@ -1,153 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * detect-dev-server.mjs
4
- *
5
- * Detects an active dev server on common localhost ports,
6
- * optionally starting one if not found.
7
- *
8
- * Usage:
9
- * node detect-dev-server.mjs [startCommand]
10
- *
11
- * Examples:
12
- * node detect-dev-server.mjs
13
- * node detect-dev-server.mjs "npm run dev"
14
- * node detect-dev-server.mjs "dotnet run --project src/Api/Api.csproj"
15
- *
16
- * Output JSON (stdout):
17
- * { found: true, url: "http://localhost:3000", started?: true } → exit 0
18
- * { found: false, error?: "..." } → exit 1
19
- *
20
- * Behavior:
21
- * 1. Scan ports [3000, 3001, 4200, 5000, 5001, 7000, 8000, 8080] with 500ms timeout
22
- * 2. If found → output { found: true, url } and exit 0
23
- * 3. If not found and startCommand provided:
24
- * - Spawn startCommand as background process
25
- * - Poll ports every 2s for up to 30s
26
- * - If server starts → output { found: true, url, started: true } and exit 0
27
- * - If timeout → output { found: false, error: "timeout after 30s" } and exit 1
28
- * 4. If not found and no startCommand → output { found: false } and exit 1
29
- */
30
-
31
- import { spawn } from 'child_process';
32
-
33
- const PORTS = [3000, 3001, 4200, 5000, 5001, 7000, 8000, 8080];
34
- const FETCH_TIMEOUT_MS = 500;
35
- const START_TIMEOUT_MS = 30_000;
36
- const POLL_INTERVAL_MS = 2_000;
37
-
38
- const startCommand = process.argv[2] ?? null;
39
-
40
- /**
41
- * Try to fetch a URL with a timeout.
42
- * Returns true if response is received (any status code), false on network error/timeout.
43
- */
44
- async function isPortOpen(url) {
45
- const controller = new AbortController();
46
- const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
47
- try {
48
- await fetch(url, { signal: controller.signal });
49
- return true;
50
- } catch {
51
- return false;
52
- } finally {
53
- clearTimeout(timer);
54
- }
55
- }
56
-
57
- /**
58
- * Scan all known ports and return the first responsive URL, or null.
59
- */
60
- async function scanPorts() {
61
- for (const port of PORTS) {
62
- const url = `http://localhost:${port}`;
63
- if (await isPortOpen(url)) {
64
- return url;
65
- }
66
- }
67
- return null;
68
- }
69
-
70
- // ─── Step 1: Scan for existing server ────────────────────────────────────────
71
-
72
- const existing = await scanPorts();
73
-
74
- if (existing) {
75
- console.log(JSON.stringify({ found: true, url: existing }, null, 2));
76
- process.exit(0);
77
- }
78
-
79
- // ─── Step 2: No server found, try to start one ───────────────────────────────
80
-
81
- if (!startCommand) {
82
- console.log(JSON.stringify({ found: false }, null, 2));
83
- process.exit(1);
84
- }
85
-
86
- // Spawn the start command as a background process
87
- // Support both simple commands and commands with "cd X && Y" syntax
88
- let child;
89
- try {
90
- // Use shell=true to handle complex commands like "cd frontend && npm run dev"
91
- child = spawn(startCommand, [], {
92
- shell: true,
93
- detached: false,
94
- stdio: ['ignore', 'pipe', 'pipe'],
95
- });
96
-
97
- // Suppress child output (we only care if it starts)
98
- child.stdout?.resume();
99
- child.stderr?.resume();
100
- } catch (err) {
101
- console.log(JSON.stringify({ found: false, error: `Failed to spawn: ${err.message}` }, null, 2));
102
- process.exit(1);
103
- }
104
-
105
- // Poll for the server to become available
106
- const startedAt = Date.now();
107
-
108
- const result = await new Promise((resolve) => {
109
- const poll = async () => {
110
- const elapsed = Date.now() - startedAt;
111
-
112
- if (elapsed >= START_TIMEOUT_MS) {
113
- resolve({ found: false, error: 'timeout after 30s — server did not start in time' });
114
- return;
115
- }
116
-
117
- const url = await scanPorts();
118
- if (url) {
119
- resolve({ found: true, url, started: true });
120
- return;
121
- }
122
-
123
- setTimeout(poll, POLL_INTERVAL_MS);
124
- };
125
-
126
- // Handle child process errors / early exit
127
- child.on('error', (err) => {
128
- resolve({ found: false, error: `Process error: ${err.message}` });
129
- });
130
-
131
- child.on('exit', (code) => {
132
- // Only treat as error if it exited quickly (< 3s) with non-zero
133
- if (Date.now() - startedAt < 3_000 && code !== 0 && code !== null) {
134
- resolve({ found: false, error: `Process exited with code ${code}` });
135
- }
136
- // Otherwise it may have crashed after starting — let the poll timeout handle it
137
- });
138
-
139
- // Start polling after a short initial delay
140
- setTimeout(poll, POLL_INTERVAL_MS);
141
- });
142
-
143
- // Clean up the child process if still running
144
- if (child && !child.killed) {
145
- try {
146
- child.kill('SIGTERM');
147
- } catch {
148
- // ignore cleanup errors
149
- }
150
- }
151
-
152
- console.log(JSON.stringify(result, null, 2));
153
- process.exit(result.found ? 0 : 1);
@@ -1,234 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * detect-stack.mjs
4
- *
5
- * Detects the project tech stack from config, context, or file scan.
6
- *
7
- * Usage:
8
- * node detect-stack.mjs
9
- *
10
- * Output JSON:
11
- * {
12
- * stack: "DOTNET" | "NEXTJS" | "FULLSTACK" | "UNKNOWN",
13
- * frontendPath: string | null,
14
- * backendPath: string | null,
15
- * startCommand: string | null
16
- * }
17
- *
18
- * Detection order (most reliable first):
19
- * 1. .morph/config/config.json → config.project.stack + frontendPath/backendPath
20
- * 2. .morph/context/README.md → "## Tech Stack" section
21
- * 3. Fallback: glob *.csproj (DOTNET) + package.json with dep "next" (NEXTJS)
22
- *
23
- * Exit code: 0 always
24
- */
25
-
26
- import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
27
- import { join, relative, dirname } from 'path';
28
-
29
- const cwd = process.cwd();
30
-
31
- /** Try to read a JSON file safely. Returns null on failure. */
32
- function readJson(filePath) {
33
- try {
34
- return JSON.parse(readFileSync(filePath, 'utf-8'));
35
- } catch {
36
- return null;
37
- }
38
- }
39
-
40
- /** Try to read a text file safely. Returns null on failure. */
41
- function readText(filePath) {
42
- try {
43
- return readFileSync(filePath, 'utf-8');
44
- } catch {
45
- return null;
46
- }
47
- }
48
-
49
- /**
50
- * Find files matching a pattern recursively.
51
- * Returns relative paths from cwd.
52
- */
53
- function findFiles(dir, predicate, maxDepth = 4, _depth = 0) {
54
- if (_depth > maxDepth) return [];
55
- if (!existsSync(dir)) return [];
56
- const results = [];
57
- const ignored = ['node_modules', '.git', '.next', 'dist', 'build', '.turbo', 'bin', 'obj', '.morph'];
58
- try {
59
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
60
- if (entry.isDirectory()) {
61
- if (!ignored.includes(entry.name)) {
62
- results.push(...findFiles(join(dir, entry.name), predicate, maxDepth, _depth + 1));
63
- }
64
- } else if (entry.isFile() && predicate(entry.name)) {
65
- results.push(join(dir, entry.name));
66
- }
67
- }
68
- } catch {
69
- // ignore permission errors
70
- }
71
- return results;
72
- }
73
-
74
- /**
75
- * Normalize a stack string from config to canonical form.
76
- * Handles values like "nextjs+dotnet", "dotnet", "next.js", "nextjs", etc.
77
- */
78
- function normalizeStack(raw) {
79
- if (!raw || typeof raw !== 'string') return null;
80
- const s = raw.toLowerCase();
81
- const hasDotnet = s.includes('dotnet') || s.includes('.net');
82
- const hasNext = s.includes('next') || s.includes('nextjs');
83
- if (hasDotnet && hasNext) return 'FULLSTACK';
84
- if (hasDotnet) return 'DOTNET';
85
- if (hasNext) return 'NEXTJS';
86
- return null;
87
- }
88
-
89
- /**
90
- * Parse "## Tech Stack" section from a README-style markdown file.
91
- * Returns normalized stack or null.
92
- */
93
- function parseReadmeStack(content) {
94
- if (!content) return null;
95
- // Find "## Tech Stack" section and read next ~10 lines
96
- const lines = content.split('\n');
97
- let inSection = false;
98
- const sectionLines = [];
99
- for (const line of lines) {
100
- if (/^##\s+tech\s+stack/i.test(line)) {
101
- inSection = true;
102
- continue;
103
- }
104
- if (inSection) {
105
- if (/^##/.test(line)) break; // next section
106
- sectionLines.push(line.toLowerCase());
107
- }
108
- }
109
- if (sectionLines.length === 0) return null;
110
- const text = sectionLines.join(' ');
111
- const hasDotnet = /dotnet|\.net|c#|asp\.net/.test(text);
112
- const hasNext = /next\.?js|nextjs/.test(text);
113
- if (hasDotnet && hasNext) return 'FULLSTACK';
114
- if (hasDotnet) return 'DOTNET';
115
- if (hasNext) return 'NEXTJS';
116
- return null;
117
- }
118
-
119
- /**
120
- * Build a startCommand from stack + paths.
121
- */
122
- function buildStartCommand(stack, frontendPath, backendPath) {
123
- if (stack === 'NEXTJS') {
124
- const base = frontendPath ? join(cwd, frontendPath) : cwd;
125
- // Check for custom dev script
126
- const pkgPath = join(base, 'package.json');
127
- const pkg = readJson(pkgPath);
128
- if (pkg?.scripts?.dev) {
129
- return frontendPath ? `cd ${frontendPath} && npm run dev` : 'npm run dev';
130
- }
131
- return frontendPath ? `cd ${frontendPath} && npm run dev` : 'npm run dev';
132
- }
133
- if (stack === 'DOTNET') {
134
- if (backendPath) {
135
- // Try to find .csproj inside backendPath
136
- const csprojFiles = findFiles(join(cwd, backendPath), (n) => n.endsWith('.csproj'), 2);
137
- if (csprojFiles.length > 0) {
138
- const rel = relative(cwd, csprojFiles[0]).replace(/\\/g, '/');
139
- return `dotnet run --project ${rel}`;
140
- }
141
- return `dotnet run --project ${backendPath}`;
142
- }
143
- // Find any .csproj
144
- const csprojFiles = findFiles(cwd, (n) => n.endsWith('.csproj'), 4);
145
- if (csprojFiles.length > 0) {
146
- const rel = relative(cwd, csprojFiles[0]).replace(/\\/g, '/');
147
- return `dotnet run --project ${rel}`;
148
- }
149
- return 'dotnet run';
150
- }
151
- if (stack === 'FULLSTACK') {
152
- // Prefer Next.js start for smoke testing
153
- const fe = frontendPath ? join(cwd, frontendPath) : cwd;
154
- return frontendPath ? `cd ${frontendPath} && npm run dev` : 'npm run dev';
155
- }
156
- return null;
157
- }
158
-
159
- // ─── Detection Strategy 1: .morph/config/config.json ─────────────────────────
160
-
161
- const configPath = join(cwd, '.morph', 'config', 'config.json');
162
- const config = readJson(configPath);
163
-
164
- if (config?.config?.project?.stack) {
165
- const stack = normalizeStack(config.config.project.stack);
166
- if (stack) {
167
- const frontendPath = config.config.project.frontendPath ?? null;
168
- const backendPath = config.config.project.backendPath ?? null;
169
- const startCommand = buildStartCommand(stack, frontendPath, backendPath);
170
- console.log(JSON.stringify({ stack, frontendPath, backendPath, startCommand }, null, 2));
171
- process.exit(0);
172
- }
173
- }
174
-
175
- // ─── Detection Strategy 2: .morph/context/README.md ──────────────────────────
176
-
177
- const readmePath = join(cwd, '.morph', 'context', 'README.md');
178
- const readmeContent = readText(readmePath);
179
- const readmeStack = parseReadmeStack(readmeContent);
180
-
181
- if (readmeStack) {
182
- const startCommand = buildStartCommand(readmeStack, null, null);
183
- console.log(JSON.stringify({
184
- stack: readmeStack,
185
- frontendPath: null,
186
- backendPath: null,
187
- startCommand,
188
- }, null, 2));
189
- process.exit(0);
190
- }
191
-
192
- // ─── Detection Strategy 3: File scan ─────────────────────────────────────────
193
-
194
- const csprojFiles = findFiles(cwd, (n) => n.endsWith('.csproj'), 4);
195
- const hasDotnet = csprojFiles.length > 0;
196
-
197
- // Find package.json files with "next" dependency (excluding node_modules already filtered)
198
- const packageJsonFiles = findFiles(cwd, (n) => n === 'package.json', 4);
199
- let hasNext = false;
200
- let nextPackageDir = null;
201
-
202
- for (const pkgFile of packageJsonFiles) {
203
- const pkg = readJson(pkgFile);
204
- if (pkg?.dependencies?.next || pkg?.devDependencies?.next) {
205
- hasNext = true;
206
- nextPackageDir = relative(cwd, dirname(pkgFile)).replace(/\\/g, '/') || null;
207
- break;
208
- }
209
- }
210
-
211
- let stack = 'UNKNOWN';
212
- let frontendPath = null;
213
- let backendPath = null;
214
-
215
- if (hasDotnet && hasNext) {
216
- stack = 'FULLSTACK';
217
- frontendPath = nextPackageDir;
218
- backendPath = csprojFiles.length > 0
219
- ? relative(cwd, dirname(csprojFiles[0])).replace(/\\/g, '/') || null
220
- : null;
221
- } else if (hasDotnet) {
222
- stack = 'DOTNET';
223
- backendPath = csprojFiles.length > 0
224
- ? relative(cwd, dirname(csprojFiles[0])).replace(/\\/g, '/') || null
225
- : null;
226
- } else if (hasNext) {
227
- stack = 'NEXTJS';
228
- frontendPath = nextPackageDir;
229
- }
230
-
231
- const startCommand = buildStartCommand(stack, frontendPath, backendPath);
232
-
233
- console.log(JSON.stringify({ stack, frontendPath, backendPath, startCommand }, null, 2));
234
- process.exit(0);
@@ -1,106 +0,0 @@
1
- ---
2
- name: morph-simulation-checklist
3
- description: Checklist for implementing mock/simulation clients for external .NET services (AI APIs, payments, email). Use when setting up simulation mode without real API access, when creating fake service implementations for development/testing, or when debugging external service integrations.
4
- user-invocable: true
5
- allowed-tools: Read, Write, Edit, Bash, Glob, Grep
6
- stacks:
7
- - dotnet
8
- ---
9
-
10
- # Simulation Checklist
11
-
12
- Checklist for simulating external services (AI, payment, email APIs) in .NET projects.
13
-
14
- ## Pre-Implementation
15
-
16
- ### 1. Map All Dependencies
17
-
18
- - [ ] List ALL interfaces in the flow (`grep -r "I{ServiceName}" src/ --include="*.cs" -l`)
19
- - [ ] Identify **transitive dependencies** (e.g., `ArtGenerationService` → `IReplicateClient` + `IImageDownloader`)
20
- - [ ] Document dependency trace: services to mock, transitive deps, required lifetimes
21
-
22
- ### 2. Verify Lifetimes
23
-
24
- | State | Lifetime | Example |
25
- |-------|----------|---------|
26
- | Stateful (Dictionary, List) | **Singleton** | `FakeReplicateClient` with prediction store |
27
- | Stateless | **Scoped** | `FakeEmailClient` that only logs |
28
-
29
- ```csharp
30
- // ❌ Stateful + Scoped = loses state between requests
31
- services.AddScoped<IReplicateClient, FakeReplicateClient>();
32
- // ✅ Stateful + Singleton
33
- services.AddSingleton<IReplicateClient, FakeReplicateClient>();
34
- ```
35
-
36
- ### 3. Read Complete Interfaces
37
-
38
- - [ ] Check full signatures — don't miss optional parameters (`attachments`, `CancellationToken`)
39
- - [ ] For AI mocks: verify current prompts, model in use, response format
40
- - [ ] For email mocks: check for attachments/inline images, log simulated sends
41
- - [ ] For payment mocks: implement state transitions (PENDING → CONFIRMED → REFUNDED), simulate webhooks
42
-
43
- ## Configuration Template
44
-
45
- ```json
46
- // appsettings.Development.json
47
- { "Simulation": { "Enabled": true, "ImageDelayMs": 500, "PlaceholderImageUrl": "https://picsum.photos/1024/1024" } }
48
- ```
49
-
50
- ```csharp
51
- if (configuration.GetValue<bool>("Simulation:Enabled"))
52
- services.AddSimulationClients(configuration); // Stateful→Singleton, Stateless→Scoped
53
- else
54
- services.AddProductionClients(configuration);
55
- ```
56
-
57
- ## MORPH-SPEC Integration
58
-
59
- When implementing simulation as a morph-spec task:
60
-
61
- ```bash
62
- # Track the simulation setup task
63
- morph-spec task start {feature} {task-id}
64
-
65
- # After implementation, validate the feature
66
- morph-spec validate-feature {feature}
67
-
68
- # Complete the task (triggers Tier-4 validators)
69
- morph-spec task done {feature} {task-id}
70
- ```
71
-
72
- Reference the project's service lifetime patterns:
73
- ```bash
74
- # Check existing DI patterns in the project
75
- grep -r "AddSingleton\|AddScoped\|AddTransient" src/ --include="*.cs" -l
76
- ```
77
-
78
- ## Post-Implementation
79
-
80
- - [ ] Validate DI: `dotnet build && dotnet run` + health check
81
- - [ ] Test full flow end-to-end (not just unit tests)
82
- - [ ] Verify simulation logs — confirm mocks are being called
83
- - [ ] Run `morph-spec validate-feature {feature}` to check architecture compliance
84
-
85
- ## Common Errors
86
-
87
- | Error | Cause | Fix |
88
- |-------|-------|-----|
89
- | Mock loses state between requests | Scoped lifetime for stateful service | Change to Singleton |
90
- | `Unable to resolve service` at runtime | Transitive dependency not registered | Map ALL dependencies |
91
- | Incorrect method signature | Didn't read full interface | Check optional params |
92
- | Mock returns wrong data | Prompts/model changed | Read current prompts |
93
- | Works in test, fails at runtime | Test doesn't cover full DI | Test with `dotnet run` |
94
-
95
- ## Checklist
96
-
97
- - [ ] All interfaces mapped (including transitive)
98
- - [ ] Lifetimes correct (stateful=Singleton, stateless=Scoped)
99
- - [ ] Complete interface signatures implemented
100
- - [ ] Simulation toggle via config
101
- - [ ] End-to-end flow tested in simulation mode
102
- - [ ] No real API calls in simulation mode
103
-
104
- ---
105
-
106
- *MORPH-SPEC Simulation Checklist*