@polymorphism-tech/morph-spec 4.8.19 → 4.10.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 (214) hide show
  1. package/CLAUDE.md +21 -0
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +44 -55
  4. package/bin/task-manager.js +133 -20
  5. package/bin/validate.js +67 -33
  6. package/claude-plugin.json +1 -1
  7. package/docs/CHEATSHEET.md +201 -203
  8. package/docs/QUICKSTART.md +2 -2
  9. package/framework/CLAUDE.md +99 -77
  10. package/framework/agents.json +734 -182
  11. package/framework/commands/commit.md +166 -0
  12. package/framework/commands/morph-apply.md +13 -2
  13. package/framework/commands/morph-archive.md +8 -2
  14. package/framework/commands/morph-infra.md +6 -0
  15. package/framework/commands/morph-preflight.md +6 -0
  16. package/framework/commands/morph-proposal.md +56 -7
  17. package/framework/commands/morph-status.md +6 -0
  18. package/framework/commands/morph-troubleshoot.md +6 -0
  19. package/framework/hooks/claude-code/notification/approval-reminder.js +3 -2
  20. package/framework/hooks/claude-code/post-tool-use/context-refresh.js +1 -1
  21. package/framework/hooks/claude-code/post-tool-use/dispatch.js +155 -32
  22. package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +78 -0
  23. package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +8 -17
  24. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +16 -3
  25. package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +4 -3
  26. package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +4 -3
  27. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +60 -0
  28. package/framework/hooks/claude-code/session-start/inject-morph-context.js +124 -2
  29. package/framework/hooks/claude-code/session-start/post-compact-restore.js +41 -0
  30. package/framework/hooks/claude-code/statusline.py +76 -30
  31. package/framework/hooks/claude-code/stop/validate-completion.js +2 -15
  32. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +23 -5
  33. package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +14 -6
  34. package/framework/hooks/shared/activity-logger.js +0 -24
  35. package/framework/hooks/shared/compact-restore.js +100 -0
  36. package/framework/hooks/shared/dispatch-helpers.js +116 -0
  37. package/framework/hooks/shared/phase-utils.js +12 -5
  38. package/framework/hooks/shared/skill-reminder-helpers.js +79 -0
  39. package/framework/hooks/shared/stale-task-reset.js +57 -0
  40. package/framework/hooks/shared/state-reader.js +29 -5
  41. package/framework/hooks/shared/worktree-helpers.js +53 -0
  42. package/framework/phases.json +69 -14
  43. package/framework/rules/morph-workflow.md +88 -86
  44. package/framework/skills/level-0-meta/mcp-registry.json +86 -51
  45. package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/SKILL.md +14 -17
  46. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
  47. package/framework/skills/level-0-meta/{code-review → morph-code-review}/SKILL.md +2 -2
  48. package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/SKILL.md +163 -163
  49. package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/SKILL.md +9 -9
  50. package/framework/skills/level-0-meta/morph-init/SKILL.md +77 -12
  51. package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/SKILL.md +62 -15
  52. package/framework/skills/level-0-meta/morph-replicate/SKILL.md +5 -5
  53. package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +1 -1
  54. package/framework/skills/level-0-meta/{simulation-checklist → morph-simulation-checklist}/SKILL.md +1 -1
  55. package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/SKILL.md +2 -2
  56. package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/SKILL.md +3 -4
  57. package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/references/tools-per-phase.md +7 -7
  58. package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/SKILL.md +2 -2
  59. package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/scripts/check-phase-outputs.mjs +2 -2
  60. package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +238 -0
  61. package/framework/skills/level-1-workflows/{phase-codebase-analysis → morph-phase-codebase-analysis}/SKILL.md +3 -3
  62. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +507 -0
  63. package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/SKILL.md +168 -27
  64. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +50 -0
  65. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +45 -0
  66. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +47 -0
  67. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +254 -0
  68. package/framework/skills/level-1-workflows/{phase-setup → morph-phase-setup}/SKILL.md +50 -3
  69. package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/SKILL.md +48 -11
  70. package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/scripts/validate-tasks.mjs +3 -3
  71. package/framework/skills/level-1-workflows/{phase-uiux → morph-phase-uiux}/SKILL.md +46 -11
  72. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +97 -0
  73. package/framework/standards/STANDARDS.json +640 -88
  74. package/framework/standards/infrastructure/vercel/vercel-database.md +106 -0
  75. package/framework/standards/integration/mcp/mcp-tools.md +25 -7
  76. package/framework/templates/REGISTRY.json +1825 -1909
  77. package/framework/templates/context/CONTEXT-FEATURE.md +276 -276
  78. package/framework/templates/docs/onboarding.md +3 -7
  79. package/package.json +2 -7
  80. package/src/commands/agents/dispatch-agents.js +104 -6
  81. package/src/commands/mcp/mcp-setup.js +39 -2
  82. package/src/commands/phase/phase-reset.js +74 -0
  83. package/src/commands/project/doctor.js +34 -51
  84. package/src/commands/project/init.js +1 -1
  85. package/src/commands/project/status.js +2 -2
  86. package/src/commands/project/update.js +381 -365
  87. package/src/commands/project/worktree.js +154 -0
  88. package/src/commands/scope/escalate.js +215 -0
  89. package/src/commands/state/advance-phase.js +132 -68
  90. package/src/commands/state/approve.js +2 -2
  91. package/src/commands/state/index.js +7 -8
  92. package/src/commands/state/phase-runner.js +1 -1
  93. package/src/commands/state/state.js +61 -6
  94. package/src/commands/task/expand.js +100 -0
  95. package/src/commands/tasks/task.js +78 -99
  96. package/src/commands/templates/template-render.js +93 -173
  97. package/src/commands/trust/trust.js +26 -21
  98. package/src/core/paths/output-schema.js +19 -3
  99. package/src/core/state/phase-state-machine.js +7 -4
  100. package/src/core/state/state-manager.js +32 -57
  101. package/src/core/workflows/workflow-detector.js +9 -87
  102. package/src/lib/detectors/claude-config-detector.js +93 -347
  103. package/src/lib/detectors/design-system-detector.js +189 -189
  104. package/src/lib/detectors/index.js +155 -57
  105. package/src/lib/generators/context-generator.js +2 -2
  106. package/src/lib/installers/mcp-installer.js +37 -5
  107. package/src/lib/phase-chain/phase-validator.js +336 -0
  108. package/src/lib/scope/impact-analyzer.js +106 -0
  109. package/src/lib/stack/stack-profile.js +88 -0
  110. package/src/lib/tasks/task-classifier.js +16 -0
  111. package/src/lib/tasks/task-parser.js +1 -1
  112. package/src/lib/tasks/test-runner.js +77 -0
  113. package/src/lib/trust/trust-manager.js +32 -144
  114. package/src/lib/validators/shared/emit-validator-dispatch.js +64 -0
  115. package/src/lib/validators/spec-validator.js +58 -4
  116. package/src/lib/validators/validation-runner.js +23 -11
  117. package/src/scripts/setup-infra.js +255 -224
  118. package/src/utils/agents-installer.js +34 -14
  119. package/src/utils/banner.js +1 -1
  120. package/src/utils/claude-settings-manager.js +1 -1
  121. package/src/utils/file-copier.js +1 -1
  122. package/src/utils/hooks-installer.js +272 -8
  123. package/framework/hooks/dev/check-sync-health.js +0 -117
  124. package/framework/hooks/dev/guard-version-numbers.js +0 -57
  125. package/framework/hooks/dev/sync-standards-registry.js +0 -60
  126. package/framework/hooks/dev/sync-template-registry.js +0 -60
  127. package/framework/hooks/dev/validate-skill-format.js +0 -70
  128. package/framework/hooks/dev/validate-standard-format.js +0 -73
  129. package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +0 -190
  130. package/framework/skills/level-1-workflows/phase-design/SKILL.md +0 -366
  131. package/framework/templates/meta-prompts/hops/hop-retry.md +0 -78
  132. package/framework/templates/meta-prompts/hops/hop-validation.md +0 -97
  133. package/framework/templates/meta-prompts/hops/hop-wrapper.md +0 -36
  134. package/framework/workflows/configs/design-impl.json +0 -49
  135. package/framework/workflows/configs/express.json +0 -45
  136. package/framework/workflows/configs/fast-track.json +0 -42
  137. package/framework/workflows/configs/full-morph.json +0 -79
  138. package/framework/workflows/configs/fusion.json +0 -39
  139. package/framework/workflows/configs/long-running.json +0 -33
  140. package/framework/workflows/configs/spec-only.json +0 -43
  141. package/framework/workflows/configs/ui-refresh.json +0 -49
  142. package/framework/workflows/configs/zero-touch.json +0 -82
  143. package/src/commands/project/index.js +0 -8
  144. package/src/commands/project/monitor.js +0 -295
  145. package/src/commands/project/tutorial.js +0 -115
  146. package/src/commands/state/validate-phase.js +0 -238
  147. package/src/commands/templates/generate-contracts.js +0 -445
  148. package/src/core/index.js +0 -10
  149. package/src/core/orchestrator.js +0 -171
  150. package/src/core/registry/command-registry.js +0 -28
  151. package/src/core/registry/index.js +0 -8
  152. package/src/core/registry/validator-registry.js +0 -204
  153. package/src/core/state/index.js +0 -8
  154. package/src/core/templates/index.js +0 -9
  155. package/src/core/templates/template-data-sources.js +0 -325
  156. package/src/core/templates/template-validator.js +0 -296
  157. package/src/core/workflows/index.js +0 -7
  158. package/src/generator/config-generator.js +0 -206
  159. package/src/generator/templates/config.json.template +0 -40
  160. package/src/generator/templates/project.md.template +0 -67
  161. package/src/lib/agents/micro-agent-factory.js +0 -161
  162. package/src/lib/analysis/complexity-analyzer.js +0 -441
  163. package/src/lib/analysis/index.js +0 -7
  164. package/src/lib/analytics/analytics-engine.js +0 -345
  165. package/src/lib/checkpoints/checkpoint-hooks.js +0 -298
  166. package/src/lib/checkpoints/index.js +0 -7
  167. package/src/lib/context/context-bundler.js +0 -241
  168. package/src/lib/context/context-optimizer.js +0 -212
  169. package/src/lib/context/context-tracker.js +0 -273
  170. package/src/lib/context/core-four-tracker.js +0 -201
  171. package/src/lib/context/mcp-optimizer.js +0 -200
  172. package/src/lib/detectors/config-detector.js +0 -223
  173. package/src/lib/detectors/standards-generator.js +0 -335
  174. package/src/lib/detectors/structure-detector.js +0 -275
  175. package/src/lib/execution/fusion-executor.js +0 -304
  176. package/src/lib/execution/parallel-executor.js +0 -270
  177. package/src/lib/hooks/stop-hook-executor.js +0 -286
  178. package/src/lib/hops/hop-composer.js +0 -221
  179. package/src/lib/monitor/agent-resolver.js +0 -144
  180. package/src/lib/monitor/renderer.js +0 -230
  181. package/src/lib/orchestration/index.js +0 -7
  182. package/src/lib/orchestration/team-orchestrator.js +0 -404
  183. package/src/lib/phase-chain/eligibility-checker.js +0 -243
  184. package/src/lib/threads/thread-coordinator.js +0 -238
  185. package/src/lib/threads/thread-manager.js +0 -317
  186. package/src/lib/tracking/artifact-trail.js +0 -202
  187. package/src/sanitizer/context-sanitizer.js +0 -221
  188. package/src/sanitizer/patterns.js +0 -163
  189. package/src/scanner/project-scanner.js +0 -242
  190. package/src/ui/diff-display.js +0 -91
  191. package/src/ui/interactive-wizard.js +0 -96
  192. package/src/ui/user-review.js +0 -211
  193. package/src/ui/wizard-questions.js +0 -188
  194. package/src/utils/color-utils.js +0 -70
  195. package/src/utils/process-handler.js +0 -97
  196. package/src/writer/file-writer.js +0 -86
  197. /package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/references/proposal-example.md +0 -0
  198. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-example.md +0 -0
  199. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-guidelines.md +0 -0
  200. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/scripts/scan-csharp.mjs +0 -0
  201. /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/references/review-example-nextjs.md +0 -0
  202. /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/scripts/scan-nextjs.mjs +0 -0
  203. /package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/scripts/scan-accessibility.mjs +0 -0
  204. /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-dev-server.mjs +0 -0
  205. /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-stack.mjs +0 -0
  206. /package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/scripts/set_title.sh +0 -0
  207. /package/framework/skills/level-1-workflows/{phase-clarify → morph-phase-clarify}/references/clarifications-example.md +0 -0
  208. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/architecture-analysis-guide.md +0 -0
  209. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-authoring-guide.md +0 -0
  210. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-example.md +0 -0
  211. /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/recap-example.md +0 -0
  212. /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/vsa-implementation-guide.md +0 -0
  213. /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/task-planning-patterns.md +0 -0
  214. /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/tasks-example.md +0 -0
