@polymorphism-tech/morph-spec 4.21.0 → 5.0.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 (213) 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 -1183
  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 -28
  33. package/framework/hooks/shared/state-reader.js +4 -5
  34. package/framework/{skills/level-0-meta/mcp-registry.json → mcp/registry.json} +254 -210
  35. package/framework/phases.json +40 -114
  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/standards/STANDARDS.json +32 -0
  47. package/framework/standards/frontend/design-system/ai-image-generation.md +244 -0
  48. package/framework/standards/integration/mcp/mcp-tools.md +48 -0
  49. package/framework/templates/CLAUDE.md.core.guide.md +29 -0
  50. package/framework/templates/CLAUDE.md.core.template +22 -0
  51. package/framework/templates/REGISTRY.json +4 -4
  52. package/framework/workflows/configs/fast-track.json +5 -10
  53. package/framework/workflows/configs/full.json +4 -8
  54. package/framework/workflows/configs/quick.json +32 -0
  55. package/framework/workflows/configs/standard.json +57 -64
  56. package/package.json +7 -6
  57. package/src/commands/mcp/mcp-setup.js +58 -31
  58. package/src/commands/project/doctor.js +69 -70
  59. package/src/commands/project/init.js +26 -5
  60. package/src/commands/project/status.js +18 -19
  61. package/src/commands/state/approve.js +1 -1
  62. package/src/commands/validation/validate-feature.js +18 -112
  63. package/src/core/paths/output-schema.js +133 -151
  64. package/src/core/state/phase-state-machine.js +10 -24
  65. package/src/core/state/state-manager.js +17 -17
  66. package/src/core/workflows/workflow-detector.js +214 -31
  67. package/src/core/workflows/workflow-router.js +204 -0
  68. package/src/dashboard/app/pages/AgentMap.jsx +32 -33
  69. package/src/dashboard/app/pages/LiveState.jsx +1 -1
  70. package/src/dashboard/app/pages/PhasePipeline.jsx +2 -4
  71. package/src/dashboard/app/pages/SkillsLibrary.jsx +2 -3
  72. package/src/dashboard/app/pages/Workflows.jsx +2 -2
  73. package/src/dashboard/scanner/agents-scanner.js +30 -35
  74. package/src/lib/agents/metrics-aggregator.js +1 -1
  75. package/src/lib/agents/output-collector.js +7 -8
  76. package/src/lib/context/context-builder.js +289 -0
  77. package/src/lib/detectors/claude-config-detector.js +19 -3
  78. package/src/lib/generators/context-generator.js +20 -42
  79. package/src/lib/generators/index.js +1 -0
  80. package/src/lib/generators/mandate-generator.js +512 -0
  81. package/src/lib/generators/metadata-extractor.js +1 -1
  82. package/src/lib/generators/recap-generator.js +3 -4
  83. package/src/lib/installers/mcp-installer.js +300 -92
  84. package/src/lib/phase-chain/phase-validator.js +36 -40
  85. package/src/lib/stack/stack-profile.js +74 -91
  86. package/src/lib/tasks/task-parser.js +1 -1
  87. package/src/lib/tasks/wave-resolver.js +353 -0
  88. package/src/lib/validators/checkpoint-persistence.js +2 -2
  89. package/src/lib/validators/content/cross-phase-validator.js +281 -0
  90. package/src/lib/validators/content/index.js +1 -0
  91. package/src/lib/validators/shared/emit-validator-dispatch.js +6 -56
  92. package/src/scripts/global-install.js +2 -27
  93. package/src/utils/agents-installer.js +224 -447
  94. package/src/utils/claude-settings-manager.js +57 -15
  95. package/src/utils/file-copier.js +1 -0
  96. package/src/utils/hooks-installer.js +15 -117
  97. package/src/utils/skills-installer.js +12 -18
  98. package/bin/detect-agents.js +0 -226
  99. package/bin/task-manager.js +0 -1036
  100. package/framework/agents/README.md +0 -13
  101. package/framework/agents/ai-agents/ai-system-architect.md +0 -194
  102. package/framework/agents/ai-agents/vector-search-expert.md +0 -247
  103. package/framework/agents/architecture/architecture-expert.md +0 -98
  104. package/framework/agents/architecture/morph-spec-architect.md +0 -66
  105. package/framework/agents/architecture/packages-validator.md +0 -89
  106. package/framework/agents/architecture/security-expert.md +0 -79
  107. package/framework/agents/architecture/seo-growth-hacker.md +0 -168
  108. package/framework/agents/architecture/standards-architect.md +0 -153
  109. package/framework/agents/backend/dotnet-senior.md +0 -163
  110. package/framework/agents/backend/ef-modeler.md +0 -119
  111. package/framework/agents/backend/hangfire-orchestrator.md +0 -133
  112. package/framework/agents/backend/ms-agent-expert.md +0 -55
  113. package/framework/agents/backend/neon-expert.md +0 -379
  114. package/framework/agents/frontend/css-specialist.md +0 -89
  115. package/framework/agents/frontend/design-system-validator.md +0 -100
  116. package/framework/agents/frontend/gsap-animator.md +0 -131
  117. package/framework/agents/frontend/nextjs-component-validator.md +0 -97
  118. package/framework/agents/frontend/nextjs-expert.md +0 -201
  119. package/framework/agents/frontend/ui-designer.md +0 -259
  120. package/framework/agents/infrastructure/infra-architect.md +0 -215
  121. package/framework/agents/integrations/asaas-financial.md +0 -138
  122. package/framework/agents/integrations/neon-auth.md +0 -151
  123. package/framework/agents/integrations/resend-email.md +0 -127
  124. package/framework/hooks/claude-code/post-tool-use/agent-complete.js +0 -122
  125. package/framework/hooks/claude-code/post-tool-use/dispatch.js +0 -363
  126. package/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +0 -78
  127. package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +0 -194
  128. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +0 -199
  129. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +0 -55
  130. package/framework/hooks/claude-code/session-start/inject-morph-context.js +0 -323
  131. package/framework/hooks/claude-code/session-start/post-compact-restore.js +0 -41
  132. package/framework/hooks/claude-code/session-start/resume-context.js +0 -77
  133. package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +0 -90
  134. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +0 -114
  135. package/framework/hooks/shared/compact-restore.js +0 -123
  136. package/framework/hooks/shared/dispatch-helpers.js +0 -147
  137. package/framework/hooks/shared/skill-reminder-helpers.js +0 -79
  138. package/framework/hooks/shared/stale-task-reset.js +0 -57
  139. package/framework/hooks/shared/worktree-helpers.js +0 -75
  140. package/framework/skills/level-0-meta/README.md +0 -7
  141. package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +0 -472
  142. package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +0 -174
  143. package/framework/skills/level-0-meta/morph-brainstorming/references/proposal-example.md +0 -138
  144. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +0 -145
  145. package/framework/skills/level-0-meta/morph-code-review/SKILL.md +0 -250
  146. package/framework/skills/level-0-meta/morph-code-review/references/review-example.md +0 -164
  147. package/framework/skills/level-0-meta/morph-code-review/references/review-guidelines.md +0 -100
  148. package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +0 -151
  149. package/framework/skills/level-0-meta/morph-code-review-nextjs/SKILL.md +0 -165
  150. package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +0 -254
  151. package/framework/skills/level-0-meta/morph-code-review-nextjs/scripts/scan-nextjs.mjs +0 -189
  152. package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +0 -344
  153. package/framework/skills/level-0-meta/morph-frontend-review/scripts/scan-accessibility.mjs +0 -376
  154. package/framework/skills/level-0-meta/morph-init/SKILL.md +0 -537
  155. package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +0 -225
  156. package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +0 -382
  157. package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-dev-server.mjs +0 -153
  158. package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-stack.mjs +0 -234
  159. package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +0 -106
  160. package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +0 -243
  161. package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +0 -169
  162. package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +0 -148
  163. package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +0 -110
  164. package/framework/skills/level-1-workflows/README.md +0 -7
  165. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +0 -269
  166. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +0 -453
  167. package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +0 -44
  168. package/framework/skills/level-1-workflows/morph-phase-design/references/spec-authoring-guide.md +0 -55
  169. package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +0 -253
  170. package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
  171. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +0 -493
  172. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +0 -50
  173. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +0 -46
  174. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +0 -47
  175. package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +0 -132
  176. package/framework/skills/level-1-workflows/morph-phase-implement/references/vsa-implementation-guide.md +0 -92
  177. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +0 -257
  178. package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
  179. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +0 -291
  180. package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -143
  181. package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +0 -404
  182. package/framework/skills/level-1-workflows/morph-phase-tasks/scripts/validate-tasks.mjs +0 -112
  183. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +0 -788
  184. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +0 -147
  185. package/framework/squad-templates/backend-only.json +0 -47
  186. package/framework/squad-templates/frontend-only.json +0 -56
  187. package/framework/squad-templates/full-stack.json +0 -74
  188. package/src/commands/agents/agent-metrics.js +0 -174
  189. package/src/commands/agents/audit-dispatch.js +0 -82
  190. package/src/commands/agents/detect-agents.js +0 -275
  191. package/src/commands/agents/dispatch-agents.js +0 -461
  192. package/src/commands/agents/index.js +0 -13
  193. package/src/commands/agents/manage-agents.js +0 -352
  194. package/src/commands/agents/project-metrics.js +0 -74
  195. package/src/commands/phase/phase-reset.js +0 -74
  196. package/src/commands/scope/escalate.js +0 -217
  197. package/src/commands/state/advance-phase.js +0 -600
  198. package/src/commands/state/phase-runner.js +0 -215
  199. package/src/commands/state/state.js +0 -444
  200. package/src/commands/task/expand.js +0 -100
  201. package/src/commands/tasks/index.js +0 -4
  202. package/src/commands/tasks/task.js +0 -105
  203. package/src/commands/templates/index.js +0 -5
  204. package/src/commands/templates/template-render.js +0 -93
  205. package/src/commands/trust/trust.js +0 -118
  206. package/src/lib/agents/agents-manager.js +0 -349
  207. package/src/lib/build/build-runner.js +0 -99
  208. package/src/lib/checkpoints/checkpoint-hooks.js +0 -111
  209. package/src/lib/contracts/sprint-contract-generator.js +0 -137
  210. package/src/lib/scope/impact-analyzer.js +0 -106
  211. package/src/lib/trust/trust-manager.js +0 -157
  212. package/src/utils/tier-helpers.js +0 -24
  213. /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();