@polymorphism-tech/morph-spec 4.10.9 → 4.10.10

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 (114) hide show
  1. package/CLAUDE.md +3 -3
  2. package/LICENSE +71 -71
  3. package/README.md +2 -2
  4. package/bin/morph-spec.js +22 -0
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +1 -1
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/agents/README.md +14 -14
  9. package/framework/agents/architecture/standards-architect.md +159 -159
  10. package/framework/agents/infrastructure/azure-architect.md +149 -149
  11. package/framework/agents/infrastructure/infra-architect.md +48 -48
  12. package/framework/hooks/README.md +204 -204
  13. package/framework/hooks/shared/phase-utils.js +139 -139
  14. package/framework/rules/csharp-standards.md +13 -13
  15. package/framework/rules/frontend-standards.md +13 -13
  16. package/framework/rules/infrastructure-standards.md +16 -16
  17. package/framework/rules/testing-standards.md +14 -14
  18. package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +254 -254
  19. package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +1 -1
  20. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +1 -1
  21. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +1 -1
  22. package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +38 -0
  23. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +1 -1
  24. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +1 -1
  25. package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +1 -1
  26. package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +14 -0
  27. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +1 -1
  28. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +1 -1
  29. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
  30. package/framework/standards/STANDARDS.json +2338 -2041
  31. package/framework/standards/frontend/nextjs/nextjs-patterns.md +17 -17
  32. package/framework/standards/infrastructure/supabase/supabase-custom-jwt.md +206 -0
  33. package/framework/templates/.idea/morph-templates.xml +92 -0
  34. package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  35. package/framework/templates/README.md +814 -814
  36. package/framework/templates/code/dotnet/contracts/Commands.cs +74 -74
  37. package/framework/templates/code/dotnet/contracts/Entities.cs +25 -25
  38. package/framework/templates/code/dotnet/contracts/Queries.cs +74 -74
  39. package/framework/templates/code/dotnet/contracts/README.md +74 -74
  40. package/framework/templates/code/dotnet/contracts/api-contracts.cs +173 -173
  41. package/framework/templates/code/dotnet/frontend/component.razor +239 -239
  42. package/framework/templates/code/dotnet/jobs/agent.cs +163 -163
  43. package/framework/templates/code/dotnet/jobs/job.cs +171 -171
  44. package/framework/templates/code/dotnet/test.cs +239 -239
  45. package/framework/templates/code/sql/rls-policy.sql +57 -57
  46. package/framework/templates/code/sql/supabase-migration.sql +100 -100
  47. package/framework/templates/code/sql/supabase-migration.template.sql +113 -113
  48. package/framework/templates/code/typescript/contracts.ts +168 -168
  49. package/framework/templates/context/CONTEXT.md +181 -181
  50. package/framework/templates/docs/proposal.md +182 -182
  51. package/framework/templates/docs/user-stories.md +34 -34
  52. package/framework/templates/examples/design-system-examples.md +357 -357
  53. package/framework/templates/examples/spec-examples.md +90 -90
  54. package/framework/templates/feature/decisions.md +187 -187
  55. package/framework/templates/feature/recap.md +146 -146
  56. package/framework/templates/feature/tasks.md +199 -199
  57. package/framework/templates/infrastructure/azure/README.md +286 -286
  58. package/framework/templates/infrastructure/azure/deploy-checklist.md +426 -426
  59. package/framework/templates/infrastructure/azure/parameters.dev.json +29 -29
  60. package/framework/templates/infrastructure/azure/parameters.prod.json +29 -29
  61. package/framework/templates/infrastructure/azure/parameters.staging.json +29 -29
  62. package/framework/templates/infrastructure/docker/docker-compose.template.yml +67 -67
  63. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  64. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  65. package/framework/templates/infrastructure/docker/easypanel.template.json +54 -54
  66. package/package.json +1 -1
  67. package/src/commands/agents/dispatch-agents.js +17 -0
  68. package/src/commands/agents/index.js +13 -13
  69. package/src/commands/agents/project-metrics.js +74 -0
  70. package/src/commands/project/install-plugin-cmd.js +28 -28
  71. package/src/commands/project/setup-infra-cmd.js +12 -12
  72. package/src/commands/standards/context-budget.js +89 -0
  73. package/src/commands/tasks/index.js +4 -4
  74. package/src/commands/templates/index.js +5 -5
  75. package/src/commands/utils/index.js +3 -3
  76. package/src/commands/validation/index.js +4 -4
  77. package/src/core/state/phase-state-machine.js +217 -217
  78. package/src/lib/agents/metrics-aggregator.js +56 -0
  79. package/src/lib/generators/design-system-generator.js +298 -298
  80. package/src/lib/generators/index.js +10 -10
  81. package/src/lib/generators/metadata-extractor.js +387 -387
  82. package/src/lib/stack-filter.js +65 -65
  83. package/src/lib/standards/digest-builder.js +57 -3
  84. package/src/lib/standards/index.js +7 -7
  85. package/src/lib/standards/standards-context-injector.js +302 -302
  86. package/src/lib/validators/architecture/architecture-validator.js +387 -387
  87. package/src/lib/validators/architecture/index.js +7 -7
  88. package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +316 -316
  89. package/src/lib/validators/blazor/blazor-state-validator.js +279 -279
  90. package/src/lib/validators/blazor/blazor-validator.js +369 -369
  91. package/src/lib/validators/blazor/index.js +9 -9
  92. package/src/lib/validators/content/content-validator.js +287 -287
  93. package/src/lib/validators/content/index.js +7 -7
  94. package/src/lib/validators/contracts/index.js +7 -7
  95. package/src/lib/validators/css/css-validator.js +352 -352
  96. package/src/lib/validators/css/index.js +7 -7
  97. package/src/lib/validators/design-system/design-system-validator.js +231 -231
  98. package/src/lib/validators/design-system/index.js +7 -7
  99. package/src/lib/validators/nextjs/index.js +6 -6
  100. package/src/lib/validators/nextjs/next-component-validator.js +183 -183
  101. package/src/lib/validators/packages/index.js +7 -7
  102. package/src/lib/validators/packages/package-validator.js +360 -360
  103. package/src/lib/validators/shared/index.js +12 -12
  104. package/src/lib/validators/shared/issue-counter.js +18 -18
  105. package/src/lib/validators/shared/result-formatter.js +124 -124
  106. package/src/lib/validators/ui/index.js +7 -7
  107. package/src/lib/validators/ui/ui-contrast-validator.js +422 -422
  108. package/src/scripts/global-install.js +58 -58
  109. package/src/scripts/install-plugin.js +126 -126
  110. package/src/utils/claude-md-injector.js +90 -90
  111. package/src/utils/file-copier.js +200 -200
  112. package/src/utils/logger.js +32 -32
  113. package/src/utils/skills-installer.js +178 -178
  114. package/src/utils/version-checker.js +175 -175
