@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.
- package/CLAUDE.md +3 -3
- package/LICENSE +71 -71
- package/README.md +5 -11
- package/bin/morph-spec.js +23 -1
- package/bin/task-manager.js +5 -5
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +2 -8
- package/framework/agents/README.md +14 -14
- package/framework/agents/architecture/standards-architect.md +159 -159
- package/framework/agents/frontend/css-specialist.md +1 -1
- package/framework/agents/infrastructure/azure-architect.md +149 -149
- package/framework/agents/infrastructure/azure-deploy-specialist.md +4 -4
- package/framework/agents/infrastructure/infra-architect.md +48 -48
- package/framework/commands/morph-apply.md +18 -18
- package/framework/commands/morph-preflight.md +3 -3
- package/framework/commands/morph-proposal.md +15 -15
- package/framework/commands/morph-status.md +3 -3
- package/framework/commands/morph-troubleshoot.md +1 -1
- package/framework/hooks/README.md +204 -204
- package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +2 -2
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +1 -1
- package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +3 -3
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +2 -2
- package/framework/hooks/git/pre-commit/agents.sh +1 -1
- package/framework/hooks/shared/phase-utils.js +139 -139
- package/framework/rules/csharp-standards.md +13 -13
- package/framework/rules/frontend-standards.md +13 -13
- package/framework/rules/infrastructure-standards.md +16 -16
- package/framework/rules/testing-standards.md +14 -14
- package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +8 -8
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +3 -3
- package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +254 -254
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +1 -1
- package/framework/skills/level-0-meta/morph-init/SKILL.md +11 -11
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +3 -3
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +1 -1
- package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +4 -4
- package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +25 -25
- package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +8 -8
- package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +7 -7
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +4 -4
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +23 -23
- package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +38 -0
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +29 -29
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +4 -4
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +9 -9
- package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +17 -17
- package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +14 -0
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +12 -12
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +16 -16
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +5 -5
- package/framework/standards/STANDARDS.json +2338 -2041
- package/framework/standards/frontend/nextjs/nextjs-patterns.md +17 -17
- package/framework/standards/infrastructure/supabase/supabase-custom-jwt.md +206 -0
- package/framework/templates/.idea/morph-templates.xml +92 -0
- package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/framework/templates/README.md +814 -814
- package/framework/templates/code/dotnet/contracts/Commands.cs +74 -74
- package/framework/templates/code/dotnet/contracts/Entities.cs +25 -25
- package/framework/templates/code/dotnet/contracts/Queries.cs +74 -74
- package/framework/templates/code/dotnet/contracts/README.md +74 -74
- package/framework/templates/code/dotnet/contracts/api-contracts.cs +173 -173
- package/framework/templates/code/dotnet/frontend/component.razor +239 -239
- package/framework/templates/code/dotnet/jobs/agent.cs +163 -163
- package/framework/templates/code/dotnet/jobs/job.cs +171 -171
- package/framework/templates/code/dotnet/test.cs +239 -239
- package/framework/templates/code/sql/rls-policy.sql +57 -57
- package/framework/templates/code/sql/supabase-migration.sql +100 -100
- package/framework/templates/code/sql/supabase-migration.template.sql +113 -113
- package/framework/templates/code/typescript/contracts.ts +168 -168
- package/framework/templates/context/CONTEXT.md +181 -181
- package/framework/templates/docs/proposal.md +182 -182
- package/framework/templates/docs/user-stories.md +34 -34
- package/framework/templates/examples/design-system-examples.md +357 -357
- package/framework/templates/examples/spec-examples.md +90 -90
- package/framework/templates/feature/decisions.md +187 -187
- package/framework/templates/feature/recap.md +146 -146
- package/framework/templates/feature/tasks.md +199 -199
- package/framework/templates/infrastructure/azure/README.md +286 -286
- package/framework/templates/infrastructure/azure/deploy-checklist.md +426 -426
- package/framework/templates/infrastructure/azure/parameters.dev.json +29 -29
- package/framework/templates/infrastructure/azure/parameters.prod.json +29 -29
- package/framework/templates/infrastructure/azure/parameters.staging.json +29 -29
- package/framework/templates/infrastructure/docker/docker-compose.template.yml +67 -67
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/docker/easypanel.template.json +54 -54
- package/framework/workflows/README.md +18 -18
- package/framework/workflows/docs/enforcement-pipeline.md +1 -1
- package/framework/workflows/docs/full-morph.md +2 -2
- package/package.json +1 -1
- package/src/commands/agents/dispatch-agents.js +17 -0
- package/src/commands/agents/index.js +13 -13
- package/src/commands/agents/project-metrics.js +74 -0
- package/src/commands/project/doctor.js +3 -4
- package/src/commands/project/install-plugin-cmd.js +28 -28
- package/src/commands/project/setup-infra-cmd.js +12 -12
- package/src/commands/project/status.js +8 -1
- package/src/commands/scope/escalate.js +5 -3
- package/src/commands/standards/context-budget.js +89 -0
- package/src/commands/tasks/index.js +4 -4
- package/src/commands/templates/index.js +5 -5
- package/src/commands/utils/index.js +3 -3
- package/src/commands/validation/index.js +4 -4
- package/src/commands/validation/validate-feature.js +32 -3
- package/src/core/state/phase-state-machine.js +217 -217
- package/src/lib/agents/metrics-aggregator.js +56 -0
- package/src/lib/generators/design-system-generator.js +298 -298
- package/src/lib/generators/index.js +10 -10
- package/src/lib/generators/metadata-extractor.js +387 -387
- package/src/lib/stack-filter.js +65 -65
- package/src/lib/standards/digest-builder.js +57 -3
- package/src/lib/standards/index.js +7 -7
- package/src/lib/standards/standards-context-injector.js +302 -302
- package/src/lib/validators/architecture/architecture-validator.js +387 -387
- package/src/lib/validators/architecture/index.js +7 -7
- package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +316 -316
- package/src/lib/validators/blazor/blazor-state-validator.js +279 -279
- package/src/lib/validators/blazor/blazor-validator.js +369 -369
- package/src/lib/validators/blazor/index.js +9 -9
- package/src/lib/validators/content/content-validator.js +287 -287
- package/src/lib/validators/content/index.js +7 -7
- package/src/lib/validators/contracts/index.js +7 -7
- package/src/lib/validators/css/css-validator.js +352 -352
- package/src/lib/validators/css/index.js +7 -7
- package/src/lib/validators/design-system/design-system-validator.js +231 -231
- package/src/lib/validators/design-system/index.js +7 -7
- package/src/lib/validators/nextjs/index.js +6 -6
- package/src/lib/validators/nextjs/next-component-validator.js +183 -183
- package/src/lib/validators/packages/index.js +7 -7
- package/src/lib/validators/packages/package-validator.js +360 -360
- package/src/lib/validators/shared/index.js +12 -12
- package/src/lib/validators/shared/issue-counter.js +18 -18
- package/src/lib/validators/shared/result-formatter.js +124 -124
- package/src/lib/validators/ui/index.js +7 -7
- package/src/lib/validators/ui/ui-contrast-validator.js +422 -422
- package/src/scripts/global-install.js +112 -58
- package/src/scripts/install-plugin.js +126 -126
- package/src/utils/claude-md-injector.js +90 -90
- package/src/utils/file-copier.js +200 -200
- package/src/utils/logger.js +32 -32
- package/src/utils/skills-installer.js +178 -178
- package/src/utils/version-checker.js +169 -175
package/src/lib/stack-filter.js
CHANGED
|
@@ -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
|
-
|
|
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';
|