@polymorphism-tech/morph-spec 4.8.19 → 4.10.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 (214) hide show
  1. package/CLAUDE.md +21 -0
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +44 -55
  4. package/bin/task-manager.js +133 -20
  5. package/bin/validate.js +67 -33
  6. package/claude-plugin.json +1 -1
  7. package/docs/CHEATSHEET.md +201 -203
  8. package/docs/QUICKSTART.md +2 -2
  9. package/framework/CLAUDE.md +99 -77
  10. package/framework/agents.json +734 -182
  11. package/framework/commands/commit.md +166 -0
  12. package/framework/commands/morph-apply.md +13 -2
  13. package/framework/commands/morph-archive.md +8 -2
  14. package/framework/commands/morph-infra.md +6 -0
  15. package/framework/commands/morph-preflight.md +6 -0
  16. package/framework/commands/morph-proposal.md +56 -7
  17. package/framework/commands/morph-status.md +6 -0
  18. package/framework/commands/morph-troubleshoot.md +6 -0
  19. package/framework/hooks/claude-code/notification/approval-reminder.js +3 -2
  20. package/framework/hooks/claude-code/post-tool-use/context-refresh.js +1 -1
  21. package/framework/hooks/claude-code/post-tool-use/dispatch.js +155 -32
  22. package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +78 -0
  23. package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +8 -17
  24. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +16 -3
  25. package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +4 -3
  26. package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +4 -3
  27. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +60 -0
  28. package/framework/hooks/claude-code/session-start/inject-morph-context.js +124 -2
  29. package/framework/hooks/claude-code/session-start/post-compact-restore.js +41 -0
  30. package/framework/hooks/claude-code/statusline.py +76 -30
  31. package/framework/hooks/claude-code/stop/validate-completion.js +2 -15
  32. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +23 -5
  33. package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +14 -6
  34. package/framework/hooks/shared/activity-logger.js +0 -24
  35. package/framework/hooks/shared/compact-restore.js +100 -0
  36. package/framework/hooks/shared/dispatch-helpers.js +116 -0
  37. package/framework/hooks/shared/phase-utils.js +12 -5
  38. package/framework/hooks/shared/skill-reminder-helpers.js +79 -0
  39. package/framework/hooks/shared/stale-task-reset.js +57 -0
  40. package/framework/hooks/shared/state-reader.js +29 -5
  41. package/framework/hooks/shared/worktree-helpers.js +53 -0
  42. package/framework/phases.json +69 -14
  43. package/framework/rules/morph-workflow.md +88 -86
  44. package/framework/skills/level-0-meta/mcp-registry.json +86 -51
  45. package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/SKILL.md +14 -17
  46. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
  47. package/framework/skills/level-0-meta/{code-review → morph-code-review}/SKILL.md +2 -2
  48. package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/SKILL.md +163 -163
  49. package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/SKILL.md +9 -9
  50. package/framework/skills/level-0-meta/morph-init/SKILL.md +77 -12
  51. package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/SKILL.md +62 -15
  52. package/framework/skills/level-0-meta/morph-replicate/SKILL.md +5 -5
  53. package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +1 -1
  54. package/framework/skills/level-0-meta/{simulation-checklist → morph-simulation-checklist}/SKILL.md +1 -1
  55. package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/SKILL.md +2 -2
  56. package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/SKILL.md +3 -4
  57. package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/references/tools-per-phase.md +7 -7
  58. package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/SKILL.md +2 -2
  59. package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/scripts/check-phase-outputs.mjs +2 -2
  60. package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +238 -0
  61. package/framework/skills/level-1-workflows/{phase-codebase-analysis → morph-phase-codebase-analysis}/SKILL.md +3 -3
  62. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +507 -0
  63. package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/SKILL.md +168 -27
  64. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +50 -0
  65. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +45 -0
  66. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +47 -0
  67. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +254 -0
  68. package/framework/skills/level-1-workflows/{phase-setup → morph-phase-setup}/SKILL.md +50 -3
  69. package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/SKILL.md +48 -11
  70. package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/scripts/validate-tasks.mjs +3 -3
  71. package/framework/skills/level-1-workflows/{phase-uiux → morph-phase-uiux}/SKILL.md +46 -11
  72. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +97 -0
  73. package/framework/standards/STANDARDS.json +640 -88
  74. package/framework/standards/infrastructure/vercel/vercel-database.md +106 -0
  75. package/framework/standards/integration/mcp/mcp-tools.md +25 -7
  76. package/framework/templates/REGISTRY.json +1825 -1909
  77. package/framework/templates/context/CONTEXT-FEATURE.md +276 -276
  78. package/framework/templates/docs/onboarding.md +3 -7
  79. package/package.json +2 -7
  80. package/src/commands/agents/dispatch-agents.js +104 -6
  81. package/src/commands/mcp/mcp-setup.js +39 -2
  82. package/src/commands/phase/phase-reset.js +74 -0
  83. package/src/commands/project/doctor.js +34 -51
  84. package/src/commands/project/init.js +1 -1
  85. package/src/commands/project/status.js +2 -2
  86. package/src/commands/project/update.js +381 -365
  87. package/src/commands/project/worktree.js +154 -0
  88. package/src/commands/scope/escalate.js +215 -0
  89. package/src/commands/state/advance-phase.js +132 -68
  90. package/src/commands/state/approve.js +2 -2
  91. package/src/commands/state/index.js +7 -8
  92. package/src/commands/state/phase-runner.js +1 -1
  93. package/src/commands/state/state.js +61 -6
  94. package/src/commands/task/expand.js +100 -0
  95. package/src/commands/tasks/task.js +78 -99
  96. package/src/commands/templates/template-render.js +93 -173
  97. package/src/commands/trust/trust.js +26 -21
  98. package/src/core/paths/output-schema.js +19 -3
  99. package/src/core/state/phase-state-machine.js +7 -4
  100. package/src/core/state/state-manager.js +32 -57
  101. package/src/core/workflows/workflow-detector.js +9 -87
  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 +336 -0
  108. package/src/lib/scope/impact-analyzer.js +106 -0
  109. package/src/lib/stack/stack-profile.js +88 -0
  110. package/src/lib/tasks/task-classifier.js +16 -0
  111. package/src/lib/tasks/task-parser.js +1 -1
  112. package/src/lib/tasks/test-runner.js +77 -0
  113. package/src/lib/trust/trust-manager.js +32 -144
  114. package/src/lib/validators/shared/emit-validator-dispatch.js +64 -0
  115. package/src/lib/validators/spec-validator.js +58 -4
  116. package/src/lib/validators/validation-runner.js +23 -11
  117. package/src/scripts/setup-infra.js +255 -224
  118. package/src/utils/agents-installer.js +34 -14
  119. package/src/utils/banner.js +1 -1
  120. package/src/utils/claude-settings-manager.js +1 -1
  121. package/src/utils/file-copier.js +1 -1
  122. package/src/utils/hooks-installer.js +272 -8
  123. package/framework/hooks/dev/check-sync-health.js +0 -117
  124. package/framework/hooks/dev/guard-version-numbers.js +0 -57
  125. package/framework/hooks/dev/sync-standards-registry.js +0 -60
  126. package/framework/hooks/dev/sync-template-registry.js +0 -60
  127. package/framework/hooks/dev/validate-skill-format.js +0 -70
  128. package/framework/hooks/dev/validate-standard-format.js +0 -73
  129. package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +0 -190
  130. package/framework/skills/level-1-workflows/phase-design/SKILL.md +0 -366
  131. package/framework/templates/meta-prompts/hops/hop-retry.md +0 -78
  132. package/framework/templates/meta-prompts/hops/hop-validation.md +0 -97
  133. package/framework/templates/meta-prompts/hops/hop-wrapper.md +0 -36
  134. package/framework/workflows/configs/design-impl.json +0 -49
  135. package/framework/workflows/configs/express.json +0 -45
  136. package/framework/workflows/configs/fast-track.json +0 -42
  137. package/framework/workflows/configs/full-morph.json +0 -79
  138. package/framework/workflows/configs/fusion.json +0 -39
  139. package/framework/workflows/configs/long-running.json +0 -33
  140. package/framework/workflows/configs/spec-only.json +0 -43
  141. package/framework/workflows/configs/ui-refresh.json +0 -49
  142. package/framework/workflows/configs/zero-touch.json +0 -82
  143. package/src/commands/project/index.js +0 -8
  144. package/src/commands/project/monitor.js +0 -295
  145. package/src/commands/project/tutorial.js +0 -115
  146. package/src/commands/state/validate-phase.js +0 -238
  147. package/src/commands/templates/generate-contracts.js +0 -445
  148. package/src/core/index.js +0 -10
  149. package/src/core/orchestrator.js +0 -171
  150. package/src/core/registry/command-registry.js +0 -28
  151. package/src/core/registry/index.js +0 -8
  152. package/src/core/registry/validator-registry.js +0 -204
  153. package/src/core/state/index.js +0 -8
  154. package/src/core/templates/index.js +0 -9
  155. package/src/core/templates/template-data-sources.js +0 -325
  156. package/src/core/templates/template-validator.js +0 -296
  157. package/src/core/workflows/index.js +0 -7
  158. package/src/generator/config-generator.js +0 -206
  159. package/src/generator/templates/config.json.template +0 -40
  160. package/src/generator/templates/project.md.template +0 -67
  161. package/src/lib/agents/micro-agent-factory.js +0 -161
  162. package/src/lib/analysis/complexity-analyzer.js +0 -441
  163. package/src/lib/analysis/index.js +0 -7
  164. package/src/lib/analytics/analytics-engine.js +0 -345
  165. package/src/lib/checkpoints/checkpoint-hooks.js +0 -298
  166. package/src/lib/checkpoints/index.js +0 -7
  167. package/src/lib/context/context-bundler.js +0 -241
  168. package/src/lib/context/context-optimizer.js +0 -212
  169. package/src/lib/context/context-tracker.js +0 -273
  170. package/src/lib/context/core-four-tracker.js +0 -201
  171. package/src/lib/context/mcp-optimizer.js +0 -200
  172. package/src/lib/detectors/config-detector.js +0 -223
  173. package/src/lib/detectors/standards-generator.js +0 -335
  174. package/src/lib/detectors/structure-detector.js +0 -275
  175. package/src/lib/execution/fusion-executor.js +0 -304
  176. package/src/lib/execution/parallel-executor.js +0 -270
  177. package/src/lib/hooks/stop-hook-executor.js +0 -286
  178. package/src/lib/hops/hop-composer.js +0 -221
  179. package/src/lib/monitor/agent-resolver.js +0 -144
  180. package/src/lib/monitor/renderer.js +0 -230
  181. package/src/lib/orchestration/index.js +0 -7
  182. package/src/lib/orchestration/team-orchestrator.js +0 -404
  183. package/src/lib/phase-chain/eligibility-checker.js +0 -243
  184. package/src/lib/threads/thread-coordinator.js +0 -238
  185. package/src/lib/threads/thread-manager.js +0 -317
  186. package/src/lib/tracking/artifact-trail.js +0 -202
  187. package/src/sanitizer/context-sanitizer.js +0 -221
  188. package/src/sanitizer/patterns.js +0 -163
  189. package/src/scanner/project-scanner.js +0 -242
  190. package/src/ui/diff-display.js +0 -91
  191. package/src/ui/interactive-wizard.js +0 -96
  192. package/src/ui/user-review.js +0 -211
  193. package/src/ui/wizard-questions.js +0 -188
  194. package/src/utils/color-utils.js +0 -70
  195. package/src/utils/process-handler.js +0 -97
  196. package/src/writer/file-writer.js +0 -86
  197. /package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/references/proposal-example.md +0 -0
  198. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-example.md +0 -0
  199. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-guidelines.md +0 -0
  200. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/scripts/scan-csharp.mjs +0 -0
  201. /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/references/review-example-nextjs.md +0 -0
  202. /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/scripts/scan-nextjs.mjs +0 -0
  203. /package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/scripts/scan-accessibility.mjs +0 -0
  204. /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-dev-server.mjs +0 -0
  205. /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-stack.mjs +0 -0
  206. /package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/scripts/set_title.sh +0 -0
  207. /package/framework/skills/level-1-workflows/{phase-clarify → morph-phase-clarify}/references/clarifications-example.md +0 -0
  208. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/architecture-analysis-guide.md +0 -0
  209. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-authoring-guide.md +0 -0
  210. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-example.md +0 -0
  211. /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/recap-example.md +0 -0
  212. /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/vsa-implementation-guide.md +0 -0
  213. /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/task-planning-patterns.md +0 -0
  214. /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/tasks-example.md +0 -0
