@polymorphism-tech/morph-spec 7.2.0 → 7.4.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 (74) hide show
  1. package/CLAUDE.md +1 -1
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +4 -2
  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 +2 -2
  8. package/framework/MORPH.md +29 -12
  9. package/framework/agents.json +132 -132
  10. package/framework/commands/morph-apply.md +1 -1
  11. package/framework/commands/morph-proposal.md +1 -1
  12. package/framework/evals/rubrics/README.md +9 -8
  13. package/framework/hooks/README.md +3 -1
  14. package/framework/hooks/claude-code/core/gate-guard.js +160 -0
  15. package/framework/hooks/claude-code/core/state-sync.js +15 -3
  16. package/framework/hooks/claude-code/core/validate-completion.js +241 -136
  17. package/framework/hooks/shared/state-reader.js +236 -221
  18. package/framework/phases.json +1 -1
  19. package/framework/skills/README.md +2 -2
  20. package/framework/skills/core/morph-apply/SKILL.md +63 -21
  21. package/framework/skills/core/morph-brainstorming/SKILL.md +5 -1
  22. package/framework/skills/core/morph-eval/SKILL.md +4 -5
  23. package/framework/skills/core/morph-implement/SKILL.md +47 -19
  24. package/framework/skills/core/morph-plan/SKILL.md +8 -2
  25. package/framework/skills/core/morph-proposal/SKILL.md +1 -1
  26. package/framework/skills/core/morph-review/SKILL.md +13 -6
  27. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  28. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  29. package/framework/standards/ai-agents/setup.md +258 -244
  30. package/framework/templates/README.md +708 -709
  31. package/framework/templates/REGISTRY.json +1 -20
  32. package/framework/templates/context/CONTEXT-FEATURE.md +276 -276
  33. package/framework/templates/docs/proposal.md +171 -171
  34. package/framework/templates/feature/recap.md +145 -145
  35. package/package.json +1 -1
  36. package/src/commands/project/doctor.js +941 -946
  37. package/src/commands/project/init.js +517 -515
  38. package/src/commands/project/status.js +373 -376
  39. package/src/commands/state/approve.js +251 -213
  40. package/src/commands/validation/validate-feature.js +356 -287
  41. package/src/core/state/state-manager.js +910 -934
  42. package/src/dashboard/app/App.jsx +29 -31
  43. package/src/dashboard/app/components/Sidebar.jsx +44 -45
  44. package/src/dashboard/scanner/index.js +20 -21
  45. package/src/lib/context/context-builder.js +3 -3
  46. package/src/lib/generators/context-generator.js +2 -3
  47. package/src/lib/generators/mandate-generator.js +48 -92
  48. package/src/lib/generators/metadata-extractor.js +385 -386
  49. package/src/lib/generators/recap-generator.js +285 -285
  50. package/src/lib/risk-detector.js +2 -2
  51. package/src/lib/validators/content/content-validator.js +89 -286
  52. package/src/lib/validators/content/index.js +0 -1
  53. package/src/utils/hooks-installer.js +527 -516
  54. package/framework/templates/feature/tasks.md +0 -199
  55. package/framework/workflows/README.md +0 -1041
  56. package/framework/workflows/configs/fast-track.json +0 -46
  57. package/framework/workflows/configs/full.json +0 -64
  58. package/framework/workflows/configs/quick.json +0 -32
  59. package/framework/workflows/configs/standard.json +0 -57
  60. package/framework/workflows/docs/STORY-DRIVEN-DEVELOPMENT.md +0 -392
  61. package/framework/workflows/docs/design-impl.md +0 -37
  62. package/framework/workflows/docs/enforcement-pipeline.md +0 -668
  63. package/framework/workflows/docs/fast-track.md +0 -29
  64. package/framework/workflows/docs/full-morph.md +0 -69
  65. package/framework/workflows/docs/standard.md +0 -49
  66. package/framework/workflows/docs/ui-refresh.md +0 -39
  67. package/src/commands/state/index.js +0 -7
  68. package/src/core/workflows/workflow-detector.js +0 -586
  69. package/src/core/workflows/workflow-router.js +0 -204
  70. package/src/dashboard/app/pages/Workflows.jsx +0 -346
  71. package/src/dashboard/scanner/workflows-scanner.js +0 -18
  72. package/src/lib/phase-chain/phase-validator.js +0 -359
  73. package/src/lib/validators/content/cross-phase-validator.js +0 -281
  74. package/src/lib/validators/spec-validator.js +0 -312
