@polymorphism-tech/morph-spec 4.10.9 → 4.11.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 (145) hide show
  1. package/CLAUDE.md +3 -3
  2. package/LICENSE +71 -71
  3. package/README.md +5 -11
  4. package/bin/morph-spec.js +23 -1
  5. package/bin/task-manager.js +5 -5
  6. package/claude-plugin.json +1 -1
  7. package/docs/CHEATSHEET.md +1 -1
  8. package/docs/QUICKSTART.md +2 -8
  9. package/framework/agents/README.md +14 -14
  10. package/framework/agents/architecture/standards-architect.md +159 -159
  11. package/framework/agents/frontend/css-specialist.md +1 -1
  12. package/framework/agents/infrastructure/azure-architect.md +149 -149
  13. package/framework/agents/infrastructure/azure-deploy-specialist.md +4 -4
  14. package/framework/agents/infrastructure/infra-architect.md +48 -48
  15. package/framework/commands/morph-apply.md +18 -18
  16. package/framework/commands/morph-preflight.md +3 -3
  17. package/framework/commands/morph-proposal.md +15 -15
  18. package/framework/commands/morph-status.md +3 -3
  19. package/framework/commands/morph-troubleshoot.md +1 -1
  20. package/framework/hooks/README.md +204 -204
  21. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +2 -2
  22. package/framework/hooks/claude-code/session-start/inject-morph-context.js +1 -1
  23. package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +3 -3
  24. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +2 -2
  25. package/framework/hooks/git/pre-commit/agents.sh +1 -1
  26. package/framework/hooks/shared/phase-utils.js +139 -139
  27. package/framework/rules/csharp-standards.md +13 -13
  28. package/framework/rules/frontend-standards.md +13 -13
  29. package/framework/rules/infrastructure-standards.md +16 -16
  30. package/framework/rules/testing-standards.md +14 -14
  31. package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +8 -8
  32. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +3 -3
  33. package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +254 -254
  34. package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +1 -1
  35. package/framework/skills/level-0-meta/morph-init/SKILL.md +11 -11
  36. package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +3 -3
  37. package/framework/skills/level-0-meta/morph-replicate/SKILL.md +1 -1
  38. package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +4 -4
  39. package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +25 -25
  40. package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +8 -8
  41. package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +7 -7
  42. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +4 -4
  43. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +23 -23
  44. package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +1 -1
  45. package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +38 -0
  46. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +29 -29
  47. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +4 -4
  48. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +9 -9
  49. package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +17 -17
  50. package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +14 -0
  51. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +12 -12
  52. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +16 -16
  53. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +5 -5
  54. package/framework/standards/STANDARDS.json +2338 -2041
  55. package/framework/standards/frontend/nextjs/nextjs-patterns.md +17 -17
  56. package/framework/standards/infrastructure/supabase/supabase-custom-jwt.md +206 -0
  57. package/framework/templates/.idea/morph-templates.xml +92 -0
  58. package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  59. package/framework/templates/README.md +814 -814
  60. package/framework/templates/code/dotnet/contracts/Commands.cs +74 -74
  61. package/framework/templates/code/dotnet/contracts/Entities.cs +25 -25
  62. package/framework/templates/code/dotnet/contracts/Queries.cs +74 -74
  63. package/framework/templates/code/dotnet/contracts/README.md +74 -74
  64. package/framework/templates/code/dotnet/contracts/api-contracts.cs +173 -173
  65. package/framework/templates/code/dotnet/frontend/component.razor +239 -239
  66. package/framework/templates/code/dotnet/jobs/agent.cs +163 -163
  67. package/framework/templates/code/dotnet/jobs/job.cs +171 -171
  68. package/framework/templates/code/dotnet/test.cs +239 -239
  69. package/framework/templates/code/sql/rls-policy.sql +57 -57
  70. package/framework/templates/code/sql/supabase-migration.sql +100 -100
  71. package/framework/templates/code/sql/supabase-migration.template.sql +113 -113
  72. package/framework/templates/code/typescript/contracts.ts +168 -168
  73. package/framework/templates/context/CONTEXT.md +181 -181
  74. package/framework/templates/docs/proposal.md +182 -182
  75. package/framework/templates/docs/user-stories.md +34 -34
  76. package/framework/templates/examples/design-system-examples.md +357 -357
  77. package/framework/templates/examples/spec-examples.md +90 -90
  78. package/framework/templates/feature/decisions.md +187 -187
  79. package/framework/templates/feature/recap.md +146 -146
  80. package/framework/templates/feature/tasks.md +199 -199
  81. package/framework/templates/infrastructure/azure/README.md +286 -286
  82. package/framework/templates/infrastructure/azure/deploy-checklist.md +426 -426
  83. package/framework/templates/infrastructure/azure/parameters.dev.json +29 -29
  84. package/framework/templates/infrastructure/azure/parameters.prod.json +29 -29
  85. package/framework/templates/infrastructure/azure/parameters.staging.json +29 -29
  86. package/framework/templates/infrastructure/docker/docker-compose.template.yml +67 -67
  87. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  88. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  89. package/framework/templates/infrastructure/docker/easypanel.template.json +54 -54
  90. package/framework/workflows/README.md +18 -18
  91. package/framework/workflows/docs/enforcement-pipeline.md +1 -1
  92. package/framework/workflows/docs/full-morph.md +2 -2
  93. package/package.json +1 -1
  94. package/src/commands/agents/dispatch-agents.js +17 -0
  95. package/src/commands/agents/index.js +13 -13
  96. package/src/commands/agents/project-metrics.js +74 -0
  97. package/src/commands/project/doctor.js +3 -4
  98. package/src/commands/project/install-plugin-cmd.js +28 -28
  99. package/src/commands/project/setup-infra-cmd.js +12 -12
  100. package/src/commands/project/status.js +8 -1
  101. package/src/commands/scope/escalate.js +5 -3
  102. package/src/commands/standards/context-budget.js +89 -0
  103. package/src/commands/tasks/index.js +4 -4
  104. package/src/commands/templates/index.js +5 -5
  105. package/src/commands/utils/index.js +3 -3
  106. package/src/commands/validation/index.js +4 -4
  107. package/src/commands/validation/validate-feature.js +32 -3
  108. package/src/core/state/phase-state-machine.js +217 -217
  109. package/src/lib/agents/metrics-aggregator.js +56 -0
  110. package/src/lib/generators/design-system-generator.js +298 -298
  111. package/src/lib/generators/index.js +10 -10
  112. package/src/lib/generators/metadata-extractor.js +387 -387
  113. package/src/lib/stack-filter.js +65 -65
  114. package/src/lib/standards/digest-builder.js +57 -3
  115. package/src/lib/standards/index.js +7 -7
  116. package/src/lib/standards/standards-context-injector.js +302 -302
  117. package/src/lib/validators/architecture/architecture-validator.js +387 -387
  118. package/src/lib/validators/architecture/index.js +7 -7
  119. package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +316 -316
  120. package/src/lib/validators/blazor/blazor-state-validator.js +279 -279
  121. package/src/lib/validators/blazor/blazor-validator.js +369 -369
  122. package/src/lib/validators/blazor/index.js +9 -9
  123. package/src/lib/validators/content/content-validator.js +287 -287
  124. package/src/lib/validators/content/index.js +7 -7
  125. package/src/lib/validators/contracts/index.js +7 -7
  126. package/src/lib/validators/css/css-validator.js +352 -352
  127. package/src/lib/validators/css/index.js +7 -7
  128. package/src/lib/validators/design-system/design-system-validator.js +231 -231
  129. package/src/lib/validators/design-system/index.js +7 -7
  130. package/src/lib/validators/nextjs/index.js +6 -6
  131. package/src/lib/validators/nextjs/next-component-validator.js +183 -183
  132. package/src/lib/validators/packages/index.js +7 -7
  133. package/src/lib/validators/packages/package-validator.js +360 -360
  134. package/src/lib/validators/shared/index.js +12 -12
  135. package/src/lib/validators/shared/issue-counter.js +18 -18
  136. package/src/lib/validators/shared/result-formatter.js +124 -124
  137. package/src/lib/validators/ui/index.js +7 -7
  138. package/src/lib/validators/ui/ui-contrast-validator.js +422 -422
  139. package/src/scripts/global-install.js +112 -58
  140. package/src/scripts/install-plugin.js +126 -126
  141. package/src/utils/claude-md-injector.js +90 -90
  142. package/src/utils/file-copier.js +200 -200
  143. package/src/utils/logger.js +32 -32
  144. package/src/utils/skills-installer.js +178 -178
  145. package/src/utils/version-checker.js +169 -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';