@polymorphism-tech/morph-spec 4.9.0 → 4.10.1

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 (164) hide show
  1. package/README.md +2 -2
  2. package/bin/morph-spec.js +30 -0
  3. package/bin/task-manager.js +34 -22
  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 +35 -98
  8. package/framework/agents/backend/api-designer.md +3 -0
  9. package/framework/agents/backend/dotnet-senior.md +3 -0
  10. package/framework/agents/backend/ef-modeler.md +2 -0
  11. package/framework/agents/backend/hangfire-orchestrator.md +2 -0
  12. package/framework/agents/backend/ms-agent-expert.md +2 -0
  13. package/framework/agents/frontend/blazor-builder.md +2 -0
  14. package/framework/agents/frontend/nextjs-expert.md +2 -0
  15. package/framework/agents/infrastructure/azure-architect.md +2 -0
  16. package/framework/agents/infrastructure/azure-deploy-specialist.md +2 -0
  17. package/framework/agents/infrastructure/bicep-architect.md +2 -0
  18. package/framework/agents/infrastructure/container-specialist.md +2 -0
  19. package/framework/agents/infrastructure/devops-engineer.md +3 -0
  20. package/framework/agents/infrastructure/infra-architect.md +3 -0
  21. package/framework/agents/integrations/asaas-financial.md +2 -0
  22. package/framework/agents/integrations/azure-identity.md +2 -0
  23. package/framework/agents/integrations/clerk-auth.md +3 -0
  24. package/framework/agents/integrations/hangfire-integration.md +2 -0
  25. package/framework/agents/integrations/resend-email.md +2 -0
  26. package/framework/agents.json +37 -7
  27. package/framework/commands/commit.md +166 -0
  28. package/framework/commands/morph-apply.md +156 -155
  29. package/framework/commands/morph-archive.md +33 -27
  30. package/framework/commands/morph-infra.md +83 -77
  31. package/framework/commands/morph-preflight.md +97 -55
  32. package/framework/commands/morph-proposal.md +131 -58
  33. package/framework/commands/morph-status.md +36 -30
  34. package/framework/commands/morph-troubleshoot.md +68 -59
  35. package/framework/hooks/claude-code/notification/approval-reminder.js +3 -2
  36. package/framework/hooks/claude-code/post-tool-use/dispatch.js +154 -31
  37. package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +7 -84
  38. package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +8 -17
  39. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +16 -3
  40. package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +4 -3
  41. package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +3 -2
  42. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +60 -0
  43. package/framework/hooks/claude-code/session-start/inject-morph-context.js +55 -2
  44. package/framework/hooks/claude-code/session-start/post-compact-restore.js +41 -0
  45. package/framework/hooks/claude-code/stop/validate-completion.js +2 -15
  46. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +23 -5
  47. package/framework/hooks/shared/compact-restore.js +100 -0
  48. package/framework/hooks/shared/dispatch-helpers.js +116 -0
  49. package/framework/hooks/shared/phase-utils.js +9 -5
  50. package/framework/hooks/shared/state-reader.js +27 -3
  51. package/framework/phases.json +30 -7
  52. package/framework/rules/csharp-standards.md +3 -0
  53. package/framework/rules/frontend-standards.md +2 -0
  54. package/framework/rules/infrastructure-standards.md +3 -0
  55. package/framework/rules/morph-workflow.md +143 -86
  56. package/framework/rules/nextjs-standards.md +2 -0
  57. package/framework/rules/testing-standards.md +3 -0
  58. package/framework/skills/level-0-meta/mcp-registry.json +86 -51
  59. package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +139 -0
  60. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +42 -19
  61. package/framework/skills/level-0-meta/{code-review → morph-code-review}/SKILL.md +8 -5
  62. package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/SKILL.md +8 -6
  63. package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +362 -0
  64. package/framework/skills/level-0-meta/morph-init/SKILL.md +114 -20
  65. package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +362 -0
  66. package/framework/skills/level-0-meta/morph-replicate/SKILL.md +95 -87
  67. package/framework/skills/level-0-meta/{simulation-checklist → morph-simulation-checklist}/SKILL.md +24 -0
  68. package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/SKILL.md +43 -43
  69. package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/references/tools-per-phase.md +1 -2
  70. package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/SKILL.md +23 -12
  71. package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/scripts/check-phase-outputs.mjs +2 -2
  72. package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +247 -0
  73. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +270 -0
  74. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +499 -0
  75. package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +38 -0
  76. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +472 -0
  77. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +50 -0
  78. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +45 -0
  79. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +47 -0
  80. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +246 -0
  81. package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +238 -0
  82. package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +14 -0
  83. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +312 -0
  84. package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/scripts/validate-tasks.mjs +3 -3
  85. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +324 -0
  86. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +146 -0
  87. package/framework/standards/integration/mcp/mcp-tools.md +25 -7
  88. package/framework/templates/docs/onboarding.md +2 -2
  89. package/package.json +3 -4
  90. package/src/commands/agents/dispatch-agents.js +50 -3
  91. package/src/commands/mcp/mcp-setup.js +39 -2
  92. package/src/commands/phase/phase-reset.js +74 -0
  93. package/src/commands/project/doctor.js +26 -7
  94. package/src/commands/project/update.js +4 -4
  95. package/src/commands/scope/escalate.js +215 -0
  96. package/src/commands/state/advance-phase.js +27 -53
  97. package/src/commands/state/state.js +1 -1
  98. package/src/commands/task/expand.js +100 -0
  99. package/src/core/paths/output-schema.js +4 -3
  100. package/src/core/state/phase-state-machine.js +7 -4
  101. package/src/core/state/state-manager.js +4 -3
  102. package/src/lib/detectors/claude-config-detector.js +93 -347
  103. package/src/lib/detectors/design-system-detector.js +189 -189
  104. package/src/lib/detectors/index.js +155 -57
  105. package/src/lib/generators/context-generator.js +2 -2
  106. package/src/lib/installers/mcp-installer.js +37 -5
  107. package/src/lib/phase-chain/phase-validator.js +22 -16
  108. package/src/lib/scope/impact-analyzer.js +106 -0
  109. package/src/lib/stack-filter.js +58 -0
  110. package/src/lib/tasks/task-parser.js +1 -1
  111. package/src/lib/validators/shared/emit-validator-dispatch.js +64 -0
  112. package/src/scripts/setup-infra.js +68 -18
  113. package/src/utils/agents-installer.js +51 -17
  114. package/src/utils/claude-md-injector.js +90 -0
  115. package/src/utils/file-copier.js +0 -1
  116. package/src/utils/hooks-installer.js +16 -5
  117. package/src/utils/skills-installer.js +67 -7
  118. package/CLAUDE.md +0 -98
  119. package/framework/memory/patterns-learned.md +0 -766
  120. package/framework/skills/level-0-meta/brainstorming/SKILL.md +0 -137
  121. package/framework/skills/level-0-meta/frontend-review/SKILL.md +0 -359
  122. package/framework/skills/level-0-meta/post-implementation/SKILL.md +0 -362
  123. package/framework/skills/level-0-meta/terminal-title/SKILL.md +0 -61
  124. package/framework/skills/level-0-meta/terminal-title/scripts/set_title.sh +0 -65
  125. package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +0 -216
  126. package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +0 -252
  127. package/framework/skills/level-1-workflows/phase-design/SKILL.md +0 -383
  128. package/framework/skills/level-1-workflows/phase-implement/SKILL.md +0 -492
  129. package/framework/skills/level-1-workflows/phase-setup/SKILL.md +0 -195
  130. package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +0 -271
  131. package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +0 -286
  132. package/src/commands/project/index.js +0 -8
  133. package/src/core/index.js +0 -10
  134. package/src/core/state/index.js +0 -8
  135. package/src/core/templates/index.js +0 -9
  136. package/src/core/templates/template-data-sources.js +0 -325
  137. package/src/core/workflows/index.js +0 -7
  138. package/src/lib/detectors/config-detector.js +0 -223
  139. package/src/lib/detectors/standards-generator.js +0 -335
  140. package/src/lib/detectors/structure-detector.js +0 -275
  141. package/src/lib/monitor/agent-resolver.js +0 -144
  142. package/src/lib/monitor/renderer.js +0 -230
  143. package/src/lib/orchestration/index.js +0 -7
  144. package/src/lib/orchestration/team-orchestrator.js +0 -404
  145. package/src/sanitizer/context-sanitizer.js +0 -221
  146. package/src/sanitizer/patterns.js +0 -163
  147. package/src/writer/file-writer.js +0 -86
  148. /package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/references/proposal-example.md +0 -0
  149. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-example.md +0 -0
  150. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-guidelines.md +0 -0
  151. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/scripts/scan-csharp.mjs +0 -0
  152. /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/references/review-example-nextjs.md +0 -0
  153. /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/scripts/scan-nextjs.mjs +0 -0
  154. /package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/scripts/scan-accessibility.mjs +0 -0
  155. /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-dev-server.mjs +0 -0
  156. /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-stack.mjs +0 -0
  157. /package/framework/skills/level-1-workflows/{phase-clarify → morph-phase-clarify}/references/clarifications-example.md +0 -0
  158. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/architecture-analysis-guide.md +0 -0
  159. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-authoring-guide.md +0 -0
  160. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-example.md +0 -0
  161. /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/recap-example.md +0 -0
  162. /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/vsa-implementation-guide.md +0 -0
  163. /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/task-planning-patterns.md +0 -0
  164. /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/tasks-example.md +0 -0
