@polymorphism-tech/morph-spec 4.22.0 → 6.0.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 (211) hide show
  1. package/CLAUDE.md +57 -71
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +224 -491
  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 +55 -38
  8. package/framework/MORPH.md +244 -0
  9. package/framework/agents.json +127 -1198
  10. package/framework/commands/morph-apply.md +56 -240
  11. package/framework/commands/morph-archive.md +48 -85
  12. package/framework/commands/morph-preflight.md +7 -7
  13. package/framework/commands/morph-proposal.md +35 -191
  14. package/framework/commands/morph-status.md +19 -50
  15. package/framework/commands/morph-troubleshoot.md +2 -2
  16. package/framework/evals/rubrics/README.md +44 -0
  17. package/framework/evals/rubrics/architecture.md +46 -0
  18. package/framework/evals/rubrics/code-quality.md +50 -0
  19. package/framework/evals/rubrics/contracts.md +47 -0
  20. package/framework/evals/rubrics/test-coverage.md +50 -0
  21. package/framework/evals/scenarios/dotnet-crud.yaml +42 -0
  22. package/framework/evals/scenarios/nextjs-auth.yaml +38 -0
  23. package/framework/evals/suites/standard.yaml +5 -0
  24. package/framework/evals/trace-schema.md +67 -0
  25. package/framework/hooks/claude-code/core/README.md +3 -0
  26. package/framework/hooks/claude-code/core/denylist.json +14 -0
  27. package/framework/hooks/claude-code/{pre-tool-use → core}/enforce-phase-writes.js +5 -5
  28. package/framework/hooks/claude-code/core/post-edit-typecheck.js +69 -0
  29. package/framework/hooks/claude-code/core/pre-bash-denylist.js +29 -0
  30. package/framework/hooks/claude-code/{pre-tool-use → core}/protect-spec-files.js +4 -6
  31. package/framework/hooks/claude-code/{stop → core}/validate-completion.js +13 -82
  32. package/framework/hooks/shared/phase-utils.js +20 -31
  33. package/framework/hooks/shared/state-reader.js +4 -5
  34. package/framework/{skills/level-0-meta/mcp-registry.json → mcp/registry.json} +254 -243
  35. package/framework/phases.json +41 -115
  36. package/framework/rubrics/eval-rubric.json +66 -0
  37. package/framework/skills/README.md +39 -64
  38. package/framework/skills/core/README.md +3 -0
  39. package/framework/skills/core/morph-brainstorming/SKILL.md +102 -0
  40. package/framework/skills/core/morph-eval/SKILL.md +57 -0
  41. package/framework/skills/core/morph-implement/SKILL.md +83 -0
  42. package/framework/skills/core/morph-plan/SKILL.md +64 -0
  43. package/framework/skills/core/morph-review/SKILL.md +62 -0
  44. package/framework/skills/core/morph-stack-scan/SKILL.md +109 -0
  45. package/framework/skills/core/morph-standards/SKILL.md +49 -0
  46. package/framework/templates/CLAUDE.md.core.guide.md +29 -0
  47. package/framework/templates/CLAUDE.md.core.template +22 -0
  48. package/framework/templates/REGISTRY.json +4 -4
  49. package/framework/workflows/configs/fast-track.json +5 -10
  50. package/framework/workflows/configs/full.json +4 -8
  51. package/framework/workflows/configs/quick.json +32 -0
  52. package/framework/workflows/configs/standard.json +57 -64
  53. package/package.json +7 -6
  54. package/src/commands/mcp/mcp-setup.js +58 -31
  55. package/src/commands/project/doctor.js +69 -70
  56. package/src/commands/project/init.js +26 -5
  57. package/src/commands/project/status.js +18 -19
  58. package/src/commands/state/approve.js +1 -1
  59. package/src/commands/validation/validate-feature.js +18 -112
  60. package/src/core/paths/output-schema.js +133 -152
  61. package/src/core/state/phase-state-machine.js +10 -24
  62. package/src/core/state/state-manager.js +17 -17
  63. package/src/core/workflows/workflow-detector.js +214 -31
  64. package/src/core/workflows/workflow-router.js +204 -0
  65. package/src/dashboard/app/pages/AgentMap.jsx +32 -33
  66. package/src/dashboard/app/pages/LiveState.jsx +1 -1
  67. package/src/dashboard/app/pages/PhasePipeline.jsx +2 -4
  68. package/src/dashboard/app/pages/SkillsLibrary.jsx +2 -3
  69. package/src/dashboard/app/pages/Workflows.jsx +2 -2
  70. package/src/dashboard/scanner/agents-scanner.js +30 -35
  71. package/src/lib/agents/metrics-aggregator.js +1 -1
  72. package/src/lib/agents/output-collector.js +7 -9
  73. package/src/lib/context/context-builder.js +289 -0
  74. package/src/lib/detectors/claude-config-detector.js +19 -3
  75. package/src/lib/generators/context-generator.js +20 -42
  76. package/src/lib/generators/index.js +1 -0
  77. package/src/lib/generators/mandate-generator.js +512 -0
  78. package/src/lib/generators/metadata-extractor.js +1 -1
  79. package/src/lib/generators/recap-generator.js +3 -4
  80. package/src/lib/installers/mcp-installer.js +300 -92
  81. package/src/lib/phase-chain/phase-validator.js +36 -40
  82. package/src/lib/stack/stack-profile.js +74 -91
  83. package/src/lib/tasks/task-parser.js +1 -1
  84. package/src/lib/tasks/wave-resolver.js +353 -0
  85. package/src/lib/validators/checkpoint-persistence.js +2 -2
  86. package/src/lib/validators/content/cross-phase-validator.js +281 -0
  87. package/src/lib/validators/content/index.js +1 -0
  88. package/src/lib/validators/shared/emit-validator-dispatch.js +6 -56
  89. package/src/scripts/global-install.js +2 -27
  90. package/src/utils/agents-installer.js +224 -447
  91. package/src/utils/claude-settings-manager.js +57 -15
  92. package/src/utils/file-copier.js +1 -0
  93. package/src/utils/hooks-installer.js +15 -117
  94. package/src/utils/skills-installer.js +12 -18
  95. package/bin/detect-agents.js +0 -226
  96. package/bin/task-manager.js +0 -1036
  97. package/framework/agents/README.md +0 -13
  98. package/framework/agents/ai-agents/ai-system-architect.md +0 -194
  99. package/framework/agents/ai-agents/vector-search-expert.md +0 -247
  100. package/framework/agents/architecture/architecture-expert.md +0 -98
  101. package/framework/agents/architecture/morph-spec-architect.md +0 -66
  102. package/framework/agents/architecture/packages-validator.md +0 -89
  103. package/framework/agents/architecture/security-expert.md +0 -79
  104. package/framework/agents/architecture/seo-growth-hacker.md +0 -168
  105. package/framework/agents/architecture/standards-architect.md +0 -153
  106. package/framework/agents/backend/dotnet-senior.md +0 -163
  107. package/framework/agents/backend/ef-modeler.md +0 -119
  108. package/framework/agents/backend/hangfire-orchestrator.md +0 -133
  109. package/framework/agents/backend/ms-agent-expert.md +0 -55
  110. package/framework/agents/backend/neon-expert.md +0 -379
  111. package/framework/agents/frontend/css-specialist.md +0 -89
  112. package/framework/agents/frontend/design-system-validator.md +0 -100
  113. package/framework/agents/frontend/gsap-animator.md +0 -131
  114. package/framework/agents/frontend/nextjs-component-validator.md +0 -97
  115. package/framework/agents/frontend/nextjs-expert.md +0 -201
  116. package/framework/agents/frontend/ui-designer.md +0 -259
  117. package/framework/agents/infrastructure/infra-architect.md +0 -215
  118. package/framework/agents/integrations/asaas-financial.md +0 -138
  119. package/framework/agents/integrations/neon-auth.md +0 -151
  120. package/framework/agents/integrations/resend-email.md +0 -127
  121. package/framework/hooks/claude-code/post-tool-use/agent-complete.js +0 -122
  122. package/framework/hooks/claude-code/post-tool-use/dispatch.js +0 -363
  123. package/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +0 -78
  124. package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +0 -194
  125. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +0 -199
  126. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +0 -55
  127. package/framework/hooks/claude-code/session-start/inject-morph-context.js +0 -323
  128. package/framework/hooks/claude-code/session-start/post-compact-restore.js +0 -41
  129. package/framework/hooks/claude-code/session-start/resume-context.js +0 -77
  130. package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +0 -90
  131. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +0 -114
  132. package/framework/hooks/shared/compact-restore.js +0 -123
  133. package/framework/hooks/shared/dispatch-helpers.js +0 -147
  134. package/framework/hooks/shared/skill-reminder-helpers.js +0 -79
  135. package/framework/hooks/shared/stale-task-reset.js +0 -57
  136. package/framework/hooks/shared/worktree-helpers.js +0 -75
  137. package/framework/skills/level-0-meta/README.md +0 -7
  138. package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +0 -472
  139. package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +0 -174
  140. package/framework/skills/level-0-meta/morph-brainstorming/references/proposal-example.md +0 -138
  141. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +0 -145
  142. package/framework/skills/level-0-meta/morph-code-review/SKILL.md +0 -250
  143. package/framework/skills/level-0-meta/morph-code-review/references/review-example.md +0 -164
  144. package/framework/skills/level-0-meta/morph-code-review/references/review-guidelines.md +0 -100
  145. package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +0 -151
  146. package/framework/skills/level-0-meta/morph-code-review-nextjs/SKILL.md +0 -165
  147. package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +0 -254
  148. package/framework/skills/level-0-meta/morph-code-review-nextjs/scripts/scan-nextjs.mjs +0 -189
  149. package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +0 -344
  150. package/framework/skills/level-0-meta/morph-frontend-review/scripts/scan-accessibility.mjs +0 -376
  151. package/framework/skills/level-0-meta/morph-gemini-image/SKILL.md +0 -290
  152. package/framework/skills/level-0-meta/morph-init/SKILL.md +0 -537
  153. package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +0 -225
  154. package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +0 -401
  155. package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-dev-server.mjs +0 -153
  156. package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-stack.mjs +0 -234
  157. package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +0 -106
  158. package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +0 -243
  159. package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +0 -169
  160. package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +0 -148
  161. package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +0 -110
  162. package/framework/skills/level-1-workflows/README.md +0 -7
  163. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +0 -269
  164. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +0 -453
  165. package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +0 -44
  166. package/framework/skills/level-1-workflows/morph-phase-design/references/spec-authoring-guide.md +0 -55
  167. package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +0 -253
  168. package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
  169. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +0 -634
  170. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +0 -50
  171. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +0 -54
  172. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +0 -47
  173. package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +0 -132
  174. package/framework/skills/level-1-workflows/morph-phase-implement/references/vsa-implementation-guide.md +0 -92
  175. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +0 -257
  176. package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
  177. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +0 -291
  178. package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -143
  179. package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +0 -404
  180. package/framework/skills/level-1-workflows/morph-phase-tasks/scripts/validate-tasks.mjs +0 -112
  181. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +0 -995
  182. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +0 -147
  183. package/framework/squad-templates/backend-only.json +0 -47
  184. package/framework/squad-templates/frontend-only.json +0 -56
  185. package/framework/squad-templates/full-stack.json +0 -74
  186. package/src/commands/agents/agent-metrics.js +0 -174
  187. package/src/commands/agents/audit-dispatch.js +0 -82
  188. package/src/commands/agents/detect-agents.js +0 -275
  189. package/src/commands/agents/dispatch-agents.js +0 -461
  190. package/src/commands/agents/index.js +0 -13
  191. package/src/commands/agents/manage-agents.js +0 -352
  192. package/src/commands/agents/project-metrics.js +0 -74
  193. package/src/commands/phase/phase-reset.js +0 -74
  194. package/src/commands/scope/escalate.js +0 -217
  195. package/src/commands/state/advance-phase.js +0 -600
  196. package/src/commands/state/phase-runner.js +0 -215
  197. package/src/commands/state/state.js +0 -444
  198. package/src/commands/task/expand.js +0 -100
  199. package/src/commands/tasks/index.js +0 -4
  200. package/src/commands/tasks/task.js +0 -105
  201. package/src/commands/templates/index.js +0 -5
  202. package/src/commands/templates/template-render.js +0 -93
  203. package/src/commands/trust/trust.js +0 -118
  204. package/src/lib/agents/agents-manager.js +0 -349
  205. package/src/lib/build/build-runner.js +0 -99
  206. package/src/lib/checkpoints/checkpoint-hooks.js +0 -111
  207. package/src/lib/contracts/sprint-contract-generator.js +0 -137
  208. package/src/lib/scope/impact-analyzer.js +0 -106
  209. package/src/lib/trust/trust-manager.js +0 -157
  210. package/src/utils/tier-helpers.js +0 -24
  211. /package/framework/hooks/claude-code/{post-tool-use → core}/loop-detect.js +0 -0