@@ -1,390 +1,136 @@
1
1
  /**
2
- * Claude Config Detector
2
+ * @fileoverview Claude Config Detector
3
3
  *
4
- * Detects the user's Claude Code environment: plugins, MCP servers,
5
- * custom skills, commands, hooks, and superpowers installation.
4
+ * Scans .claude/ settings and plugins for MCP servers, hooks, skills, and commands.
5
+ * Used by mcp-setup and session-start hooks.
6
+ *
7
+ * @module detectors/claude-config-detector
6
8
  */
7
9
 
10
+ import { existsSync, readFileSync, readdirSync } from 'fs';
8
11
  import { join } from 'path';
9
- import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
10
12
  import { homedir } from 'os';
11
13
 
12
14
  /**
13
- * Detect full Claude Code configuration for a project
14
- * @param {string} targetPath - Project root directory
15
- * @param {Object} options - Options
16
- * @param {string} options.globalDir - Override global .claude dir (for testing)
17
- * @returns {Promise<Object>} Detected configuration
15
+ * Detect Claude Code configuration for a project.
16
+ *
17
+ * @param {string} targetPath - Root path of the project
18
+ * @returns {{ plugins: string[], mcpServers: Array<{name: string, config: Object}>, superpowers: boolean, customSkills: string[], customCommands: string[], hooks: string[] }}
18
19
  */