@@ -8,8 +8,9 @@
8
8
  * maxTurns, skills, memory) followed by the agent's spawn prompt as the body.
9
9
  */
10
10
 
11
- import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, statSync } from 'node:fs';
11
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, statSync, unlinkSync } from 'node:fs';
12
12
  import { join } from 'path';
13
+ import { parseStacks, shouldInstall } from '../lib/stack-filter.js';
13
14
 
14
15
  /**
15
16
  * Installs tier-1 and tier-2 morph agents as native Claude Code subagents
@@ -39,7 +40,9 @@ export async function installAgents(projectDir, frameworkDir = 'framework', opti
39
40
 
40
41
  const DOTNET_STACKS = ['dotnet', 'blazor', 'dotnet-api', 'fullstack'];
41
42
  const eligible = agents.filter(a => {
42
- if (a.tier !== 1 && a.tier !== 2) return false;
43
+ if (a.tier !== 1 && a.tier !== 2 && a.tier !== 4) return false;
44
+ // Tier 4 validators must have a teammate with spawn_prompt
45
+ if (a.tier === 4 && !a.teammate?.spawn_prompt) return false;
43
46
  // Skip dotnet-senior for non-.NET projects
44
47
  if (a.id === 'dotnet-senior' && projectStack && !DOTNET_STACKS.includes(projectStack)) {
45
48
  return false;
@@ -49,9 +52,11 @@ export async function installAgents(projectDir, frameworkDir = 'framework', opti
49
52
 
50
53
  let tier1 = 0;
51
54
  let tier2 = 0;
55
+ let tier4 = 0;
52
56
  for (const agent of eligible) {
53
57
  const slug = agent.id ?? agent.name?.toLowerCase().replace(/\s+/g, '-');
54
- const filename = `morph-${slug}.md`;
58
+ const prefix = agent.tier === 4 ? 'morph-validator-' : 'morph-';
59
+ const filename = `${prefix}${slug}.md`;
55
60
  const targetPath = join(targetDir, filename);
56
61
 
57
62
  const description = buildDescription(agent);
@@ -63,8 +68,9 @@ export async function installAgents(projectDir, frameworkDir = 'framework', opti
63
68
 
64
69
  if (agent.tier === 1) tier1++;
65
70
  else if (agent.tier === 2) tier2++;
71
+ else if (agent.tier === 4) tier4++;
66
72
  }
67
- return { tier1, tier2 };
73
+ return { tier1, tier2, tier4 };
68
74
  }
69
75
 
70
76
  /**
@@ -88,6 +94,13 @@ const TIER_DEFAULTS = {
88
94
  skills: ['morph:checklist'],
89
95
  memory: 'local',
90
96
  },
97
+ 4: {
98
+ model: 'haiku',
99
+ tools: 'Read, Glob, Grep',
100
+ maxTurns: 10,
101
+ skills: [],
102
+ memory: 'project',
103
+ },
91
104
  };
92
105
 
93
106
  /**
@@ -101,19 +114,27 @@ const TIER_DEFAULTS = {
101
114
  function buildFrontmatter(agent, description) {
102
115
  const defaults = TIER_DEFAULTS[agent.tier] ?? TIER_DEFAULTS[2];
103
116
  const name = agent.title ?? agent.name;
104
- const skillsList = defaults.skills.map(s => ` - ${s}`).join('\n');
105
117
 
106
- return [
118
+ // Allow per-agent tool overrides (e.g., morph-spec-validator needs Bash for npm test)
119
+ const tools = agent.teammate?.tools ?? defaults.tools;
120
+ // Allow per-agent maxTurns override
121
+ const maxTurns = agent.teammate?.maxTurns ?? defaults.maxTurns;
122
+
123
+ const lines = [
107
124
  `name: ${name}`,
108
125
  `description: ${description}`,
109
126
  `model: ${defaults.model}`,
110
- `tools: ${defaults.tools}`,
111
- `maxTurns: ${defaults.maxTurns}`,
112
- `skills:`,
113
- skillsList,
114
- `memory: ${defaults.memory}`,
115
- '',
116
- ].join('\n');
127
+ `tools: ${tools}`,
128
+ `maxTurns: ${maxTurns}`,
129
+ ];
130
+
131
+ if (defaults.skills.length > 0) {
132
+ const skillsList = defaults.skills.map(s => ` - ${s}`).join('\n');
133
+ lines.push(`skills:`, skillsList);
134
+ }
135
+
136
+ lines.push(`memory: ${defaults.memory}`, '');
137
+ return lines.join('\n');
117
138
  }
118
139
 
119
140
  function buildDescription(agent) {
@@ -166,7 +187,8 @@ export function parseSkillFile(rawContent) {
166
187
  if (inlineMatch) description = inlineMatch[1].trim();
167
188
  }
168
189
 
169
- return { name, description, allowedTools, body };
190
+ const stacks = parseStacks(rawContent);
191
+ return { name, description, allowedTools, stacks, body };
170
192
  }
171
193
 
172
194
  /**
@@ -195,7 +217,7 @@ export function collectSkillFiles(dir) {
195
217
  * @param {string} projectDir - Target project directory
196
218
  * @param {string} frameworkDir - Path to morph-spec framework/ directory
197
219
  */