@@ -1,312 +0,0 @@
1
- /**
2
- * Spec Validator
3
- *
4
- * Validates spec.md and contracts.cs at design time (Phase 2) to catch
5
- * architectural and coding standard violations early.
6
- *
7
- * MORPH-SPEC 3.0 - Phase 4: Spec-Time Validation
8
- */
9
-
10
- import { readFileSync, existsSync } from 'fs';
11
- import { join } from 'path';
12
- import { getAbsoluteOutputPath, resolveContractsOutputType } from '../../core/paths/output-schema.js';
13
- import { getStackProfile } from '../stack/stack-profile.js';
14
-
15
- /**
16
- * Validate spec.md and contracts.cs at design time
17
- *
18
- * @param {string} projectPath - Project root path
19
- * @param {string} featureName - Feature name
20
- * @param {Object} options - Options
21
- * @returns {Object} { status, errors, warnings, issues }
22
- */
23
- export async function validateSpec(projectPath, featureName, options = {}) {
24
- const { stack, isNextjs } = getStackProfile(projectPath);
25
- const contractsType = resolveContractsOutputType(stack);
26
-
27
- const specPath = getAbsoluteOutputPath(projectPath, featureName, 'spec');
28
- const contractsPath = getAbsoluteOutputPath(projectPath, featureName, contractsType);
29
-
30
- const issues = [];
31
-
32
- // Validate spec.md
33
- if (existsSync(specPath)) {
34
- const specContent = readFileSync(specPath, 'utf8');
35
- const specIssues = validateSpecMd(specContent);
36
- issues.push(...specIssues);
37
- }
38
-
39
- // Validate contracts file (stack-aware)
40
- if (existsSync(contractsPath)) {
41
- const contractsContent = readFileSync(contractsPath, 'utf8');
42
- const contractsIssues = isNextjs
43
- ? validateContractsTs(contractsContent)
44
- : validateContractsCs(contractsContent);
45
- issues.push(...contractsIssues);
46
- }
47
-
48
- const errors = issues.filter(i => i.level === 'error').length;
49
- const warnings = issues.filter(i => i.level === 'warning').length;
50
-
51
- return {
52
- status: errors > 0 ? 'error' : (warnings > 0 ? 'warning' : 'ok'),
53
- errors,
54
- warnings,
55
- issues,
56
- passed: errors === 0
57
- };
58
- }
59
-
60
- /**
61
- * Validate spec.md structure and content
62
- */
63
- function validateSpecMd(content) {
64
- const issues = [];
65
-
66
- // Required sections
67
- const requiredSections = [
68
- { name: 'Overview', pattern: /^##\s+(?:1\.\s+)?Overview/mi },
69
- { name: 'Functional Requirements', pattern: /^##\s+(?:\d+\.\s+)?Functional Requirements/mi },
70
- { name: 'Non-Functional Requirements', pattern: /^##\s+(?:\d+\.\s+)?Non-Functional Requirements/mi },
71
- { name: 'Technical Architecture', pattern: /^##\s+(?:\d+\.\s+)?Technical Architecture/mi },
72
- { name: 'Data Model', pattern: /^##\s+(?:\d+\.\s+)?Data Model/mi }
73
- ];
74
-
75
- for (const section of requiredSections) {
76
- if (!section.pattern.test(content)) {
77
- issues.push({
78
- level: 'warning',
79
- type: 'spec',
80
- message: `Missing recommended section: "${section.name}"`,
81
- solution: `Add ## ${section.name} section to spec.md`
82
- });
83
- }
84
- }
85
-
86
- // Check for manual infrastructure creation (anti-pattern)
87
- const manualInfraPatterns = [
88
- /create.*manually/i,
89
- /portal.*azure/i,
90
- /click.*portal/i,
91
- /via.*portal/i,
92
- /manual.*setup/i
93
- ];
94
-
95
- for (const pattern of manualInfraPatterns) {
96
- if (pattern.test(content)) {
97
- issues.push({
98
- level: 'error',
99
- type: 'spec',
100
- message: 'Spec references manual Azure portal creation (anti-pattern)',
101
- solution: 'Use Infrastructure as Code (Bicep) instead of manual portal operations'
102
- });
103
- break; // One error is enough
104
- }
105
- }
106
-
107
- // Check for Bicep reference (best practice)
108
- const hasBicep = /bicep/i.test(content) || /infrastructure\s+as\s+code/i.test(content) || /\.bicep/i.test(content);
109
- if (!hasBicep && /azure|sql|storage|container/i.test(content)) {
110
- issues.push({
111
- level: 'warning',
112
- type: 'spec',
113
- message: 'Spec mentions Azure resources but no Bicep/IaC reference found',
114
- solution: 'Add Infrastructure section with Bicep module references'
115
- });
116
- }
117
-
118
- return issues;
119
- }
120
-
121
- /**
122
- * Validate contracts.cs coding standards
123
- */
124
- function validateContractsCs(content) {
125
- const issues = [];
126
-
127
- // 1. Interface naming: Must start with 'I' and be PascalCase
128
- const interfaceRegex = /public\s+interface\s+(\w+)/g;
129
- let match;
130
-
131
- while ((match = interfaceRegex.exec(content)) !== null) {
132
- const name = match[1];
133
-
134
- if (!name.startsWith('I')) {
135
- issues.push({
136
- level: 'error',
137
- type: 'contracts',
138
- message: `Interface '${name}' must start with 'I' (e.g., I${name})`,
139
- solution: `Rename to I${name}`
140
- });
141
- } else if (!isPascalCase(name)) {
142
- issues.push({
143
- level: 'error',
144
- type: 'contracts',
145
- message: `Interface '${name}' must use PascalCase`,
146
- solution: `Rename to ${toPascalCase(name)}`
147
- });
148
- }
149
- }
150
-
151
- // 2. Async methods must have CancellationToken parameter
152
- const asyncMethodRegex = /Task(?:<[^>]+>)?\s+(\w+Async)\s*\(([^)]*)\)/g;
153
- while ((match = asyncMethodRegex.exec(content)) !== null) {
154
- const methodName = match[1];
155
- const params = match[2];
156
-
157
- if (!params.includes('CancellationToken')) {
158
- issues.push({
159
- level: 'error',
160
- type: 'contracts',
161
- message: `Async method '${methodName}' missing CancellationToken parameter`,
162
- solution: `Add 'CancellationToken ct = default' parameter to ${methodName}`
163
- });
164
- }
165
- }
166
-
167
- // 3. DTOs should use records (not classes)
168
- const classRegex = /public\s+class\s+(\w+(?:Dto|Response|Request|Command|Query))/g;
169
- while ((match = classRegex.exec(content)) !== null) {
170
- const className = match[1];
171
- issues.push({
172
- level: 'warning',
173
- type: 'contracts',
174
- message: `DTO '${className}' defined as class instead of record`,
175
- solution: `Use 'public record ${className}' for immutable DTOs`
176
- });
177
- }
178
-
179
- // 4. Classes should be sealed (unless abstract/base)
180
- const unsealedClassRegex = /public\s+class\s+(\w+)(?!\s*:\s*\w+Base)/g;
181
- while ((match = unsealedClassRegex.exec(content)) !== null) {
182
- const className = match[1];
183
- // Skip if already sealed
184
- const lineStart = content.lastIndexOf('\n', match.index) + 1;
185
- const lineEnd = content.indexOf('\n', match.index);
186
- const line = content.substring(lineStart, lineEnd);
187
-
188
- if (!line.includes('sealed') && !line.includes('abstract')) {
189
- issues.push({
190
- level: 'warning',
191
- type: 'contracts',
192
- message: `Class '${className}' should be sealed (unless designed for inheritance)`,
193
- solution: `Change to 'public sealed class ${className}'`
194
- });
195
- }
196
- }
197
-
198
- // 5. PascalCase for constants (NOT UPPER_SNAKE_CASE)
199
- const upperSnakeRegex = /const\s+\w+\s+([A-Z_][A-Z0-9_]+)\s*=/g;
200
- while ((match = upperSnakeRegex.exec(content)) !== null) {
201
- const constName = match[1];
202
- if (constName.includes('_') && constName === constName.toUpperCase()) {
203
- issues.push({
204
- level: 'error',
205
- type: 'contracts',
206
- message: `Constant '${constName}' uses UPPER_SNAKE_CASE (anti-pattern in C#)`,
207
- solution: `Use PascalCase: '${toPascalCase(constName)}'`
208
- });
209
- }
210
- }
211
-
212
- // 6. Methods returning Task must end with Async
213
- const taskMethodRegex = /Task(?:<[^>]+>)?\s+(\w+)\s*\(/g;
214
- while ((match = taskMethodRegex.exec(content)) !== null) {
215
- const methodName = match[1];
216
- if (!methodName.endsWith('Async') && !['get', 'set', 'init'].includes(methodName)) {
217
- issues.push({
218
- level: 'error',
219
- type: 'contracts',
220
- message: `Method '${methodName}' returns Task but doesn't end with 'Async'`,
221
- solution: `Rename to '${methodName}Async'`
222
- });
223
- }
224
- }
225
-
226
- // 7. Enum members should be PascalCase (NOT UPPER_SNAKE_CASE)
227
- const enumRegex = /enum\s+\w+\s*\{([^}]+)\}/gs;
228
- while ((match = enumRegex.exec(content)) !== null) {
229
- const enumBody = match[1];
230
- const memberRegex = /\n\s*([A-Z_][A-Z0-9_]+)/g;
231
- let memberMatch;
232
-
233
- while ((memberMatch = memberRegex.exec(enumBody)) !== null) {
234
- const memberName = memberMatch[1];
235
- if (memberName.includes('_') && memberName === memberName.toUpperCase()) {
236
- issues.push({
237
- level: 'error',
238
- type: 'contracts',
239
- message: `Enum member '${memberName}' uses UPPER_SNAKE_CASE (must be PascalCase)`,
240
- solution: `Use PascalCase: '${toPascalCase(memberName)}'`
241
- });
242
- }
243
- }
244
- }
245
-
246
- return issues;
247
- }
248
-
249
- /**
250
- * Validate contracts.ts structure for Next.js projects.
251
- * Checks: has exports, has Zod schemas, no C# syntax.
252
- */
253
- function validateContractsTs(content) {
254
- const issues = [];
255
-
256
- // Must have at least one export
257
- if (!/^export\s+(interface|type|const|function)/m.test(content)) {
258
- issues.push({
259
- level: 'error',
260
- type: 'contracts',
261
- message: 'contracts.ts has no exported types, interfaces, or schemas',
262
- solution: 'Add at least one export interface or export type'
263
- });
264
- }
265
-
266
- // Should have at least one Zod schema (warning only)
267
- if (!content.includes('z.object(')) {
268
- issues.push({
269
- level: 'warning',
270
- type: 'contracts',
271
- message: 'contracts.ts has no Zod schemas (z.object)',
272
- solution: "Add Zod schemas for runtime validation. Import from 'zod'."
273
- });
274
- }
275
-
276
- // Must NOT contain C# syntax
277
- const csharpPatterns = [
278
- { re: /^namespace\s+/m, name: 'namespace declaration' },
279
- { re: /^using\s+System/m, name: 'C# using directive' },
280
- { re: /\bpublic\s+record\b/m, name: 'C# record type' },
281
- { re: /\bpublic\s+class\b/m, name: 'C# class declaration' },
282
- ];
283
- for (const { re, name } of csharpPatterns) {
284
- if (re.test(content)) {
285
- issues.push({
286
- level: 'error',
287
- type: 'contracts',
288
- message: `contracts.ts contains C# syntax (${name})`,
289
- solution: 'This file was generated for .NET — regenerate contracts using the phase-design skill for a Next.js project'
290
- });
291
- }
292
- }
293
-
294
- return issues;
295
- }
296
-
297
- /**
298
- * Check if a string is PascalCase
299
- */
300
- function isPascalCase(str) {
301
- return /^[A-Z][a-z0-9]*([A-Z][a-z0-9]*)*$/.test(str);
302
- }
303
-
304
- /**
305
- * Convert string to PascalCase
306
- */
307
- function toPascalCase(str) {
308
- return str
309
- .split('_')
310
- .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
311
- .join('');
312
- }