19
- export async function detectClaudeConfig(targetPath, options = {}) {
20
- const home = homedir();
21
- const globalClaudeDir = options.globalDir || join(home, '.claude');
22
- const projectClaudeDir = join(targetPath, '.claude');
23
-
20
+ export function detectClaudeConfig(targetPath) {
24
21
  const result = {
25
22
  plugins: [],
26
23
  mcpServers: [],
24
+ superpowers: false,
27
25
  customSkills: [],
28
26
  customCommands: [],
29
27
  hooks: [],
30
- superpowers: { installed: false, version: null, skills: [] }
31
28
  };
32
29
 
33
- // 1. Detect enabled plugins from global settings
34
- result.plugins = detectPlugins(globalClaudeDir);
35
-
36
- // 2. Detect MCP servers from global + project settings
37
- result.mcpServers = detectMcpServers(globalClaudeDir, projectClaudeDir);
38
-
39
- // 3. Detect custom skills
40
- result.customSkills = detectCustomSkills(globalClaudeDir, projectClaudeDir);
41
-
42
- // 4. Detect custom commands
43
- result.customCommands = detectCustomCommands(globalClaudeDir, projectClaudeDir);
44
-
45
- // 5. Detect hooks
46
- result.hooks = detectHooks(globalClaudeDir, projectClaudeDir);
47
-
48
- // 6. Detect superpowers plugin
49
- result.superpowers = detectSuperpowers(result.plugins, result.customSkills);
50
-
51
- return result;
52
- }
53
-
54
- /**
55
- * Detect enabled plugins from settings.json
56
- */
57
- function detectPlugins(globalClaudeDir) {
58
- const plugins = [];
59
-
60
- // Read global settings for enabledPlugins
61
- const settingsPath = join(globalClaudeDir, 'settings.json');
62
- const settings = readJsonSafe(settingsPath);
63
- const rawPlugins = settings?.enabledPlugins;
64
- const enabledPlugins = Array.isArray(rawPlugins) ? rawPlugins : [];
65
-
66
- // Read installed plugins for version info
67
- const installedPath = join(globalClaudeDir, 'plugins', 'installed_plugins.json');
68
- const installed = readJsonSafe(installedPath) || {};
69
-
70
- for (const pluginId of enabledPlugins) {
71
- const pluginInfo = installed[pluginId] || {};
72
- plugins.push({
73
- name: extractPluginName(pluginId),
74
- id: pluginId,
75
- version: pluginInfo.version || null,
76
- enabled: true
77
- });
78
- }
79
-
80
- return plugins;
81
- }
82
-
83
- /**
84
- * Detect MCP servers from global and project settings
85
- */
86
- function detectMcpServers(globalClaudeDir, projectClaudeDir) {
87
- const servers = [];
88
-
89
- // Global MCP servers from settings.json
90
- const globalSettings = readJsonSafe(join(globalClaudeDir, 'settings.json'));
91
- if (globalSettings?.mcpServers) {
92
- for (const [name, config] of Object.entries(globalSettings.mcpServers)) {
93
- servers.push({ name, source: 'global', config });
94
- }
95
- }
96
-
97
- // Global MCP servers from settings.local.json
98
- const globalLocal = readJsonSafe(join(globalClaudeDir, 'settings.local.json'));
99
- if (globalLocal?.mcpServers) {
100
- for (const [name, config] of Object.entries(globalLocal.mcpServers)) {
101
- if (!servers.find(s => s.name === name)) {
102
- servers.push({ name, source: 'global', config });
103
- }
104
- }
105
- }
106
-
107
- // Project MCP servers from .claude/settings.local.json
108
- const projectSettings = readJsonSafe(join(projectClaudeDir, 'settings.local.json'));
109
- if (projectSettings?.mcpServers) {
110
- for (const [name, config] of Object.entries(projectSettings.mcpServers)) {
111
- const existing = servers.find(s => s.name === name);
112
- if (existing) {
113
- existing.source = 'project'; // project overrides global
114
- } else {
115
- servers.push({ name, source: 'project', config });
30
+ try {
31
+ // ── MCP Servers from settings ────────────────────────────────────────────
32
+ const settingsPaths = [
33
+ join(targetPath, '.claude', 'settings.json'),
34
+ join(targetPath, '.claude', 'settings.local.json'),
35
+ ];
36
+
37
+ for (const sp of settingsPaths) {
38
+ if (existsSync(sp)) {
39
+ try {
40
+ const settings = JSON.parse(readFileSync(sp, 'utf8'));
41
+ const mcps = settings.mcpServers || {};
42
+ for (const [name, config] of Object.entries(mcps)) {
43
+ // Avoid duplicates — later file wins
44
+ const idx = result.mcpServers.findIndex(m => m.name === name);
45
+ if (idx >= 0) {
46
+ result.mcpServers[idx] = { name, config };
47
+ } else {
48
+ result.mcpServers.push({ name, config });
49
+ }
50
+ }
51
+
52
+ // Collect hooks
53
+ if (settings.hooks) {
54
+ for (const [event, eventHooks] of Object.entries(settings.hooks)) {
55
+ for (const group of (Array.isArray(eventHooks) ? eventHooks : [])) {
56
+ for (const h of (group.hooks || [])) {
57
+ result.hooks.push(`${event}:${h.type || 'unknown'}`);
58
+ }
59
+ }
60
+ }
61
+ }
62
+ } catch { /* invalid JSON */ }
116
63
  }
117
64
  }
118
- }
119
65
 
120
- return servers;
121
- }
122
-
123
- /**
124
- * Detect custom skills from .claude/skills/ directories
125
- */
126
- function detectCustomSkills(globalClaudeDir, projectClaudeDir) {
127
- const skills = [];
128
-
129
- // Global skills
130
- const globalSkills = join(globalClaudeDir, 'skills');
131
- if (existsSync(globalSkills)) {
132
- for (const file of walkMdFiles(globalSkills)) {
133
- skills.push({
134
- name: extractSkillName(file),
135
- path: file,
136
- source: 'global'
137
- });
66
+ // ── Plugins from global config ──────────────────────────────────────────
67
+ const pluginsPath = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');
68
+ if (existsSync(pluginsPath)) {
69
+ try {
70
+ const pluginsData = JSON.parse(readFileSync(pluginsPath, 'utf8'));
71
+ const plugins = pluginsData.plugins || {};
72
+ result.plugins = Object.keys(plugins);
73
+ result.superpowers = 'superpowers@claude-plugins-official' in plugins;
74
+ } catch { /* invalid JSON */ }
138
75
  }
139
- }
140
76
 
141
- // Project skills (may include morph-spec skills)
142
- const projectSkills = join(projectClaudeDir, 'skills');
143
- if (existsSync(projectSkills)) {
144
- for (const file of walkMdFiles(projectSkills)) {
145
- const name = extractSkillName(file);
146
- // Skip morph-spec framework skills (they're managed by init)
147
- if (file.includes('level-0-meta') || file.includes('level-1-workflows')) {
148
- continue;
149
- }
150
- skills.push({
151
- name,
152
- path: file,
153
- source: 'project'
154
- });
77
+ // ── Custom skills ───────────────────────────────────────────────────────
78
+ const skillsPath = join(targetPath, '.claude', 'skills');
79
+ if (existsSync(skillsPath)) {
80
+ try {
81
+ result.customSkills = readdirSync(skillsPath).filter(f => !f.startsWith('.'));
82
+ } catch { /* ignore */ }
155
83
  }
156
- }
157
84
 
158
- return skills;
159
- }
160
-
161
- /**
162
- * Detect custom commands from .claude/commands/ directories
163
- */
164
- function detectCustomCommands(globalClaudeDir, projectClaudeDir) {
165
- const commands = [];
166
-
167
- // Global commands
168
- const globalCommands = join(globalClaudeDir, 'commands');
169
- if (existsSync(globalCommands)) {
170
- for (const file of walkMdFiles(globalCommands)) {
171
- commands.push({
172
- name: extractCommandName(file),
173
- path: file,
174
- source: 'global'
175
- });
176
- }
177
- }
178
-
179
- // Project commands (excluding morph-spec managed commands)
180
- const projectCommands = join(projectClaudeDir, 'commands');
181
- if (existsSync(projectCommands)) {
182
- for (const file of walkMdFiles(projectCommands)) {
183
- const name = extractCommandName(file);
184
- // Skip morph-spec commands
185
- if (name.startsWith('morph-')) continue;
186
- commands.push({
187
- name,
188
- path: file,
189
- source: 'project'
190
- });
191
- }
192
- }
193
-
194
- return commands;
195
- }
196
-
197
- /**
198
- * Detect hooks from settings files
199
- */
200
- function detectHooks(globalClaudeDir, projectClaudeDir) {
201
- const hooks = [];
202
-
203
- // Global hooks
204
- const globalSettings = readJsonSafe(join(globalClaudeDir, 'settings.json'));
205
- if (globalSettings?.hooks) {
206
- for (const [trigger, entries] of Object.entries(globalSettings.hooks)) {
207
- hooks.push({ trigger, source: 'global', entries });
85
+ // ── Custom commands ─────────────────────────────────────────────────────
86
+ const commandsPath = join(targetPath, '.claude', 'commands');
87
+ if (existsSync(commandsPath)) {
88
+ try {
89
+ result.customCommands = readdirSync(commandsPath).filter(f => f.endsWith('.md'));
90
+ } catch { /* ignore */ }
208
91
  }
209
- }
210
-
211
- // Project hooks
212
- const projectSettings = readJsonSafe(join(projectClaudeDir, 'settings.local.json'));
213
- if (projectSettings?.hooks) {
214
- for (const [trigger, entries] of Object.entries(projectSettings.hooks)) {
215
- hooks.push({ trigger, source: 'project', entries });
216
- }
217
- }
218
-
219
- return hooks;
220
- }
221
-
222
- /**
223
- * Detect superpowers plugin installation
224
- */
225
- function detectSuperpowers(plugins, customSkills) {
226
- const result = { installed: false, version: null, skills: [] };
227
-
228
- // Check if superpowers is in enabled plugins
229
- const superPlugin = plugins.find(p =>
230
- p.name === 'superpowers' || p.id?.includes('superpowers')
231
- );
232
-
233
- if (superPlugin) {
234
- result.installed = true;
235
- result.version = superPlugin.version;
236
- }
237
-
238
- // Collect superpowers skills from detected skills
239
- const superpowerSkills = customSkills.filter(s =>
240
- s.path?.includes('superpowers') || s.name?.startsWith('superpowers:')
241
- );
242
-
243
- if (superpowerSkills.length > 0) {
244
- result.installed = true;
245
- result.skills = superpowerSkills.map(s => s.name);
92
+ } catch {
93
+ // Fail-safe
246
94
  }
247
95
 
248
96
  return result;
249
97
  }
250
98
 
251
- // ============================================================================
252
- // MCP Phase Mapping
253
- // ============================================================================
254
-
255
99
  /**
256
- * Map detected MCPs to morph-spec phases where they're useful
257
- * @param {Array} mcpServers - Detected MCP servers
258
- * @returns {Object} Map of phase relevant MCPs
100
+ * Cross-reference configured MCPs with the MCP registry's phase matrix.
101
+ *
102
+ * @param {Array<{name: string}>} mcpServers - Detected MCP server list
103
+ * @param {Object} phaseMatrix - From mcp-registry.json → phaseMatrix
104
+ * @param {string} currentPhase - Current morph-spec phase
105
+ * @returns {{ available: string[], recommended: string[], missing: string[] }}
259
106
  */
260
- export function mapMcpsToPhases(mcpServers) {
261
- const phaseMap = {
262
- proposal: [],
263
- setup: [],
264
- uiux: [],
265
- design: [],
266
- clarify: [],
267
- tasks: [],
268
- implement: []
269
- };
270
-
271
- const mcpPhaseConfig = {
272
- context7: ['proposal', 'design', 'tasks', 'implement'],
273
- supabase: ['design', 'implement'],
274
- playwright: ['uiux', 'implement'],
275
- github: ['setup', 'tasks', 'implement'],
276
- figma: ['uiux'],
277
- docker: ['implement'],
278
- azure: ['implement']
107
+ export function mapMcpsToPhases(mcpServers, phaseMatrix, currentPhase) {
108
+ const configuredNames = new Set(mcpServers.map(m => m.name.toLowerCase()));
109
+ const recommended = (phaseMatrix[currentPhase] || []);
110
+
111
+ return {
112
+ available: recommended.filter(name => configuredNames.has(name)),
113
+ recommended,
114
+ missing: recommended.filter(name => !configuredNames.has(name)),
279
115
  };
280
-
281
- for (const server of mcpServers) {
282
- const name = server.name.toLowerCase();
283
- for (const [mcpKey, phases] of Object.entries(mcpPhaseConfig)) {
284
- if (name.includes(mcpKey)) {
285
- for (const phase of phases) {
286
- phaseMap[phase].push({
287
- name: server.name,
288
- source: server.source
289
- });
290
- }
291
- }
292
- }
293
- }
294
-
295
- return phaseMap;
296
116
  }
297
117
 
298
118
  /**
299
- * Format detected config as a summary table string
300
- * @param {Object} config - Detected Claude config
301
- * @returns {string} Formatted summary
119
+ * Format a human-readable one-liner summary of detected config.
120
+ *
121
+ * @param {{ plugins: string[], mcpServers: Array<{name: string}>, superpowers: boolean }} config
122
+ * @returns {string}
302
123
  */
303
124
  export function formatConfigSummary(config) {
304
- const lines = [];
305
-
306
- if (config.plugins.length > 0) {
307
- lines.push('Plugins:');
308
- for (const p of config.plugins) {
309
- const ver = p.version ? ` v${p.version}` : '';
310
- lines.push(` - ${p.name}${ver}`);
311
- }
312
- }
313
-
125
+ const parts = [];
314
126
  if (config.mcpServers.length > 0) {
315
- lines.push('MCP Servers:');
316
- for (const s of config.mcpServers) {
317
- lines.push(` - ${s.name} (${s.source})`);
318
- }
319
- }
320
-
321
- if (config.superpowers.installed) {
322
- lines.push(`Superpowers: installed (${config.superpowers.skills.length} skills)`);
323
- }
324
-
325
- if (config.customSkills.length > 0) {
326
- lines.push(`Custom Skills: ${config.customSkills.length}`);
327
- }
328
-
329
- if (config.customCommands.length > 0) {
330
- lines.push(`Custom Commands: ${config.customCommands.length}`);
331
- }
332
-
333
- return lines.join('\n');
334
- }
335
-
336
- // ============================================================================
337
- // Helpers
338
- // ============================================================================
339
-
340
- function readJsonSafe(filePath) {
341
- try {
342
- if (!existsSync(filePath)) return null;
343
- return JSON.parse(readFileSync(filePath, 'utf-8'));
344
- } catch {
345
- return null;
127
+ parts.push(`MCPs: ${config.mcpServers.map(m => m.name).join(', ')}`);
346
128
  }
347
- }
348
-
349
- function extractPluginName(pluginId) {
350
- // Plugin IDs can be URLs or package names
351
- // e.g., "https://github.com/user/superpowers" → "superpowers"
352
- // e.g., "@user/superpowers" → "superpowers"
353
- const parts = pluginId.split('/');
354
- return parts[parts.length - 1].replace(/\.git$/, '');
355
- }
356
-
357
- function extractSkillName(filePath) {
358
- // Extract skill name from file path
359
- // e.g., ".claude/skills/superpowers/brainstorming.md" → "superpowers:brainstorming"
360
- const parts = filePath.replace(/\\/g, '/').split('/');
361
- const mdFile = parts[parts.length - 1].replace('.md', '');
362
- const parentDir = parts.length >= 2 ? parts[parts.length - 2] : null;
363
-
364
- if (parentDir && parentDir !== 'skills') {
365
- return `${parentDir}:${mdFile}`;
129
+ if (config.plugins.length > 0) {
130
+ parts.push(`Plugins: ${config.plugins.length}`);
366
131
  }
367
- return mdFile;
368
- }
369
-
370
- function extractCommandName(filePath) {
371
- return filePath.replace(/\\/g, '/').split('/').pop().replace('.md', '');
372
- }
373
-
374
- function walkMdFiles(dir) {
375
- const results = [];
376
- try {
377
- const entries = readdirSync(dir, { withFileTypes: true });
378
- for (const entry of entries) {
379
- const fullPath = join(dir, entry.name);
380
- if (entry.isDirectory()) {
381
- results.push(...walkMdFiles(fullPath));
382
- } else if (entry.isFile() && entry.name.endsWith('.md')) {
383
- results.push(fullPath);
384
- }
385
- }
386
- } catch {
387
- // Permission errors or broken symlinks
132
+ if (config.superpowers) {
133
+ parts.push('superpowers: yes');
388
134
  }
389
- return results;
135
+ return parts.join(' | ') || 'No Claude config detected';
390
136
  }