198
- export async function installDomainAgents(projectDir, frameworkDir = 'framework') {
220
+ export async function installDomainAgents(projectDir, frameworkDir = 'framework', { projectTags = [] } = {}) {
199
221
  const domainsDir = join(frameworkDir, 'agents');
200
222
  if (!existsSync(domainsDir)) return { specialists: 0 };
201
223
 
@@ -203,10 +225,12 @@ export async function installDomainAgents(projectDir, frameworkDir = 'framework'
203
225
  mkdirSync(targetDir, { recursive: true });
204
226
 
205
227
  let specialists = 0;
228
+ const installed = new Set();
206
229
  for (const filePath of collectSkillFiles(domainsDir)) {
207
230
  const raw = readFileSync(filePath, 'utf-8');
208
- const { name, description, allowedTools, body } = parseSkillFile(raw);
231
+ const { name, description, allowedTools, stacks, body } = parseSkillFile(raw);
209
232
  if (!name) continue;
233
+ if (!shouldInstall(stacks, projectTags)) continue;
210
234
 
211
235
  const desc = description ?? `MORPH-SPEC domain agent: ${name}`;
212
236
  const frontmatter = [
@@ -219,9 +243,19 @@ export async function installDomainAgents(projectDir, frameworkDir = 'framework'
219
243
  '',
220
244
  ].join('\n');
221
245
 
222
- const targetPath = join(targetDir, `morph-domain-${name}.md`);
246
+ const filename = `morph-domain-${name}.md`;
247
+ const targetPath = join(targetDir, filename);
223
248
  writeFileSync(targetPath, `---\n${frontmatter}---\n\n${body.trimStart()}`, 'utf-8');
249
+ installed.add(filename);
224
250
  specialists++;
225
251
  }
252
+
253
+ // Cleanup orphan morph-domain-* files
254
+ for (const file of readdirSync(targetDir)) {
255
+ if (file.startsWith('morph-domain-') && !installed.has(file)) {
256
+ unlinkSync(join(targetDir, file));
257
+ }
258
+ }
259
+
226
260
  return { specialists };
227
261
  }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * claude-md-injector.js
3
+ *
4
+ * Manages the root CLAUDE.md file in client projects.
5
+ * Instead of overwriting the user's CLAUDE.md, injects an @import line
6
+ * that references the morph-spec managed .claude/CLAUDE.md.
7
+ *
8
+ * The root CLAUDE.md belongs to the USER — morph-spec only adds its import.
9
+ */
10
+
11
+ import { pathExists, readFile, writeFile } from './file-copier.js';
12
+
13
+ const MORPH_IMPORT = '@.claude/CLAUDE.md';
14
+ const MORPH_MARKER = 'MORPH-SPEC';
15
+
16
+ /**
17
+ * Injects the morph-spec @import into the root CLAUDE.md.
18
+ *
19
+ * Three scenarios:
20
+ * 1. No CLAUDE.md exists → create minimal file with the import
21
+ * 2. Existing morph-only CLAUDE.md (old overwrite) → replace with minimal + import
22
+ * 3. Existing user CLAUDE.md → append import section at the end
23
+ *
24
+ * Idempotent — does nothing if the import already exists.
25
+ *
26
+ * @param {string} claudeMdPath - Absolute path to root CLAUDE.md
27
+ * @returns {Promise<'created'|'migrated'|'injected'|'already-present'>}
28
+ */
29
+ export async function injectMorphImport(claudeMdPath) {
30
+ if (!await pathExists(claudeMdPath)) {
31
+ await writeFile(claudeMdPath, `# Project\n\n${MORPH_IMPORT}\n`);
32
+ return 'created';
33
+ }
34
+
35
+ const content = await readFile(claudeMdPath);
36
+
37
+ if (content.includes(MORPH_IMPORT)) {
38
+ return 'already-present';
39
+ }
40
+
41
+ if (isMorphOnly(content)) {
42
+ await writeFile(claudeMdPath, `# Project\n\n${MORPH_IMPORT}\n`);
43
+ return 'migrated';
44
+ }
45
+
46
+ // User has their own content — append the import section
47
+ const separator = content.endsWith('\n') ? '\n' : '\n\n';
48
+ await writeFile(claudeMdPath, `${content}${separator}## MORPH-SPEC\n\n${MORPH_IMPORT}\n`);
49
+ return 'injected';
50
+ }
51
+
52
+ /**
53
+ * Checks if CLAUDE.md contains ONLY morph-spec content (old overwrite pattern).
54
+ * Heuristic: contains MORPH-SPEC marker AND no substantial user content.
55
+ */
56
+ function isMorphOnly(content) {
57
+ if (!content.includes(MORPH_MARKER)) return false;
58
+
59
+ // Strip morph-spec boilerplate markers and check if anything meaningful remains
60
+ const stripped = content
61
+ .replace(/# MORPH-SPEC Runtime Instructions/g, '')
62
+ .replace(/by Polymorphism Tech[^\n]*/g, '')
63
+ .replace(/\*MORPH-SPEC by Polymorphism Tech\*/g, '')
64
+ .replace(/@\.morph\/context\/README\.md/g, '')
65
+ .replace(/@\.claude\/CLAUDE\.md/g, '')
66
+ .replace(/## Critical Rules[\s\S]*?(?=##|$)/g, '')
67
+ .replace(/## Quick Reference[\s\S]*?(?=##|$)/g, '')
68
+ .replace(/## State & Outputs[\s\S]*?(?=##|$)/g, '')
69
+ .replace(/## Phase Sequence[\s\S]*?(?=##|$)/g, '')
70
+ .replace(/## Agents[\s\S]*?(?=##|$)/g, '')
71
+ .replace(/## Context Window Tip[\s\S]*?(?=##|$)/g, '')
72
+ .replace(/## Project Context[\s\S]*?(?=##|$)/g, '')
73
+ .replace(/[#\-|>*`\s]/g, '');
74
+
75
+ return stripped.length < 50;
76
+ }
77
+
78
+ /**
79
+ * Checks if the root CLAUDE.md has the morph-spec import.
80
+ * Used by doctor.js for health checks.
81
+ *
82
+ * @param {string} claudeMdPath - Absolute path to root CLAUDE.md
83
+ * @returns {Promise<'ok'|'missing'|'missing-import'>}
84
+ */
85
+ export async function checkClaudeMdImport(claudeMdPath) {
86
+ if (!await pathExists(claudeMdPath)) return 'missing';
87
+ const content = await readFile(claudeMdPath);
88
+ if (content.includes(MORPH_IMPORT)) return 'ok';
89
+ return 'missing-import';
90
+ }
@@ -144,7 +144,6 @@ export async function updateGitignore(projectPath) {
144
144
  '.claude/agents/',
145
145
  '.claude/CLAUDE.md',
146
146
  '.claude/settings.local.json',
147
- '.mcp.json',
148
147
  '.worktrees/',
149
148
  ''
150
149
  ];
@@ -15,7 +15,7 @@ import { homedir } from 'os';
15
15
  import { execSync } from 'child_process';
16
16
 
17
17
  /** Current hooks schema version — bump when hook definitions change */
18
- const HOOKS_VERSION = '2.10.0';
18
+ const HOOKS_VERSION = '2.12.0';
19
19
 
20
20
  /** Marker for old dispatch.js (v1) */
21
21
  const OLD_DISPATCH_COMMAND = 'node framework/hooks/agent-teams/dispatch.js';
@@ -31,10 +31,7 @@ const MORPH_PERMISSIONS = [
31
31
  'Edit(.morph/state.json)',
32
32
  'Write(.morph/framework/**)',
33
33
  'Edit(.morph/framework/**)',
34
- // Root CLAUDE.md (managed copy — source is framework/CLAUDE.md)
35
- 'Write(CLAUDE.md)',
36
- 'Edit(CLAUDE.md)',
37
- // .claude/CLAUDE.md (managed copy — source is framework/CLAUDE_runtime.md)
34
+ // .claude/CLAUDE.md (managed copy — source is framework/CLAUDE.md)
38
35
  'Write(.claude/CLAUDE.md)',
39
36
  'Edit(.claude/CLAUDE.md)',
40
37
  // .claude/rules/ (copied from framework/rules/)
@@ -63,6 +60,16 @@ const MORPH_HOOKS = [
63
60
  }]
64
61
  },
65
62
 
63
+ // === SessionStart: compact only — post-compact context restore ===
64
+ {
65
+ event: 'SessionStart',
66
+ matcher: 'compact',
67
+ hooks: [{
68
+ type: 'command',
69
+ command: 'node framework/hooks/claude-code/session-start/post-compact-restore.js'
70
+ }]
71
+ },
72
+
66
73
  // === UserPromptSubmit ===
67
74
  {
68
75
  event: 'UserPromptSubmit',
@@ -93,6 +100,10 @@ const MORPH_HOOKS = [
93
100
  {
94
101
  type: 'command',
95
102
  command: 'node framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js'
103
+ },
104
+ {
105
+ type: 'command',
106
+ command: 'node framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js'
96
107
  }
97
108
  ]
98
109
  },
@@ -16,9 +16,10 @@
16
16
  * assets/) so Claude Code can execute scripts and load references on demand.
17
17
  */
18
18
 
19
- import { mkdirSync, copyFileSync, existsSync, readdirSync, statSync } from 'fs';
19
+ import { mkdirSync, copyFileSync, existsSync, readdirSync, statSync, readFileSync, rmSync } from 'fs';
20
20
  import { join } from 'path';
21
21
  import { fileURLToPath } from 'url';
22
+ import { parseStacks, shouldInstall } from '../lib/stack-filter.js';
22
23
 
23
24
  const __dirname = fileURLToPath(new URL('.', import.meta.url));
24
25
  const FRAMEWORK_SKILLS_DIR = join(__dirname, '..', '..', 'framework', 'skills');
@@ -32,6 +33,16 @@ const FRAMEWORK_SKILLS_DIR = join(__dirname, '..', '..', 'framework', 'skills');
32
33
  */
33
34
  const SKILL_LEVELS_TO_INSTALL = ['level-0-meta', 'level-1-workflows'];
34
35
 
36
+ /**
37
+ * Check if a skill should be installed based on its stacks: frontmatter.
38
+ * @param {string} skillContent - Raw SKILL.md content
39
+ * @param {string[]} projectTags - Stack tags from config
40
+ * @returns {boolean}
41
+ */
42
+ export function shouldInstallSkill(skillContent, projectTags) {
43
+ return shouldInstall(parseStacks(skillContent), projectTags);
44
+ }
45
+
35
46
  /**
36
47
  * Recursively copy a directory tree from src to dest.
37
48
  * Creates dest if it doesn't exist. Skips README.md files.
@@ -71,7 +82,7 @@ function copyDirectory(src, dest) {
71
82
  * @param {string} srcDir - Source directory to walk
72
83
  * @param {string} destDir - Destination base directory (.claude/skills/)
73
84
  */
74
- function installSkillsFromDir(srcDir, destDir) {
85
+ function installSkillsFromDir(srcDir, destDir, projectTags) {
75
86
  const entries = readdirSync(srcDir);
76
87
  for (const entry of entries) {
77
88
  const srcPath = join(srcDir, entry);
@@ -80,15 +91,19 @@ function installSkillsFromDir(srcDir, destDir) {
80
91
  if (stat.isDirectory()) {
81
92
  const skillMdPath = join(srcPath, 'SKILL.md');
82
93
  if (existsSync(skillMdPath)) {
83
- // Directory-based skill — copy entire directory including scripts/, references/, assets/
94
+ // Directory-based skill — check stack filter before copying
95
+ const content = readFileSync(skillMdPath, 'utf-8');
96
+ if (!shouldInstallSkill(content, projectTags)) continue;
84
97
  const skillDestDir = join(destDir, entry);
85
98
  copyDirectory(srcPath, skillDestDir);
86
99
  } else {
87
100
  // Level/category directory — recurse
88
- installSkillsFromDir(srcPath, destDir);
101
+ installSkillsFromDir(srcPath, destDir, projectTags);
89
102
  }
90
103
  } else if (entry.endsWith('.md') && entry !== 'README.md') {
91
- // Legacy flat .md skill — wrap in subdirectory as SKILL.md
104
+ // Legacy flat .md skill — check stack filter before copying
105
+ const content = readFileSync(srcPath, 'utf-8');
106
+ if (!shouldInstallSkill(content, projectTags)) continue;
92
107
  const skillName = entry.slice(0, -3);
93
108
  const skillDir = join(destDir, skillName);
94
109
  mkdirSync(skillDir, { recursive: true });
@@ -97,6 +112,28 @@ function installSkillsFromDir(srcDir, destDir) {
97
112
  }
98
113
  }
99
114
 
115
+ function collectSourceSkillNames() {
116
+ const names = new Set();
117
+ for (const level of SKILL_LEVELS_TO_INSTALL) {
118
+ const levelDir = join(FRAMEWORK_SKILLS_DIR, level);
119
+ if (!existsSync(levelDir)) continue;
120
+ collectNamesFromDir(levelDir, names);
121
+ }
122
+ return names;
123
+ }
124
+
125
+ function collectNamesFromDir(dir, names) {
126
+ for (const entry of readdirSync(dir)) {
127
+ const fullPath = join(dir, entry);
128
+ if (!statSync(fullPath).isDirectory()) continue;
129
+ if (existsSync(join(fullPath, 'SKILL.md'))) {
130
+ names.add(entry);
131
+ } else {
132
+ collectNamesFromDir(fullPath, names);
133
+ }
134
+ }
135
+ }
136
+
100
137
  /**
101
138
  * Install morph framework skills to .claude/skills/ in the target project.
102
139
  * Skills are copied into subdirectories as SKILL.md files so Claude Code can
@@ -104,15 +141,38 @@ function installSkillsFromDir(srcDir, destDir) {
104
141
  * /blazor-builder). Directory-based skills also get their scripts/,
105
142
  * references/, and assets/ subdirectories copied for on-demand loading.
106
143
  *
144
+ * Stack filtering: if projectTags is provided, only skills whose `stacks:`
145
+ * frontmatter matches the project tags are installed. Skills without a
146
+ * `stacks:` field are always installed (universal).
147
+ *
107
148
  * @param {string} projectDir - Target project root directory
149
+ * @param {Object} [options]
150
+ * @param {string[]} [options.projectTags=[]] - Stack tags from config
108
151
  */
109
- export async function installSkills(projectDir) {
152
+ export async function installSkills(projectDir, { projectTags = [] } = {}) {
110
153
  const claudeSkillsDir = join(projectDir, '.claude', 'skills');
111
154
  mkdirSync(claudeSkillsDir, { recursive: true });
112
155
 
113
156
  for (const level of SKILL_LEVELS_TO_INSTALL) {
114
157
  const levelDir = join(FRAMEWORK_SKILLS_DIR, level);
115
158
  if (!existsSync(levelDir)) continue;
116
- installSkillsFromDir(levelDir, claudeSkillsDir);
159
+ installSkillsFromDir(levelDir, claudeSkillsDir, projectTags);
160
+ }
161
+
162
+ // Collect installed morph-* skill names
163
+ const installed = new Set();
164
+ for (const entry of readdirSync(claudeSkillsDir)) {
165
+ if (existsSync(join(claudeSkillsDir, entry, 'SKILL.md'))) {
166
+ installed.add(entry);
167
+ }
168
+ }
169
+
170
+ // Cleanup orphan morph-* skill dirs that exist in source but were filtered out
171
+ const allSourceSkills = collectSourceSkillNames();
172
+ for (const entry of readdirSync(claudeSkillsDir)) {
173
+ if (!entry.startsWith('morph-')) continue;
174
+ if (!installed.has(entry) && allSourceSkills.has(entry)) {
175
+ rmSync(join(claudeSkillsDir, entry), { recursive: true, force: true });
176
+ }
117
177
  }
118
178
  }
package/CLAUDE.md DELETED
@@ -1,98 +0,0 @@
1
- # MORPH-SPEC Runtime Instructions
2
-
3
- > by Polymorphism Tech — Spec-driven development for .NET/Blazor/Next.js/Azure
4
-
5
- ---
6
-
7
- ## Project Context
8
-
9
- @.morph/context/README.md
10
-
11
- ---
12
-
13
- ## Critical Rules
14
-
15
- **NEVER:**
16
- - Skip to code without a specification
17
- - Implement without design approval
18
- - Ignore standards in `.morph/framework/standards/`
19
- - Create infrastructure manually
20
- - Generate code without defined contracts
21
- - List questions as plain text — always use the `AskUserQuestion` tool
22
-
23
- **ALWAYS:**
24
- - Follow the mandatory phases
25
- - Generate outputs in `.morph/features/{feature}/`
26
- - Document decisions in `decisions.md`
27
- - Checkpoint every 3 implemented tasks
28
- - Use Infrastructure as Code
29
- - Use `AskUserQuestion` tool whenever asking the user anything (1–4 questions per call; split into sequential calls if more)
30
-
31
- ---
32
-
33
- ## Quick Reference
34
-
35
- | Command | Purpose |
36
- |---------|---------|
37
- | `/morph-proposal {feature}` | Full spec pipeline (phases 1–4, pauses for approval) |
38
- | `/morph-apply {feature}` | Implement feature (phase 5) |
39
- | `/morph-status` | Feature status dashboard |
40
- | `/morph-preflight` | Pre-implementation validation |
41
-
42
- ---
43
-
44
- ## State & Outputs
45
-
46
- | Path | Notes |
47
- |------|-------|
48
- | `.morph/state.json` | **READ-ONLY** — use `morph-spec` CLI to update |
49
- | `.morph/features/{feature}/{phase}/` | Feature outputs organized by phase |
50
- | `.morph/framework/` | **READ-ONLY** — framework files managed by morph-spec |
51
- | `.morph/config/config.json` | Project configuration (editable) |
52
-
53
- ### mark-output types
54
-
55
- Use `morph-spec state mark-output <feature> <type>` with one of these exact type names:
56
-
57
- | Type | Phase | kebab alias |
58
- |------|-------|-------------|
59
- | `proposal` | proposal | — |
60
- | `schemaAnalysis` | design | `schema-analysis` |
61
- | `spec` | design | — |
62
- | `contracts` | design | — |
63
- | `contractsVsa` | design | `contracts-vsa` |
64
- | `decisions` | design | — |
65
- | `clarifications` | clarify | — |
66
- | `tasks` | tasks | — |
67
- | `uiDesignSystem` | uiux | `ui-design-system` |
68
- | `uiMockups` | uiux | `ui-mockups` |
69
- | `uiComponents` | uiux | `ui-components` |
70
- | `uiFlows` | uiux | `ui-flows` |
71
- | `recap` | implement | — |
72
- ---
73
-
74
- ## Phase Sequence
75
-
76
- ```
77
- proposal → setup → [uiux] → design → clarify → tasks → implement → [sync]
78
- ```
79
-
80
- Use `morph-spec status {feature}` to see current phase and pending approval gates.
81
-
82
- ---
83
-
84
- ## Agents
85
-
86
- Tier-1 and tier-2 MORPH agents are available as native subagents in `.claude/agents/`.
87
- They can be invoked directly by Claude Code during multi-agent workflows.
88
-
89
- ---
90
-
91
- ## Context Window Tip
92
-
93
- When using 3+ MCPs, add `"experimental": { "mcpCliMode": true }` to `.claude/settings.json`.
94
- MCP tools load on-demand instead of all at startup — keeps context clean for actual work.
95
-
96
- ---
97
-
98
- *MORPH-SPEC by Polymorphism Tech*