@@ -1,445 +0,0 @@
1
- /**
2
- * MORPH-SPEC Generate Contracts Command
3
- *
4
- * Reads schema-analysis.md and generates contracts.cs with REAL field names and types.
5
- * Bridges the gap between schema analysis and contract template rendering (Problem 5 fix).
6
- *
7
- * Usage:
8
- * morph-spec generate contracts <feature>
9
- * morph-spec generate contracts <feature> --dry-run
10
- * morph-spec generate contracts <feature> --output <path>
11
- */
12
-
13
- import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs';
14
- import { join, dirname } from 'path';
15
- import chalk from 'chalk';
16
- import { logger } from '../../utils/logger.js';
17
- import { markOutput } from '../../core/state/state-manager.js';
18
-
19
- // ============================================================================
20
- // Type Mapping — SQL/TypeScript → C#
21
- // ============================================================================
22
-
23
- const TYPE_MAP = {
24
- // String
25
- 'varchar': 'string', 'text': 'string', 'character varying': 'string',
26
- 'char': 'string', 'nvarchar': 'string', 'ntext': 'string', 'string': 'string',
27
- 'character': 'string', 'name': 'string', 'citext': 'string',
28
- // UUID
29
- 'uuid': 'Guid', 'uniqueidentifier': 'Guid',
30
- // Integer
31
- 'int': 'int', 'integer': 'int', 'int4': 'int', 'number': 'int',
32
- 'bigint': 'long', 'int8': 'long', 'smallint': 'short', 'int2': 'short',
33
- 'serial': 'int', 'bigserial': 'long', 'oid': 'int',
34
- // Decimal
35
- 'decimal': 'decimal', 'numeric': 'decimal', 'money': 'decimal',
36
- 'float': 'double', 'double precision': 'double', 'real': 'float',
37
- 'float4': 'float', 'float8': 'double',
38
- // Boolean
39
- 'boolean': 'bool', 'bool': 'bool', 'bit': 'bool',
40
- // DateTime
41
- 'timestamp': 'DateTime', 'timestamp with time zone': 'DateTimeOffset',
42
- 'timestamp without time zone': 'DateTime', 'timestamptz': 'DateTimeOffset',
43
- 'datetime': 'DateTime', 'datetime2': 'DateTime', 'datetimeoffset': 'DateTimeOffset',
44
- 'date': 'DateOnly', 'time': 'TimeOnly', 'timetz': 'TimeOnly',
45
- // JSON
46
- 'jsonb': 'JsonElement', 'json': 'JsonElement',
47
- // Binary
48
- 'bytea': 'byte[]', 'binary': 'byte[]', 'varbinary': 'byte[]',
49
- };
50
-
51
- /**
52
- * Map a raw SQL/TS type string to a C# type.
53
- * Non-nullable value types get ? suffix when nullable is true.
54
- */
55
- function toCSharpType(rawType, nullable) {
56
- const lower = rawType.toLowerCase().trim();
57
- const csType = TYPE_MAP[lower] ?? 'string';
58
- const isReferenceType = csType === 'string' || csType === 'byte[]' || csType === 'JsonElement';
59
- const suffix = nullable && !isReferenceType ? '?' : '';
60
- return csType + suffix;
61
- }
62
-
63
- /**
64
- * Convert snake_case / kebab-case / space-separated to PascalCase.
65
- */
66
- function toPascalCase(str) {
67
- return str
68
- .split(/[_\s-]+/)
69
- .map(w => (w ? w[0].toUpperCase() + w.slice(1).toLowerCase() : ''))
70
- .join('');
71
- }
72
-
73
- /**
74
- * Derive a C# entity class name from a table name.
75
- * Strips a trailing plural 's' where safe (leads → Lead, users → User).
76
- */
77
- function toEntityName(tableName) {
78
- const pascal = toPascalCase(tableName);
79
- // Simple singularization: strip trailing s unless double-s, -us, -ss pattern
80
- if (pascal.endsWith('s') && !pascal.endsWith('ss') && !pascal.endsWith('us') && pascal.length > 3) {
81
- return pascal.slice(0, -1);
82
- }
83
- return pascal;
84
- }
85
-
86
- // ============================================================================
87
- // Schema-Analysis Parser
88
- // ============================================================================
89
-
90
- /**
91
- * Parse schema-analysis.md to extract table definitions with column metadata.
92
- *
93
- * Handles the standard MORPH-SPEC template output:
94
- * ### Table: <name>
95
- * **Columns:**
96
- * | Column Name | Type | Nullable | Default | Notes |
97
- * |------------|------|----------|---------|-------|
98
- * | id | uuid | NO | gen..() | PK |
99
- *
100
- * @param {string} content - Raw file content of schema-analysis.md
101
- * @returns {Array<{name: string, columns: Array<{name, type, nullable, notes}>}>}
102
- */
103
- export function parseSchemaAnalysisMd(content) {
104
- const tables = [];
105
-
106
- // Locate all "### Table:" headings and slice between them
107
- const headingRe = /^### Table:/gm;
108
- const positions = [];
109
- let m;
110
- while ((m = headingRe.exec(content)) !== null) {
111
- positions.push(m.index);
112
- }
113
- if (positions.length === 0) return tables;
114
-
115
- const sections = positions.map((pos, i) => {
116
- const end = positions[i + 1] ?? content.length;
117
- return content.slice(pos, end);
118
- });
119
-
120
- for (const section of sections) {
121
- const nameMatch = section.match(/^### Table:\s*(\S+)/);
122
- if (!nameMatch) continue;
123
- const tableName = nameMatch[1].replace(/[*`]/g, ''); // strip markdown decoration (preserve underscores)
124
-
125
- // Find **Columns:** subsection
126
- const colIdx = section.indexOf('**Columns:**');
127
- if (colIdx === -1) continue;
128
-
129
- const afterColumns = section.slice(colIdx + '**Columns:**'.length);
130
- const columns = [];
131
- let headerSeen = false;
132
-
133
- for (const line of afterColumns.split('\n')) {
134
- const trimmed = line.trim();
135
-
136
- // Stop at next sub-section (**Relationships:**, **Indexes:**, etc.)
137
- if (trimmed.startsWith('**') && trimmed.endsWith('**')) break;
138
- if (!trimmed.startsWith('|')) continue;
139
-
140
- // Split markdown table row into cells
141
- const cells = trimmed.split('|').slice(1, -1).map(c => c.trim());
142
- if (cells.length < 2) continue;
143
-
144
- // Skip header row
145
- if (!headerSeen && cells[0] === 'Column Name') {
146
- headerSeen = true;
147
- continue;
148
- }
149
-
150
- // Skip separator rows (---|---| etc.)
151
- if (cells[0].match(/^-+:?$/) || cells[0].match(/^:?-+$/)) continue;
152
-
153
- if (cells[0]) {
154
- columns.push({
155
- name: cells[0],
156
- type: cells[1] || 'string',
157
- nullable: (cells[2] || 'NO').toUpperCase() === 'YES',
158
- default: cells[3] && cells[3] !== '-' ? cells[3] : null,
159
- notes: cells[4] || '',
160
- });
161
- }
162
- }
163
-
164
- if (columns.length > 0) {
165
- tables.push({ name: tableName, columns });
166
- }
167
- }
168
-
169
- return tables;
170
- }
171
-
172
- // ============================================================================
173
- // Contract Generator
174
- // ============================================================================
175
-
176
- /**
177
- * Determine whether a column is a primary key or auto-managed field
178
- * (should be excluded from Create/Update requests).
179
- */
180
- function isAutoManagedField(col) {
181
- const name = col.name.toLowerCase();
182
- const notes = col.notes.toLowerCase();
183
- return (
184
- notes.includes('pk') ||
185
- name === 'id' ||
186
- name === 'created_at' || name === 'createdat' ||
187
- name === 'updated_at' || name === 'updatedat' ||
188
- notes.includes('auto') || notes.includes('generated') || notes.includes('computed')
189
- );
190
- }
191
-
192
- /**
193
- * Generate C# contracts.cs content from parsed table definitions.
194
- *
195
- * Produces:
196
- * - One Dto record per table (all columns)
197
- * - One CreateRequest record per table (non-auto fields, required)
198
- * - One UpdateRequest record per table (non-auto fields, nullable/patch)
199
- * - Service interface for the primary table
200
- * - NotFoundException for the primary entity
201
- *
202
- * @param {Array} tables - Parsed table definitions
203
- * @param {string} featureName - Feature name (used for namespace/class names)
204
- * @param {string} namespace - C# namespace
205
- * @returns {string} Generated C# source
206
- */
207
- function generateContracts(tables, featureName, namespace) {
208
- const entityName = toPascalCase(featureName);
209
- const today = new Date().toISOString().split('T')[0];
210
-
211
- const lines = [
212
- `// ============================================================`,
213
- `// CONTRACTS: ${entityName} — Generated from real schema-analysis.md`,
214
- `// Generated by MORPH-SPEC generate contracts | Date: ${today}`,
215
- `// Source: .morph/features/${featureName}/1-design/schema-analysis.md`,
216
- `// Review: rename types, add validation attributes, adjust access modifiers.`,
217
- `// ============================================================`,
218
- ``,
219
- `using System;`,
220
- `using System.Text.Json;`,
221
- `using System.Collections.Generic;`,
222
- `using System.Threading;`,
223
- `using System.Threading.Tasks;`,
224
- ``,
225
- `namespace ${namespace}.Application.Features.${entityName};`,
226
- ``,
227
- ];
228
-
229
- for (const table of tables) {
230
- const dtoPascal = toEntityName(table.name);
231
- const userFields = table.columns.filter(c => !isAutoManagedField(c));
232
-
233
- lines.push(`#region ${dtoPascal}`, ``);
234
-
235
- // ── DTO ──────────────────────────────────────────────────────────────────
236
- lines.push(`/// <summary>Read DTO — maps to the "${table.name}" table.</summary>`);
237
- lines.push(`public record ${dtoPascal}Dto(`);
238
- table.columns.forEach((col, i) => {
239
- const csType = toCSharpType(col.type, col.nullable);
240
- const propName = toPascalCase(col.name);
241
- const comment = col.notes && col.notes !== '-' ? ` // ${table.name}.${col.name} (${col.type})` : '';
242
- const comma = i < table.columns.length - 1 ? ',' : '';
243
- lines.push(` ${csType} ${propName}${comma}${comment}`);
244
- });
245
- lines.push(`);`, ``);
246
-
247
- // ── Create Request ────────────────────────────────────────────────────────
248
- lines.push(`/// <summary>Command to create a new ${dtoPascal}.</summary>`);
249
- lines.push(`public record Create${dtoPascal}Request(`);
250
- if (userFields.length > 0) {
251
- userFields.forEach((col, i) => {
252
- const csType = toCSharpType(col.type, false); // Required for create
253
- const propName = toPascalCase(col.name);
254
- const comma = i < userFields.length - 1 ? ',' : '';
255
- lines.push(` ${csType} ${propName}${comma}`);
256
- });
257
- } else {
258
- lines.push(` // TODO: Add required fields — all columns are auto-managed`);
259
- }
260
- lines.push(`);`, ``);
261
-
262
- // ── Update Request ────────────────────────────────────────────────────────
263
- lines.push(`/// <summary>Command to update an existing ${dtoPascal} (patch semantics).</summary>`);
264
- lines.push(`public record Update${dtoPascal}Request(`);
265
- if (userFields.length > 0) {
266
- userFields.forEach((col, i) => {
267
- const csType = toCSharpType(col.type, true); // All nullable for patch
268
- const propName = toPascalCase(col.name);
269
- const comma = i < userFields.length - 1 ? ',' : '';
270
- lines.push(` ${csType} ${propName}${comma}`);
271
- });
272
- } else {
273
- lines.push(` // TODO: Add updatable fields`);
274
- }
275
- lines.push(`);`, ``);
276
- lines.push(`#endregion`, ``);
277
- }
278
-
279
- // ── Service Interface for primary entity ──────────────────────────────────
280
- const primaryTable = tables[0];
281
- const primaryEntity = primaryTable ? toEntityName(primaryTable.name) : entityName;
282
-
283
- lines.push(
284
- `#region Service Interface`,
285
- ``,
286
- `public interface I${entityName}Service`,
287
- `{`,
288
- ` Task<${primaryEntity}Dto?> GetByIdAsync(Guid id, CancellationToken ct = default);`,
289
- ` Task<List<${primaryEntity}Dto>> GetAllAsync(CancellationToken ct = default);`,
290
- ` Task<${primaryEntity}Dto> CreateAsync(Create${primaryEntity}Request request, CancellationToken ct = default);`,
291
- ` Task UpdateAsync(Guid id, Update${primaryEntity}Request request, CancellationToken ct = default);`,
292
- ` Task DeleteAsync(Guid id, CancellationToken ct = default);`,
293
- `}`,
294
- ``,
295
- `#endregion`,
296
- ``,
297
- );
298
-
299
- // ── Exception ─────────────────────────────────────────────────────────────
300
- lines.push(
301
- `#region Exceptions`,
302
- ``,
303
- `public class ${primaryEntity}NotFoundException(Guid id)`,
304
- ` : Exception($"${primaryEntity} '{{id}}' not found.");`,
305
- ``,
306
- `#endregion`,
307
- );
308
-
309
- return lines.join('\n') + '\n';
310
- }
311
-
312
- // ============================================================================
313
- // Namespace Detection
314
- // ============================================================================
315
-
316
- /**
317
- * Attempt to derive the C# namespace from project config or .csproj file.
318
- */
319
- async function detectNamespace(projectPath) {
320
- const configPath = join(projectPath, '.morph/config/config.json');
321
- try {
322
- const config = JSON.parse(readFileSync(configPath, 'utf-8'));
323
- if (config.project?.namespace) return config.project.namespace;
324
- if (config.project?.name) {
325
- return config.project.name
326
- .split(/[\s\-_]+/)
327
- .map(w => (w ? w[0].toUpperCase() + w.slice(1) : ''))
328
- .join('');
329
- }
330
- } catch { /* ignore */ }
331
-
332
- // Try to find a .csproj via git ls-files
333
- try {
334
- const { execSync } = await import('child_process');
335
- const csprojList = execSync('git ls-files "*.csproj"', {
336
- cwd: projectPath,
337
- stdio: ['pipe', 'pipe', 'pipe'],
338
- encoding: 'utf-8',
339
- }).trim().split('\n').filter(Boolean);
340
- if (csprojList.length > 0) {
341
- const name = csprojList[0].split('/').pop().replace('.csproj', '');
342
- return name.replace(/\.|-/g, '');
343
- }
344
- } catch { /* not a git repo or no .csproj */ }
345
-
346
- return 'YourProject';
347
- }
348
-
349
- // ============================================================================
350
- // CLI Entry Point
351
- // ============================================================================
352
-
353
- /**
354
- * Main handler for: morph-spec generate contracts <feature>
355
- *
356
- * @param {string} featureName - Feature slug (e.g. "email-marketing")
357
- * @param {Object} options
358
- * @param {boolean} options.dryRun - Print to stdout instead of writing
359
- * @param {string} options.output - Override output path
360
- */
361
- export async function generateContractsCommand(featureName, options = {}) {
362
- logger.header(`Generate Contracts: ${featureName}`);
363
- logger.blank();
364
-
365
- const projectPath = options.projectPath || process.cwd();
366
-
367
- // Locate schema-analysis.md
368
- const schemaPath = join(projectPath, `.morph/features/${featureName}/1-design/schema-analysis.md`);
369
- if (!existsSync(schemaPath)) {
370
- logger.error(`schema-analysis.md not found at: ${schemaPath}`);
371
- logger.dim(' Run the Design phase schema analysis first:');
372
- logger.dim(' npx morph-spec phase advance ' + featureName);
373
- logger.dim(' then execute /phase-codebase-analysis in Claude Code');
374
- process.exit(1);
375
- }
376
-
377
- logger.dim(`Reading: ${schemaPath}`);
378
- const content = readFileSync(schemaPath, 'utf-8');
379
-
380
- // Parse tables
381
- const tables = parseSchemaAnalysisMd(content);
382
-
383
- if (tables.length === 0) {
384
- logger.error('No tables found in schema-analysis.md');
385
- logger.blank();
386
- logger.dim(' Ensure the file uses the standard MORPH-SPEC format:');
387
- logger.dim(' ### Table: your_table_name');
388
- logger.dim(' **Columns:**');
389
- logger.dim(' | Column Name | Type | Nullable | Default | Notes |');
390
- logger.dim(' |------------|------|----------|---------|-------|');
391
- logger.dim(' | id | uuid | NO | - | PK |');
392
- process.exit(1);
393
- }
394
-
395
- logger.info(`Tables found: ${chalk.cyan(tables.map(t => t.name).join(', '))}`);
396
- tables.forEach(t => {
397
- const colSummary = t.columns
398
- .slice(0, 4)
399
- .map(c => `${c.name}:${c.type}`)
400
- .join(', ');
401
- const more = t.columns.length > 4 ? ` +${t.columns.length - 4} more` : '';
402
- logger.dim(` ${t.name}: ${colSummary}${more}`);
403
- });
404
- logger.blank();
405
-
406
- // Detect namespace
407
- const namespace = await detectNamespace(projectPath);
408
- logger.dim(`Namespace: ${namespace}`);
409
- logger.blank();
410
-
411
- // Generate content
412
- const contractsContent = generateContracts(tables, featureName, namespace);
413
-
414
- // Output path
415
- const outputPath = options.output
416
- || join(projectPath, `.morph/features/${featureName}/1-design/contracts.cs`);
417
-
418
- if (options.dryRun) {
419
- logger.info(chalk.cyan('📋 DRY RUN — Preview (nothing written):'));
420
- logger.blank();
421
- console.log(contractsContent);
422
- logger.dim(`Would be written to: ${outputPath}`);
423
- return;
424
- }
425
-
426
- mkdirSync(dirname(outputPath), { recursive: true });
427
- writeFileSync(outputPath, contractsContent, 'utf-8');
428
-
429
- logger.success('✅ contracts.cs generated from real schema');
430
- logger.dim(` Output: ${chalk.cyan(outputPath)}`);
431
- const totalCols = tables.reduce((n, t) => n + t.columns.length, 0);
432
- logger.dim(` ${tables.length} table(s) → ${totalCols} columns mapped`);
433
- logger.blank();
434
-
435
- // Mark output in state (non-blocking)
436
- try {
437
- markOutput(featureName, 'contracts');
438
- logger.dim(' State: contracts output marked ✓');
439
- } catch {
440
- // Feature might not be in state yet — non-blocking
441
- }
442
-
443
- logger.blank();
444
- logger.dim('Next step: review contracts.cs for type accuracy, then proceed with /phase-design');
445
- }
package/src/core/index.js DELETED
@@ -1,10 +0,0 @@
1
- /**
2
- * Core Framework Logic
3
- *
4
- * Central hub for state management, templates, and workflows.
5
- * Separated from shared libraries (lib/) for clear architectural boundaries.
6
- */
7
-
8
- export * from './state/index.js';
9
- export * from './templates/index.js';
10
- export * from './workflows/index.js';
@@ -1,171 +0,0 @@
1
- /**
2
- * @fileoverview AutoContextOrchestrator - Main orchestrator for CLI auto-detection
3
- * @module morph-spec/orchestrator
4
- */
5
-
6
- import chalk from 'chalk';
7
- import { ProjectScanner } from '../scanner/project-scanner.js';
8
- import { ContextSanitizer } from '../sanitizer/context-sanitizer.js';
9
- import { ConfigGenerator } from '../generator/config-generator.js';
10
- import { UserReview } from '../ui/user-review.js';
11
- import { InteractiveWizard } from '../ui/interactive-wizard.js';
12
- import { FileWriter } from '../writer/file-writer.js';
13
- import { readFile, access } from 'fs/promises';
14
- import { join } from 'path';
15
-
16
- /**
17
- * @typedef {import('../types/index.js').GeneratedConfigs} GeneratedConfigs
18
- */
19
-
20
- /**
21
- * AutoContextOrchestrator - Orchestrates the complete auto-detection flow
22
- * @class
23
- */
24
- export class AutoContextOrchestrator {
25
- constructor() {
26
- this.scanner = new ProjectScanner();
27
- this.sanitizer = new ContextSanitizer();
28
- this.configGenerator = new ConfigGenerator();
29
- this.userReview = new UserReview();
30
- this.wizard = new InteractiveWizard();
31
- this.fileWriter = new FileWriter();
32
-
33
- // Handle Ctrl+C gracefully
34
- this.setupSignalHandlers();
35
- }
36
-
37
- /**
38
- * Execute the complete auto-detection flow
39
- * @param {string} cwd - Current working directory
40
- * @param {Object} [options] - Orchestration options
41
- * @param {boolean} [options.skipReview] - Skip user review (auto-approve)
42
- * @returns {Promise<{success: boolean, configs: GeneratedConfigs|null}>}
43
- */
44
- async execute(cwd, options = {}) {
45
- const {
46
- skipReview = false,
47
- } = options;
48
-
49
- try {
50
- console.log(chalk.bold.cyan('\n🔍 MORPH-SPEC Auto Context Detection\n'));
51
-
52
- let projectConfig;
53
-
54
- // Use interactive wizard (LLM invocation not implemented; wizard provides all context)
55
- console.log(chalk.yellow(' Using interactive wizard mode\n'));
56
- projectConfig = await this.wizard.run();
57
-
58
- // Step 2: Generate configs
59
- console.log(chalk.dim(' [4/6] Generating configuration files...'));
60
- const configs = await this.configGenerator.generate(projectConfig);
61
-
62
- // Step 3: User review (unless skipped)
63
- let finalConfigs = configs;
64
-
65
- if (!skipReview) {
66
- console.log(chalk.dim(' [5/6] Requesting user approval...'));
67
-
68
- // Check if updating existing configs
69
- const existingConfigs = await this.readExistingConfigs(cwd);
70
-
71
- const approvalResponse = await this.userReview.promptForApproval(
72
- configs,
73
- projectConfig,
74
- existingConfigs
75
- );
76
-
77
- if (approvalResponse.action === 'cancel') {
78
- console.log(chalk.yellow('\n❌ Operation canceled by user\n'));
79
- console.log(chalk.dim(` Reason: ${approvalResponse.cancelReason || 'User canceled'}\n`));
80
- return { success: false, configs: null };
81
- }
82
-
83
- if (approvalResponse.editedConfigs) {
84
- finalConfigs = approvalResponse.editedConfigs;
85
- }
86
- } else {
87
- console.log(chalk.dim(' [5/6] Skipping user review (auto-approve)'));
88
- }
89
-
90
- // Step 4: Save configs
91
- console.log(chalk.dim(' [6/6] Saving configuration files...'));
92
-
93
- // Backup existing configs if they exist
94
- await this.configGenerator.backupExisting(cwd);
95
-
96
- // Write new configs
97
- await this.fileWriter.save(cwd, finalConfigs);
98
-
99
- console.log(chalk.bold.green('🎉 Auto-detection complete!\n'));
100
-
101
- return { success: true, configs: finalConfigs };
102
- } catch (error) {
103
- console.log(chalk.bold.red('\n❌ Auto-detection failed\n'));
104
- console.log(chalk.red(` Error: ${error.message}\n`));
105
-
106
- if (error.stack && process.env.DEBUG) {
107
- console.log(chalk.dim(error.stack));
108
- }
109
-
110
- return { success: false, configs: null };
111
- }
112
- }
113
-
114
- /**
115
- * Read existing configs (if they exist)
116
- * @param {string} cwd - Current working directory
117
- * @returns {Promise<Object|null>} Existing configs or null
118
- */
119
- async readExistingConfigs(cwd) {
120
- try {
121
- const projectMdPath = join(cwd, '.morph', 'project.md');
122
- const configJsonPath = join(cwd, '.morph', 'config', 'config.json');
123
-
124
- const [projectMdExists, configJsonExists] = await Promise.all([
125
- this.fileExists(projectMdPath),
126
- this.fileExists(configJsonPath)
127
- ]);
128
-
129
- if (!projectMdExists && !configJsonExists) {
130
- return null; // No existing configs
131
- }
132
-
133
- const [projectMd, configJson] = await Promise.all([
134
- projectMdExists ? readFile(projectMdPath, 'utf-8') : null,
135
- configJsonExists ? readFile(configJsonPath, 'utf-8') : null
136
- ]);
137
-
138
- return { projectMd, configJson };
139
- } catch (error) {
140
- return null; // Error reading configs, treat as non-existent
141
- }
142
- }
143
-
144
- /**
145
- * Check if file exists
146
- * @param {string} filepath - File path
147
- * @returns {Promise<boolean>}
148
- */
149
- async fileExists(filepath) {
150
- try {
151
- await access(filepath);
152
- return true;
153
- } catch {
154
- return false;
155
- }
156
- }
157
-
158
- /**
159
- * Setup signal handlers for graceful shutdown
160
- */
161
- setupSignalHandlers() {
162
- const handleExit = () => {
163
- console.log(chalk.yellow('\n\n⚠️ Operation interrupted by user (Ctrl+C)\n'));
164
- console.log(chalk.dim(' No files were modified\n'));
165
- process.exit(0);
166
- };
167
-
168
- process.on('SIGINT', handleExit);
169
- process.on('SIGTERM', handleExit);
170
- }
171
- }
@@ -1,28 +0,0 @@
1
- /**
2
- * Command Registry — DEPRECATED
3
- *
4
- * This module is no longer used. CLI commands are registered directly
5
- * in bin/morph-spec.js via Commander.js. This file is kept as an empty
6
- * stub to prevent import errors from any remaining references.
7
- *
8
- * @module command-registry
9
- * @deprecated
10
- */
11
-
12
- export const commandMetadata = {};
13
-
14
- export async function loadCommand(commandName) {
15
- throw new Error(`Command registry is deprecated. Use bin/morph-spec.js directly.`);
16
- }
17
-
18
- export function getAllCommandNames() {
19
- return [];
20
- }
21
-
22
- export function getCommandsByCategory() {
23
- return [];
24
- }
25
-
26
- export function getAllCategories() {
27
- return [];
28
- }
@@ -1,8 +0,0 @@
1
- /**
2
- * Core Registry System
3
- *
4
- * Auto-discovery for commands and validators.
5
- */
6
-
7
- export * from './command-registry.js';
8
- export * from './validator-registry.js';