@@ -1,65 +1,65 @@
1
- /**
2
- * Parse `stacks:` from YAML frontmatter content.
3
- * Returns ['*'] if no stacks field found (backwards compatible — always install).
4
- *
5
- * Supports:
6
- * stacks:\n - dotnet\n - blazor (YAML list)
7
- * stacks: [dotnet, blazor] (inline array)
8
- *
9
- * @param {string} content - Raw file content with YAML frontmatter
10
- * @returns {string[]}
11
- */
12
- export function parseStacks(content) {
13
- const normalized = content.replace(/\r\n/g, '\n');
14
- const fmMatch = normalized.match(/^---\n([\s\S]*?)\n---/);
15
- if (!fmMatch) return ['*'];
16
-
17
- const fm = fmMatch[1];
18
-
19
- // Try inline format: stacks: [dotnet, blazor]
20
- const inlineMatch = fm.match(/^stacks:\s*\[([^\]]*)\]/m);
21
- if (inlineMatch) {
22
- const items = inlineMatch[1].split(',').map(s => s.trim()).filter(Boolean);
23
- return items.length > 0 ? items : ['*'];
24
- }
25
-
26
- // Try YAML list format: stacks:\n - dotnet\n - blazor
27
- const listMatch = fm.match(/^stacks:\s*\n((?:\s+-\s+.+\n?)*)/m);
28
- if (listMatch) {
29
- const items = listMatch[1]
30
- .split('\n')
31
- .map(line => line.replace(/^\s+-\s+/, '').replace(/["']/g, '').trim())
32
- .filter(Boolean);
33
- return items.length > 0 ? items : ['*'];
34
- }
35
-
36
- return ['*'];
37
- }
38
-
39
- /**
40
- * Extract project tags from config object.
41
- * Combines stack tags (from `project.stack` split by '-' or '+')
42
- * with integration tags (from `project.integrations` array).
43
- * Deduplicates the result.
44
- */
45
- export function getProjectTags(config) {
46
- const stack = config?.project?.stack;
47
- const stackTags = (stack && typeof stack === 'string')
48
- ? stack.split(/[-+]/).filter(Boolean)
49
- : [];
50
- const integrations = Array.isArray(config?.project?.integrations)
51
- ? config.project.integrations
52
- : [];
53
- return [...new Set([...stackTags, ...integrations])];
54
- }
55
-
56
- /**
57
- * Determine if an asset should be installed for the given project tags.
58
- * - stacks includes '*' → always install
59
- * - stacks is empty → treat as '*' (backwards compatible)
60
- * - Otherwise → install if intersection with projectTags is non-empty
61
- */
62
- export function shouldInstall(assetStacks, projectTags) {
63
- if (assetStacks.length === 0 || assetStacks.includes('*')) return true;
64
- return assetStacks.some(s => projectTags.includes(s));
65
- }
1
+ /**
2
+ * Parse `stacks:` from YAML frontmatter content.
3
+ * Returns ['*'] if no stacks field found (backwards compatible — always install).
4
+ *
5
+ * Supports:
6
+ * stacks:\n - dotnet\n - blazor (YAML list)
7
+ * stacks: [dotnet, blazor] (inline array)
8
+ *
9
+ * @param {string} content - Raw file content with YAML frontmatter
10
+ * @returns {string[]}
11
+ */
12
+ export function parseStacks(content) {
13
+ const normalized = content.replace(/\r\n/g, '\n');
14
+ const fmMatch = normalized.match(/^---\n([\s\S]*?)\n---/);
15
+ if (!fmMatch) return ['*'];
16
+
17
+ const fm = fmMatch[1];
18
+
19
+ // Try inline format: stacks: [dotnet, blazor]
20
+ const inlineMatch = fm.match(/^stacks:\s*\[([^\]]*)\]/m);
21
+ if (inlineMatch) {
22
+ const items = inlineMatch[1].split(',').map(s => s.trim()).filter(Boolean);
23
+ return items.length > 0 ? items : ['*'];
24
+ }
25
+
26
+ // Try YAML list format: stacks:\n - dotnet\n - blazor
27
+ const listMatch = fm.match(/^stacks:\s*\n((?:\s+-\s+.+\n?)*)/m);
28
+ if (listMatch) {
29
+ const items = listMatch[1]
30
+ .split('\n')
31
+ .map(line => line.replace(/^\s+-\s+/, '').replace(/["']/g, '').trim())
32
+ .filter(Boolean);
33
+ return items.length > 0 ? items : ['*'];
34
+ }
35
+
36
+ return ['*'];
37
+ }
38
+
39
+ /**
40
+ * Extract project tags from config object.
41
+ * Combines stack tags (from `project.stack` split by '-' or '+')
42
+ * with integration tags (from `project.integrations` array).
43
+ * Deduplicates the result.
44
+ */
45
+ export function getProjectTags(config) {
46
+ const stack = config?.project?.stack;
47
+ const stackTags = (stack && typeof stack === 'string')
48
+ ? stack.split(/[-+]/).filter(Boolean)
49
+ : [];
50
+ const integrations = Array.isArray(config?.project?.integrations)
51
+ ? config.project.integrations
52
+ : [];
53
+ return [...new Set([...stackTags, ...integrations])];
54
+ }
55
+
56
+ /**
57
+ * Determine if an asset should be installed for the given project tags.
58
+ * - stacks includes '*' → always install
59
+ * - stacks is empty → treat as '*' (backwards compatible)
60
+ * - Otherwise → install if intersection with projectTags is non-empty
61
+ */
62
+ export function shouldInstall(assetStacks, projectTags) {
63
+ if (assetStacks.length === 0 || assetStacks.includes('*')) return true;
64
+ return assetStacks.some(s => projectTags.includes(s));
65
+ }
@@ -17,12 +17,16 @@
17
17
  import { readFileSync, existsSync } from 'fs';
18
18
  import { join, dirname } from 'path';
19
19
  import { fileURLToPath } from 'url';
20
+ import { shouldInstall, getProjectTags } from '../../lib/stack-filter.js';
20
21
 
21
22
  const __dirname = dirname(fileURLToPath(import.meta.url));
22
23
 
23
24
  const AGENTS_JSON_PATH = join(__dirname, '../../../framework/agents.json');
24
25
  const STANDARDS_JSON_PATH = join(__dirname, '../../../framework/standards/STANDARDS.json');
25
26
 
27
+ /** Token budget threshold — warn if briefing exceeds this */
28
+ const TOKEN_BUDGET_THRESHOLD = 8000;
29
+
26
30
  let _agentsCache = null;
27
31
  let _standardsCache = null;
28
32
 
@@ -65,6 +69,34 @@ function loadStandards() {
65
69
  }
66
70
  }
67
71
 
72
+ /**
73
+ * Load project config.json from .morph/config.json.
74
+ *
75
+ * @param {string} [projectPath] - Override project path (defaults to cwd)
76
+ * @returns {Object|null}
77
+ */
78
+ function loadProjectConfig(projectPath) {
79
+ const configPath = join(projectPath || process.cwd(), '.morph', 'config.json');
80
+ try {
81
+ if (!existsSync(configPath)) return null;
82
+ return JSON.parse(readFileSync(configPath, 'utf8'));
83
+ } catch {
84
+ return null;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Estimate token count for a text string.
90
+ * Uses ~4 chars/token heuristic (conservative for English + code).
91
+ *
92
+ * @param {string} text
93
+ * @returns {number}
94
+ */
95
+ export function estimateTokens(text) {
96
+ if (!text) return 0;
97
+ return Math.ceil(text.length / 4);
98
+ }
99
+
68
100
  // ─────────────────────────────────────────────────────────────────────────────
69
101
  // Helpers
70
102
  // ─────────────────────────────────────────────────────────────────────────────
@@ -149,13 +181,15 @@ function formatBriefingLine(entry, priority, anchor) {
149
181
  /**
150
182
  * Build an agent briefing string for injection into taskPrompt.
151
183
  * Returns empty string when agent not found, has no standards, or all refs
152
- * are filtered by scope.
184
+ * are filtered by scope or stack.
153
185
  *
154
186
  * @param {string} agentId - Agent ID from agents.json
155
187
  * @param {string} phase - Current phase (design|tasks|implement)
188
+ * @param {Object} [opts] - Options
189
+ * @param {string} [opts.projectPath] - Override project path (defaults to cwd)
156
190
  * @returns {string} Formatted briefing or ''
157
191
  */
158
- export function buildAgentBriefing(agentId, phase) {
192
+ export function buildAgentBriefing(agentId, phase, opts = {}) {
159
193
  const agents = loadAgents();
160
194
  const agentData = agents.agents?.[agentId];
161
195
  if (!agentData) return '';
@@ -164,6 +198,11 @@ export function buildAgentBriefing(agentId, phase) {
164
198
  if (standardsRefs.length === 0) return '';
165
199
 
166
200
  const standards = loadStandards();
201
+
202
+ // Load project tags for stack filtering
203
+ const config = loadProjectConfig(opts.projectPath);
204
+ const projectTags = config ? getProjectTags(config) : [];
205
+
167
206
  const lines = [];
168
207
 
169
208
  for (const rawRef of standardsRefs) {
@@ -180,11 +219,26 @@ export function buildAgentBriefing(agentId, phase) {
180
219
  continue;
181
220
  }
182
221
 
222
+ // Stack filtering: skip standards not relevant to this project's stack
223
+ if (projectTags.length > 0 && !shouldInstall(entry.stacks || ['*'], projectTags)) continue;
224
+
183
225
  const line = formatBriefingLine(entry, ref.priority, ref.anchor);
184
226
  if (line) lines.push(line);
185
227
  }
186
228
 
187
- return lines.length > 0 ? lines.join('\n') : '';
229
+ const briefing = lines.length > 0 ? lines.join('\n') : '';
230
+
231
+ // Token budget warning
232
+ if (briefing) {
233
+ const tokens = estimateTokens(briefing);
234
+ if (tokens > TOKEN_BUDGET_THRESHOLD) {
235
+ process.stderr.write(
236
+ `[digest-builder] Warning: briefing for agent '${agentId}' is ~${tokens} tokens (threshold: ${TOKEN_BUDGET_THRESHOLD})\n`
237
+ );
238
+ }
239
+ }
240
+
241
+ return briefing;
188
242
  }
189
243
 
190
244
  /**
@@ -1,7 +1,7 @@
1
- /**
2
- * Standards Utilities
3
- *
4
- * Injects relevant coding standards and architectural guidelines into LLM context.
5
- */
6
-
7
- export * from './standards-context-injector.js';
1
+ /**
2
+ * Standards Utilities
3
+ *
4
+ * Injects relevant coding standards and architectural guidelines into LLM context.
5
+ */
6
+
7
+ export * from './standards-context-injector.js';