@@ -165,13 +165,36 @@ export async function resetMorphSettings(targetPath) {
165
165
  }
166
166
  }
167
167
 
168
- // Remove managed MCP servers
169
- if (managed.mcps_installed && settings.mcpServers) {
170
- for (const mcpName of managed.mcps_installed) {
171
- delete settings.mcpServers[mcpName];
168
+ // Remove managed MCP servers from .mcp.json (primary location)
169
+ if (managed.mcps_installed) {
170
+ const mcpJson = await loadMcpJson(targetPath);
171
+ if (mcpJson.mcpServers) {
172
+ for (const mcpName of managed.mcps_installed) {
173
+ delete mcpJson.mcpServers[mcpName];
174
+ }
175
+ if (Object.keys(mcpJson.mcpServers).length === 0) {
176
+ delete mcpJson.mcpServers;
177
+ }
178
+ // Write back (or remove file if empty)
179
+ if (Object.keys(mcpJson).length === 0) {
180
+ const mcpJsonPath = join(targetPath, '.mcp.json');
181
+ if (existsSync(mcpJsonPath)) {
182
+ const { unlink } = await import('fs/promises');
183
+ await unlink(mcpJsonPath);
184
+ }
185
+ } else {
186
+ await saveMcpJson(targetPath, mcpJson);
187
+ }
172
188
  }
173
- if (Object.keys(settings.mcpServers).length === 0) {
174
- delete settings.mcpServers;
189
+
190
+ // Also clean legacy entries from settings.local.json
191
+ if (settings.mcpServers) {
192
+ for (const mcpName of managed.mcps_installed) {
193
+ delete settings.mcpServers[mcpName];
194
+ }
195
+ if (Object.keys(settings.mcpServers).length === 0) {
196
+ delete settings.mcpServers;
197
+ }
175
198
  }
176
199
  }
177
200
 
@@ -192,31 +215,35 @@ export async function resetMorphSettings(targetPath) {
192
215
  }
193
216
 
194
217
  /**
195
- * Install MCP server entries into .claude/settings.local.json
196
- * Tracks installed MCPs in _morph_managed.mcps_installed for safe reset.
218
+ * Install MCP server entries into .mcp.json at the project root.
219
+ * Claude Code reads MCP servers from .mcp.json (not settings.local.json).
220
+ * Tracks installed names in settings.local.json _morph_managed.mcps_installed for safe reset.
197
221
  *
198
222
  * @param {string} targetPath - Project root directory
199
- * @param {Object} servers - Map of server name → config { command, args, env? }
223
+ * @param {Object} servers - Map of server name → config { type?, command, args, env? }
200
224
  * @returns {Promise<Object>} Summary of changes { added, skipped, total }
201
225
  */
202
226
  export async function installMcpServers(targetPath, servers) {
203
- const settings = await loadSettings(targetPath);
227
+ const mcpJson = await loadMcpJson(targetPath);
204
228
 
205
- settings.mcpServers = settings.mcpServers || {};
229
+ mcpJson.mcpServers = mcpJson.mcpServers || {};
206
230
 
207
231
  const added = [];
208
232
  const skipped = [];
209
233
 
210
234
  for (const [name, config] of Object.entries(servers)) {
211
- if (settings.mcpServers[name]) {
235
+ if (mcpJson.mcpServers[name]) {
212
236
  skipped.push(name);
213
237
  } else {
214
- settings.mcpServers[name] = config;
238
+ mcpJson.mcpServers[name] = config;
215
239
  added.push(name);
216
240
  }
217
241
  }
218
242
 
219
- // Track in _morph_managed
243
+ await saveMcpJson(targetPath, mcpJson);
244
+
245
+ // Track in settings.local.json _morph_managed
246
+ const settings = await loadSettings(targetPath);
220
247
  settings[MORPH_MANAGED_KEY] = settings[MORPH_MANAGED_KEY] || {};
221
248
  settings[MORPH_MANAGED_KEY].mcps_installed =
222
249
  settings[MORPH_MANAGED_KEY].mcps_installed || [];
@@ -229,13 +256,28 @@ export async function installMcpServers(targetPath, servers) {
229
256
 
230
257
  await saveSettings(targetPath, settings);
231
258
 
232
- return { added, skipped, total: Object.keys(settings.mcpServers).length };
259
+ return { added, skipped, total: Object.keys(mcpJson.mcpServers).length };
233
260
  }
234
261
 
235
262
  // ============================================================================
236
263
  // Helpers
237
264
  // ============================================================================
238
265
 
266
+ async function loadMcpJson(targetPath) {
267
+ const mcpJsonPath = join(targetPath, '.mcp.json');
268
+ try {
269
+ if (!existsSync(mcpJsonPath)) return {};
270
+ return JSON.parse(await readFile(mcpJsonPath, 'utf-8'));
271
+ } catch {
272
+ return {};
273
+ }
274
+ }
275
+
276
+ async function saveMcpJson(targetPath, data) {
277
+ const mcpJsonPath = join(targetPath, '.mcp.json');
278
+ await writeFile(mcpJsonPath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
279
+ }
280
+
239
281
  async function loadSettings(targetPath) {
240
282
  const settingsPath = join(targetPath, '.claude', 'settings.local.json');
241
283
  try {
@@ -142,6 +142,7 @@ export async function updateGitignore(projectPath) {
142
142
  '.morph/memory/',
143
143
  '.morph/plans/',
144
144
  '.morph/analytics/',
145
+ '.morph/traces/',
145
146
  '.claude/commands/',
146
147
  '.claude/rules/',
147
148
  '.claude/skills/',
@@ -50,164 +50,62 @@ const MORPH_PERMISSIONS = [
50
50
  * Each entry maps to a Claude Code hook event.
51
51
  */
52
52
  const MORPH_HOOKS = [
53
- // === SessionStart ===
54
- {
55
- event: 'SessionStart',
56
- matcher: 'startup|resume|compact',
57
- hooks: [{
58
- type: 'command',
59
- command: 'node framework/hooks/claude-code/session-start/inject-morph-context.js'
60
- }]
61
- },
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
-
73
- // === SessionStart: resume only — orientation context ===
74
- {
75
- event: 'SessionStart',
76
- matcher: 'resume',
77
- hooks: [{
78
- type: 'command',
79
- command: 'node framework/hooks/claude-code/session-start/resume-context.js'
80
- }]
81
- },
82
-
83
- // === UserPromptSubmit ===
84
- {
85
- event: 'UserPromptSubmit',
86
- matcher: null,
87
- hooks: [
88
- {
89
- type: 'command',
90
- command: 'node framework/hooks/claude-code/user-prompt/enrich-prompt.js'
91
- },
92
- ]
93
- },
94
-
95
- // === PreToolUse: Write|Edit ===
53
+ // === PreToolUse: Write|Edit — phase + spec protection (silent on success) ===
96
54
  {
97
55
  event: 'PreToolUse',
98
56
  matcher: 'Write|Edit',
99
57
  hooks: [
100
- // Note: protect-readonly-files.js replaced by native permissions.deny (see MORPH_PERMISSIONS)
101
- // Order matters: cheapest check first
102
58
  {
103
59
  type: 'command',
104
- command: 'node framework/hooks/claude-code/pre-tool-use/protect-spec-files.js'
60
+ command: 'node framework/hooks/claude-code/core/protect-spec-files.js'
105
61
  },
106
62
  {
107
63
  type: 'command',
108
- command: 'node framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js'
109
- },
110
- {
111
- type: 'command',
112
- command: 'node framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js'
64
+ command: 'node framework/hooks/claude-code/core/enforce-phase-writes.js'
113
65
  }
114
66
  ]
115
67
  },
116
68
 
117
- // === PreToolUse: Bash ===
69
+ // === PreToolUse: Bash — denylist (irreversible commands only) ===
118
70
  {
119
71
  event: 'PreToolUse',
120
72
  matcher: 'Bash',
121
- hooks: [{
122
- type: 'prompt',
123
- prompt: `You are a guard for a morph-spec project. Read the bash command from the hook input and check EXACTLY these 3 conditions. IMPORTANT: reading files (cat, head, tail, grep, less, bat) is NEVER blocked regardless of path.
124
- 1. Does the command DELETE .morph/ or .morph/state.json or .morph/framework/ using rm -r, rm -rf, or rmdir? BLOCK only these exact paths. NOTE: deleting .morph/features/ subdirectories is NOT blocked.
125
- 2. Does the command OVERWRITE .morph/state.json using a shell redirect (>) combined with sed, jq, awk, echo, cat, or printf? If yes, BLOCK.
126
- 3. Does the command WRITE TO .morph/framework/ via a shell redirect (>) or Node.js fs.writeFileSync/appendFileSync on a path under .morph/framework/? READING .morph/framework/ is always allowed. Writing to .morph/features/, .morph/config/, .morph/context/ is allowed.
127
- If ANY of these 3 specific conditions matches, respond: {"ok": false, "reason": "MORPH-SPEC: [explain what was blocked]. To read agents/state use: morph-spec state get <feature> | morph-spec dispatch-agents <feature> <phase> --table | cat .morph/framework/agents.json (reading is always allowed). To update state use: morph-spec state set <feature> <key> <value>."}
128
- Otherwise respond: {"ok": true}`
129
- }]
130
- },
131
-
132
- // === PostToolUse: Agent ===
133
- {
134
- event: 'PostToolUse',
135
- matcher: 'Agent',
136
- hooks: [{
137
- type: 'command',
138
- command: 'node framework/hooks/claude-code/post-tool-use/agent-complete.js'
139
- }]
140
- },
141
-
142
- // === PostToolUse: Bash ===
143
- {
144
- event: 'PostToolUse',
145
- matcher: 'Bash',
146
73
  hooks: [
147
74
  {
148
75
  type: 'command',
149
- command: 'node framework/hooks/claude-code/post-tool-use/dispatch.js'
150
- },
151
- {
152
- type: 'command',
153
- command: 'node framework/hooks/claude-code/post-tool-use/validator-feedback.js'
76
+ command: 'node framework/hooks/claude-code/core/pre-bash-denylist.js'
154
77
  }
155
78
  ]
156
79
  },
157
80
 
158
- // === PostToolUse: All — loop detection ===
81
+ // === PostToolUse: Write|Edit — typecheck (debounced, silent on success) ===
159
82
  {
160
83
  event: 'PostToolUse',
161
- matcher: null,
162
- hooks: [{
163
- type: 'command',
164
- command: 'node framework/hooks/claude-code/post-tool-use/loop-detect.js',
165
- timeout: 5000
166
- }]
167
- },
168
-
169
- // === Stop ===
170
- {
171
- event: 'Stop',
172
- matcher: null,
173
- hooks: [{
174
- type: 'command',
175
- command: 'node framework/hooks/claude-code/stop/validate-completion.js'
176
- }]
177
- },
178
-
179
- // === PreCompact ===
180
- {
181
- event: 'PreCompact',
182
- matcher: null,
84
+ matcher: 'Write|Edit',
183
85
  hooks: [{
184
86
  type: 'command',
185
- command: 'node framework/hooks/claude-code/pre-compact/save-morph-context.js'
87
+ command: 'node framework/hooks/claude-code/core/post-edit-typecheck.js'
186
88
  }]
187
89
  },
188
90
 
189
- // === PostToolUseFailure ===
91
+ // === PostToolUse: All — loop detection ===
190
92
  {
191
- event: 'PostToolUseFailure',
93
+ event: 'PostToolUse',
192
94
  matcher: null,
193
95
  hooks: [{
194
96
  type: 'command',
195
- command: 'node framework/hooks/claude-code/post-tool-use/handle-tool-failure.js',
196
- timeout: 10000,
97
+ command: 'node framework/hooks/claude-code/core/loop-detect.js',
98
+ timeout: 5000
197
99
  }]
198
100
  },
199
101
 
200
- // === TeammateIdle ===
201
- // Fires when an Agent Team teammate finishes and is about to go idle.
202
- // Exit 2 = send feedback to teammate to continue working (validation failed).
203
- // Exit 0 = teammate may become idle (validation passed).
204
- // Requires CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 (set in env above).
102
+ // === Stop — build check + session snapshot ===
205
103
  {
206
- event: 'TeammateIdle',
104
+ event: 'Stop',
207
105
  matcher: null,
208
106
  hooks: [{
209
107
  type: 'command',
210
- command: 'node framework/hooks/claude-code/teammate-idle/teammate-idle.js'
108
+ command: 'node framework/hooks/claude-code/core/validate-completion.js'
211
109
  }]
212
110
  }
213
111
  ];
@@ -2,18 +2,16 @@
2
2
  * Skills Installer
3
3
  *
4
4
  * Copies framework skills to .claude/skills/ during morph-spec init.
5
- * Installs level-0-meta and level-1-workflows skills using the native Claude Code
6
- * subdirectory format: .claude/skills/{skill-name}/SKILL.md (plus optional
7
- * scripts/, references/, and assets/ subdirectories).
8
- * Level-2 domain specialists are installed as native subagents via installDomainAgents().
9
- * Level-3 and level-4 only contain README.md placeholder files and are skipped.
5
+ * Installs only the `core/` skill set — the canonical NLH skills referenced by
6
+ * MORPH.md (morph-brainstorming, morph-stack-scan, morph-plan, morph-implement,
7
+ * morph-eval, morph-review, morph-standards). The 3-tier system was collapsed
8
+ * into a single source of truth in framework/skills/core/.
10
9
  *
11
- * Source skills can be either:
12
- * - Directory-based: framework/skills/{level}/{skill-name}/SKILL.md (preferred)
13
- * - Flat file: framework/skills/{level}/{skill-name}.md (legacy, still supported)
10
+ * Source skills are directory-based: framework/skills/core/{skill-name}/SKILL.md
11
+ * (plus optional scripts/, references/, assets/ subdirectories).
14
12
  *
15
- * Directory-based skills have their entire directory copied (scripts/, references/,
16
- * assets/) so Claude Code can execute scripts and load references on demand.
13
+ * Directory-based skills have their entire directory copied so Claude Code can
14
+ * execute scripts and load references on demand.
17
15
  */
18
16
 
19
17
  import { mkdirSync, copyFileSync, existsSync, readdirSync, statSync, readFileSync, rmSync } from 'fs';
@@ -26,12 +24,9 @@ const FRAMEWORK_SKILLS_DIR = join(__dirname, '..', '..', 'framework', 'skills');
26
24
 
27
25
  /**
28
26
  * Skill levels to install in .claude/skills/.
29
- * Level-0 meta skills (planning, reviewing) and level-1 workflow skills
30
- * (phase-design, phase-implement, etc.) are installed. Level-2 domain
31
- * specialists are installed as native subagents via installDomainAgents()
32
- * instead. Level-3 and level-4 are skipped (no content yet).
27
+ * Only `core/` survives — single source of truth aligned with MORPH.md.
33
28
  */
34
- const SKILL_LEVELS_TO_INSTALL = ['level-0-meta', 'level-1-workflows'];
29
+ const SKILL_LEVELS_TO_INSTALL = ['core'];
35
30
 
36
31
  /**
37
32
  * Check if a skill should be installed based on its stacks: frontmatter.
@@ -75,9 +70,8 @@ function copyDirectory(src, dest) {
75
70
  * 2. Flat .md skill (legacy): srcDir/{skill-name}.md
76
71
  * → destDir/{skill-name}/SKILL.md
77
72
  *
78
- * Level directories (level-0-meta, level-1-workflows, sub-categories) are
79
- * traversed recursively. A directory is recognized as a skill directory when
80
- * it contains a SKILL.md file.
73
+ * The `core/` level directory is traversed recursively. A directory is
74
+ * recognized as a skill directory when it contains a SKILL.md file.
81
75
  *
82
76
  * @param {string} srcDir - Source directory to walk
83
77
  * @param {string} destDir - Destination base directory (.claude/skills/)
@@ -1,226 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * MORPH-SPEC Agent Detection CLI
5
- *
6
- * Detecta automaticamente quais agentes devem ser ativados baseado
7
- * em keywords no input do usuário.
8
- *
9
- * Uso:
10
- * node bin/detect-agents.js "implementar jobs agendados"
11
- * node bin/detect-agents.js --verbose "criar dashboard com charts"
12
- * node bin/detect-agents.js --json "adicionar background tasks"
13
- */
14
-
15
- import { readFileSync } from 'fs';
16
- import { join, dirname } from 'path';
17
- import { fileURLToPath } from 'url';
18
-
19
- const __dirname = dirname(fileURLToPath(import.meta.url));
20
- const frameworkRoot = join(__dirname, '..');
21
- const AGENTS_CONFIG_PATH = join(frameworkRoot, '.morph/framework/agents.json');
22
-
23
- // Cores para output no terminal
24
- const colors = {
25
- reset: '\x1b[0m',
26
- bright: '\x1b[1m',
27
- green: '\x1b[32m',
28
- yellow: '\x1b[33m',
29
- blue: '\x1b[34m',
30
- cyan: '\x1b[36m',
31
- gray: '\x1b[90m'
32
- };
33
-
34
- function log(message, color = 'reset') {
35
- console.log(`${colors[color]}${message}${colors.reset}`);
36
- }
37
-
38
- /**
39
- * Carrega configuração de agentes
40
- * @returns {Object} Configuração de agentes
41
- */
42
- function loadAgentsConfig() {
43
- try {
44
- const content = readFileSync(AGENTS_CONFIG_PATH, 'utf8');
45
- return JSON.parse(content);
46
- } catch (err) {
47
- console.error(`ERROR: Failed to load agents config: ${err.message}`);
48
- process.exit(1);
49
- }
50
- }
51
-
52
- /**
53
- * Detecta agentes baseado em user input
54
- * @param {string} userInput - Input do usuário
55
- * @param {Object} config - Configuração de agentes
56
- * @returns {Object} Resultado da detecção { core, specialists, all, matches }
57
- */
58
- function detectAgents(userInput, config) {
59
- const input = userInput.toLowerCase();
60
- const coreAgents = config.agents.core || [];
61
- const specialists = config.agents.specialists || [];
62
-
63
- // Core agents sempre ativos
64
- const coreIds = coreAgents.map(a => a.id);
65
-
66
- // Detectar specialists por keyword matching
67
- const specialistMatches = [];
68
-
69
- for (const agent of specialists) {
70
- const keywords = agent.autoActivation?.keywords || [];
71
- const matchedKeywords = [];
72
-
73
- for (const keyword of keywords) {
74
- if (input.includes(keyword.toLowerCase())) {
75
- matchedKeywords.push(keyword);
76
- }
77
- }
78
-
79
- if (matchedKeywords.length > 0) {
80
- specialistMatches.push({
81
- id: agent.id,
82
- name: agent.name,
83
- emoji: agent.emoji,
84
- matchedKeywords: matchedKeywords,
85
- matchCount: matchedKeywords.length
86
- });
87
- }
88
- }
89
-
90
- // Ordenar por número de matches (mais relevante primeiro)
91
- specialistMatches.sort((a, b) => b.matchCount - a.matchCount);
92
-
93
- const specialistIds = specialistMatches.map(m => m.id);
94
- const allIds = [...new Set([...coreIds, ...specialistIds])];
95
-
96
- return {
97
- core: coreIds,
98
- specialists: specialistIds,
99
- all: allIds,
100
- matches: specialistMatches,
101
- input: userInput
102
- };
103
- }
104
-
105
- /**
106
- * Formata output em modo verbose
107
- * @param {Object} result - Resultado da detecção
108
- */
109
- function formatVerbose(result) {
110
- log('\n╔════════════════════════════════════════════════════════════════╗', 'cyan');
111
- log('║ AGENT DETECTION RESULT ║', 'cyan');
112
- log('╠════════════════════════════════════════════════════════════════╣', 'cyan');
113
- log(`║ Input: ${result.input.substring(0, 54).padEnd(54)} ║`, 'bright');
114
- log('╠════════════════════════════════════════════════════════════════╣', 'cyan');
115
- log('║ CORE AGENTS (always active) ║', 'cyan');
116
- log('╠════════════════════════════════════════════════════════════════╣', 'cyan');
117
-
118
- result.core.forEach(id => {
119
- log(`║ ✓ ${id.padEnd(58)}║`, 'green');
120
- });
121
-
122
- if (result.specialists.length > 0) {
123
- log('╠════════════════════════════════════════════════════════════════╣', 'cyan');
124
- log('║ SPECIALIST AGENTS (keyword matched) ║', 'cyan');
125
- log('╠════════════════════════════════════════════════════════════════╣', 'cyan');
126
-
127
- result.matches.forEach(match => {
128
- const line = `${match.emoji} ${match.name} (${match.matchCount} matches)`;
129
- log(`║ ${line.padEnd(61)}║`, 'yellow');
130
- const keywords = match.matchedKeywords.join(', ');
131
- const keywordLine = `Keywords: ${keywords}`;
132
- // Quebrar em múltiplas linhas se necessário
133
- if (keywordLine.length > 58) {
134
- const chunks = keywordLine.match(/.{1,58}/g) || [];
135
- chunks.forEach((chunk, i) => {
136
- log(`║ ${chunk.padEnd(59)}║`, 'gray');
137
- });
138
- } else {
139
- log(`║ ${keywordLine.padEnd(59)}║`, 'gray');
140
- }
141
- });
142
- } else {
143
- log('╠════════════════════════════════════════════════════════════════╣', 'cyan');
144
- log('║ No specialist agents matched ║', 'gray');
145
- }
146
-
147
- log('╠════════════════════════════════════════════════════════════════╣', 'cyan');
148
- log(`║ TOTAL: ${result.all.length} agents activated ║`, 'bright');
149
- log('╚════════════════════════════════════════════════════════════════╝\n', 'cyan');
150
- }
151
-
152
- /**
153
- * Formata output em modo simples (apenas IDs)
154
- * @param {Object} result - Resultado da detecção
155
- */
156
- function formatSimple(result) {
157
- result.all.forEach(id => console.log(id));
158
- }
159
-
160
- /**
161
- * Formata output em modo JSON
162
- * @param {Object} result - Resultado da detecção
163
- */
164
- function formatJSON(result) {
165
- console.log(JSON.stringify(result, null, 2));
166
- }
167
-
168
- // ============================================================================
169
- // MAIN
170
- // ============================================================================
171
-
172
- function main() {
173
- const args = process.argv.slice(2);
174
-
175
- // Parse flags
176
- const hasVerbose = args.includes('--verbose') || args.includes('-v');
177
- const hasJSON = args.includes('--json') || args.includes('-j');
178
- const hasHelp = args.includes('--help') || args.includes('-h');
179
- const hasIdsOnly = args.includes('--ids-only') || args.includes('-i');
180
-
181
- // Remove flags para pegar user input
182
- const userInput = args
183
- .filter(arg => !arg.startsWith('--') && !arg.startsWith('-'))
184
- .join(' ');
185
-
186
- if (hasHelp || args.length === 0) {
187
- log('\nMORPH-SPEC Agent Detection CLI', 'bright');
188
- log('\nUsage:', 'cyan');
189
- log(' node bin/detect-agents.js [options] "<user input>"', 'gray');
190
- log('\nOptions:', 'cyan');
191
- log(' -v, --verbose Show detailed detection results', 'gray');
192
- log(' -j, --json Output full result as JSON', 'gray');
193
- log(' -i, --ids-only Output only agent IDs (one per line)', 'gray');
194
- log(' -h, --help Show this help', 'gray');
195
- log('\nExamples:', 'cyan');
196
- log(' node bin/detect-agents.js "criar dashboard com charts"', 'gray');
197
- log(' node bin/detect-agents.js --verbose "implementar jobs agendados"', 'gray');
198
- log(' node bin/detect-agents.js --json "adicionar RAG pipeline"', 'gray');
199
- log(' node bin/detect-agents.js --ids-only "background tasks"\n', 'gray');
200
- process.exit(0);
201
- }
202
-
203
- if (!userInput) {
204
- console.error('ERROR: User input is required');
205
- console.error('Run with --help for usage information');
206
- process.exit(1);
207
- }
208
-
209
- // Carregar config e detectar agentes
210
- const config = loadAgentsConfig();
211
- const result = detectAgents(userInput, config);
212
-
213
- // Formatar output
214
- if (hasJSON) {
215
- formatJSON(result);
216
- } else if (hasVerbose) {
217
- formatVerbose(result);
218
- } else if (hasIdsOnly) {
219
- formatSimple(result);
220
- } else {
221
- // Default: JSON compact (só all array)
222
- console.log(JSON.stringify(result.all));
223
- }
224
- }
225
-
226
- main();