@polymorphism-tech/morph-spec 4.10.8 → 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 (116) 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-0-meta/morph-init/SKILL.md +57 -9
  20. package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +1 -1
  21. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +1 -1
  22. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +1 -1
  23. package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +38 -0
  24. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +1 -1
  25. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +1 -1
  26. package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +1 -1
  27. package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +14 -0
  28. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +1 -1
  29. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +49 -16
  30. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
  31. package/framework/standards/STANDARDS.json +2338 -2041
  32. package/framework/standards/frontend/nextjs/nextjs-patterns.md +17 -17
  33. package/framework/standards/infrastructure/supabase/supabase-custom-jwt.md +206 -0
  34. package/framework/templates/.idea/morph-templates.xml +92 -0
  35. package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  36. package/framework/templates/README.md +814 -814
  37. package/framework/templates/code/dotnet/contracts/Commands.cs +74 -74
  38. package/framework/templates/code/dotnet/contracts/Entities.cs +25 -25
  39. package/framework/templates/code/dotnet/contracts/Queries.cs +74 -74
  40. package/framework/templates/code/dotnet/contracts/README.md +74 -74
  41. package/framework/templates/code/dotnet/contracts/api-contracts.cs +173 -173
  42. package/framework/templates/code/dotnet/frontend/component.razor +239 -239
  43. package/framework/templates/code/dotnet/jobs/agent.cs +163 -163
  44. package/framework/templates/code/dotnet/jobs/job.cs +171 -171
  45. package/framework/templates/code/dotnet/test.cs +239 -239
  46. package/framework/templates/code/sql/rls-policy.sql +57 -57
  47. package/framework/templates/code/sql/supabase-migration.sql +100 -100
  48. package/framework/templates/code/sql/supabase-migration.template.sql +113 -113
  49. package/framework/templates/code/typescript/contracts.ts +168 -168
  50. package/framework/templates/context/CONTEXT.md +181 -181
  51. package/framework/templates/docs/proposal.md +182 -182
  52. package/framework/templates/docs/user-stories.md +34 -34
  53. package/framework/templates/examples/design-system-examples.md +357 -357
  54. package/framework/templates/examples/spec-examples.md +90 -90
  55. package/framework/templates/feature/decisions.md +187 -187
  56. package/framework/templates/feature/recap.md +146 -146
  57. package/framework/templates/feature/tasks.md +199 -199
  58. package/framework/templates/infrastructure/azure/README.md +286 -286
  59. package/framework/templates/infrastructure/azure/deploy-checklist.md +426 -426
  60. package/framework/templates/infrastructure/azure/parameters.dev.json +29 -29
  61. package/framework/templates/infrastructure/azure/parameters.prod.json +29 -29
  62. package/framework/templates/infrastructure/azure/parameters.staging.json +29 -29
  63. package/framework/templates/infrastructure/docker/docker-compose.template.yml +67 -67
  64. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  65. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  66. package/framework/templates/infrastructure/docker/easypanel.template.json +54 -54
  67. package/package.json +1 -1
  68. package/src/commands/agents/dispatch-agents.js +17 -0
  69. package/src/commands/agents/index.js +13 -13
  70. package/src/commands/agents/project-metrics.js +74 -0
  71. package/src/commands/project/install-plugin-cmd.js +28 -28
  72. package/src/commands/project/setup-infra-cmd.js +12 -12
  73. package/src/commands/standards/context-budget.js +89 -0
  74. package/src/commands/tasks/index.js +4 -4
  75. package/src/commands/templates/index.js +5 -5
  76. package/src/commands/utils/index.js +3 -3
  77. package/src/commands/validation/index.js +4 -4
  78. package/src/core/state/phase-state-machine.js +217 -217
  79. package/src/lib/agents/metrics-aggregator.js +56 -0
  80. package/src/lib/detectors/design-system-detector.js +73 -31
  81. package/src/lib/generators/design-system-generator.js +298 -298
  82. package/src/lib/generators/index.js +10 -10
  83. package/src/lib/generators/metadata-extractor.js +387 -387
  84. package/src/lib/stack-filter.js +65 -65
  85. package/src/lib/standards/digest-builder.js +57 -3
  86. package/src/lib/standards/index.js +7 -7
  87. package/src/lib/standards/standards-context-injector.js +302 -302
  88. package/src/lib/validators/architecture/architecture-validator.js +387 -387
  89. package/src/lib/validators/architecture/index.js +7 -7
  90. package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +316 -316
  91. package/src/lib/validators/blazor/blazor-state-validator.js +279 -279
  92. package/src/lib/validators/blazor/blazor-validator.js +369 -369
  93. package/src/lib/validators/blazor/index.js +9 -9
  94. package/src/lib/validators/content/content-validator.js +287 -287
  95. package/src/lib/validators/content/index.js +7 -7
  96. package/src/lib/validators/contracts/index.js +7 -7
  97. package/src/lib/validators/css/css-validator.js +352 -352
  98. package/src/lib/validators/css/index.js +7 -7
  99. package/src/lib/validators/design-system/design-system-validator.js +231 -231
  100. package/src/lib/validators/design-system/index.js +7 -7
  101. package/src/lib/validators/nextjs/index.js +6 -6
  102. package/src/lib/validators/nextjs/next-component-validator.js +183 -183
  103. package/src/lib/validators/packages/index.js +7 -7
  104. package/src/lib/validators/packages/package-validator.js +360 -360
  105. package/src/lib/validators/shared/index.js +12 -12
  106. package/src/lib/validators/shared/issue-counter.js +18 -18
  107. package/src/lib/validators/shared/result-formatter.js +124 -124
  108. package/src/lib/validators/ui/index.js +7 -7
  109. package/src/lib/validators/ui/ui-contrast-validator.js +422 -422
  110. package/src/scripts/global-install.js +58 -58
  111. package/src/scripts/install-plugin.js +126 -126
  112. package/src/utils/claude-md-injector.js +90 -90
  113. package/src/utils/file-copier.js +200 -200
  114. package/src/utils/logger.js +32 -32
  115. package/src/utils/skills-installer.js +178 -178
  116. 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';