@polymorphism-tech/morph-spec 8.0.0 → 8.1.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 (168) hide show
  1. package/README.md +2 -2
  2. package/bin/validate.js +379 -379
  3. package/claude-plugin.json +1 -1
  4. package/docs/CHEATSHEET.md +1 -1
  5. package/docs/QUICKSTART.md +1 -1
  6. package/framework/CLAUDE.md +1 -1
  7. package/framework/MORPH.md +5 -5
  8. package/framework/agents.json +1 -1
  9. package/framework/commands/commit.md +166 -166
  10. package/framework/commands/morph-infra.md +215 -215
  11. package/framework/commands/morph-preflight.md +205 -205
  12. package/framework/commands/morph-troubleshoot.md +124 -124
  13. package/framework/hooks/README.md +0 -1
  14. package/framework/hooks/claude-code/statusline.py +679 -679
  15. package/framework/hooks/claude-code/statusline.sh +11 -11
  16. package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
  17. package/framework/hooks/git/pre-commit/agents.sh +25 -25
  18. package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
  19. package/framework/hooks/git/pre-commit/specs.sh +50 -50
  20. package/framework/hooks/git/pre-push/run-tests.sh +44 -44
  21. package/framework/hooks/shared/activity-logger.js +160 -160
  22. package/framework/hooks/shared/stdin-reader.js +26 -26
  23. package/framework/rubrics/backend.json +41 -41
  24. package/framework/rules/ai-agents-standards.md +45 -45
  25. package/framework/rules/dotnet-templates.md +86 -86
  26. package/framework/rules/frontend-standards.md +13 -13
  27. package/framework/rules/infrastructure-standards.md +27 -27
  28. package/framework/rules/neon-standards.md +25 -25
  29. package/framework/rules/nextjs-standards.md +20 -20
  30. package/framework/rules/nextjs-templates.md +40 -40
  31. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  32. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  33. package/framework/skills/core/morph-apply/SKILL.md +12 -21
  34. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  35. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  36. package/framework/skills/core/morph-hotfix/SKILL.md +2 -4
  37. package/framework/skills/core/morph-implement/SKILL.md +10 -19
  38. package/framework/skills/core/morph-plan/SKILL.md +1 -1
  39. package/framework/skills/core/morph-proposal/SKILL.md +1 -1
  40. package/framework/skills/core/morph-review/SKILL.md +11 -15
  41. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  42. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  43. package/framework/standards/ai-agents/agent-session.md +160 -160
  44. package/framework/standards/ai-agents/batching.md +193 -193
  45. package/framework/standards/ai-agents/context-providers.md +188 -188
  46. package/framework/standards/ai-agents/mcp-server.md +250 -250
  47. package/framework/standards/ai-agents/mcp-tools.md +197 -197
  48. package/framework/standards/ai-agents/middleware-patterns.md +207 -207
  49. package/framework/standards/ai-agents/modalities-image-gen.md +173 -173
  50. package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -171
  51. package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -181
  52. package/framework/standards/ai-agents/modalities-vision.md +201 -201
  53. package/framework/standards/ai-agents/multi-agent-patterns.md +155 -155
  54. package/framework/standards/ai-agents/production.md +275 -275
  55. package/framework/standards/ai-agents/prompts-in-db.md +214 -214
  56. package/framework/standards/ai-agents/providers/model-registry.md +158 -158
  57. package/framework/standards/ai-agents/service-tier-flex.md +135 -135
  58. package/framework/standards/ai-agents/structured-output.md +249 -249
  59. package/framework/standards/ai-agents/sweet-spot.md +109 -109
  60. package/framework/templates/IDE-SNIPPETS.md +262 -262
  61. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  62. package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
  63. package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
  64. package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
  65. package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
  66. package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
  67. package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
  68. package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
  69. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
  70. package/framework/templates/code/dotnet/database/migration.cs +83 -83
  71. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
  72. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  73. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  74. package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
  75. package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
  76. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  77. package/framework/templates/code/typescript/contracts.ts +167 -167
  78. package/framework/templates/docs/clarifications.md +253 -253
  79. package/framework/templates/docs/schema-analysis.md +119 -119
  80. package/framework/templates/docs/ui-components.md +52 -52
  81. package/framework/templates/docs/ui-design-system.md +76 -76
  82. package/framework/templates/docs/ui-flows.md +167 -167
  83. package/framework/templates/docs/ui-mockups.md +98 -98
  84. package/framework/templates/feature/decisions.md +122 -122
  85. package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
  86. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  87. package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
  88. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  89. package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
  90. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  91. package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
  92. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  93. package/framework/templates/infrastructure/docker/docker-compose.template.yml +50 -50
  94. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  95. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  96. package/framework/templates/infrastructure/github/README.md +56 -56
  97. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  98. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  99. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  100. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  101. package/framework/templates/integrations/asaas-client.cs +387 -387
  102. package/framework/templates/integrations/asaas-webhook.cs +351 -351
  103. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  104. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  105. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
  106. package/framework/templates/project/validate.js +120 -120
  107. package/framework/templates/state.template.json +17 -17
  108. package/framework/templates/ui/design-system.css +226 -226
  109. package/package.json +1 -1
  110. package/src/commands/dashboard/dashboard.js +53 -53
  111. package/src/commands/index.js +13 -13
  112. package/src/commands/mcp/mcp-setup.js +519 -519
  113. package/src/commands/project/checkpoint.js +209 -209
  114. package/src/commands/project/worktree.js +83 -1
  115. package/src/core/state/phase-state-machine.js +197 -197
  116. package/src/core/templates/template-renderer.js +477 -477
  117. package/src/dashboard/app/components/DetailPanel.jsx +11 -11
  118. package/src/dashboard/app/components/FlowGraph.jsx +17 -17
  119. package/src/dashboard/app/components/SearchFilter.jsx +22 -22
  120. package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
  121. package/src/dashboard/app/main.jsx +13 -13
  122. package/src/dashboard/app/pages/AgentMap.jsx +370 -370
  123. package/src/dashboard/app/pages/HookInspector.jsx +418 -418
  124. package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
  125. package/src/dashboard/app/pages/RulesMap.jsx +273 -273
  126. package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
  127. package/src/dashboard/app/pages/Standards.jsx +332 -332
  128. package/src/dashboard/app/styles/index.css +47 -47
  129. package/src/dashboard/index.html +12 -12
  130. package/src/dashboard/package.json +24 -24
  131. package/src/dashboard/scanner/hooks-scanner.js +41 -41
  132. package/src/dashboard/scanner/phases-scanner.js +15 -15
  133. package/src/dashboard/scanner/rubrics-scanner.js +12 -12
  134. package/src/dashboard/scanner/rules-scanner.js +37 -37
  135. package/src/dashboard/scanner/skills-scanner.js +43 -43
  136. package/src/dashboard/scanner/squads-scanner.js +13 -13
  137. package/src/dashboard/scanner/standards-scanner.js +26 -26
  138. package/src/dashboard/vite-plugin-morph-data.js +17 -17
  139. package/src/dashboard/vite.config.js +24 -24
  140. package/src/lib/agents/metrics-aggregator.js +56 -56
  141. package/src/lib/agents/metrics-calculator.js +125 -125
  142. package/src/lib/agents/output-collector.js +73 -73
  143. package/src/lib/detectors/claude-config-detector.js +152 -152
  144. package/src/lib/detectors/design-system-detector.js +231 -231
  145. package/src/lib/detectors/index.js +217 -217
  146. package/src/lib/generators/architecture-scanner.js +639 -639
  147. package/src/lib/generators/design-system-generator.js +508 -508
  148. package/src/lib/generators/index.js +13 -13
  149. package/src/lib/generators/settings-generator.js +209 -209
  150. package/src/lib/hooks/hook-executor.js +167 -167
  151. package/src/lib/installers/mcp-installer.js +719 -719
  152. package/src/lib/tasks/task-classifier.js +30 -30
  153. package/src/lib/tasks/task-parser.js +209 -209
  154. package/src/lib/validators/architecture/architecture-validator.js +259 -259
  155. package/src/lib/validators/checkpoint-persistence.js +39 -39
  156. package/src/lib/validators/contracts/contract-compliance-validator.js +274 -274
  157. package/src/lib/validators/css/css-validator.js +340 -340
  158. package/src/lib/validators/packages/package-validator.js +347 -347
  159. package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
  160. package/src/lib/validators/shared/issue-counter.js +14 -14
  161. package/src/lib/validators/validation-runner.js +349 -349
  162. package/src/utils/agent-id-mapper.js +11 -11
  163. package/src/utils/claude-settings-manager.js +300 -300
  164. package/src/utils/hooks-installer.js +3 -24
  165. package/src/utils/skills-installer.js +174 -174
  166. package/framework/hooks/claude-code/core/subagent-report-guard.js +0 -95
  167. package/framework/templates/.idea/morph-templates.xml +0 -92
  168. package/framework/templates/.vscode/morph-templates.code-snippets +0 -186
@@ -1,152 +1,152 @@
1
- /**
2
- * @fileoverview Claude Config Detector
3
- *
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
8
- */
9
-
10
- import { existsSync, readFileSync, readdirSync } from 'fs';
11
- import { join } from 'path';
12
- import { homedir } from 'os';
13
-
14
- /**
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[] }}
19
- */
20
- export function detectClaudeConfig(targetPath) {
21
- const result = {
22
- plugins: [],
23
- mcpServers: [],
24
- superpowers: false,
25
- customSkills: [],
26
- customCommands: [],
27
- hooks: [],
28
- };
29
-
30
- const mcpJsonNames = new Set();
31
-
32
- try {
33
- // ── MCP Servers from .mcp.json (primary — Claude Code reads this) ───────
34
- const mcpJsonPath = join(targetPath, '.mcp.json');
35
- if (existsSync(mcpJsonPath)) {
36
- try {
37
- const mcpJson = JSON.parse(readFileSync(mcpJsonPath, 'utf8'));
38
- const mcps = mcpJson.mcpServers || {};
39
- for (const [name, config] of Object.entries(mcps)) {
40
- result.mcpServers.push({ name, config });
41
- mcpJsonNames.add(name);
42
- }
43
- } catch { /* invalid JSON */ }
44
- }
45
-
46
- // ── MCP Servers + hooks from settings (legacy + hooks/permissions) ───────
47
- const settingsPaths = [
48
- join(targetPath, '.claude', 'settings.json'),
49
- join(targetPath, '.claude', 'settings.local.json'),
50
- ];
51
-
52
- for (const sp of settingsPaths) {
53
- if (existsSync(sp)) {
54
- try {
55
- const settings = JSON.parse(readFileSync(sp, 'utf8'));
56
- // Legacy MCP entries — skip if already in .mcp.json, but allow later settings files to override earlier ones
57
- const mcps = settings.mcpServers || {};
58
- for (const [name, config] of Object.entries(mcps)) {
59
- if (mcpJsonNames.has(name)) continue; // .mcp.json takes priority
60
- const idx = result.mcpServers.findIndex(m => m.name === name);
61
- if (idx >= 0) {
62
- result.mcpServers[idx] = { name, config }; // later file wins
63
- } else {
64
- result.mcpServers.push({ name, config });
65
- }
66
- }
67
-
68
- // Collect hooks
69
- if (settings.hooks) {
70
- for (const [event, eventHooks] of Object.entries(settings.hooks)) {
71
- for (const group of (Array.isArray(eventHooks) ? eventHooks : [])) {
72
- for (const h of (group.hooks || [])) {
73
- result.hooks.push(`${event}:${h.type || 'unknown'}`);
74
- }
75
- }
76
- }
77
- }
78
- } catch { /* invalid JSON */ }
79
- }
80
- }
81
-
82
- // ── Plugins from global config ──────────────────────────────────────────
83
- const pluginsPath = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');
84
- if (existsSync(pluginsPath)) {
85
- try {
86
- const pluginsData = JSON.parse(readFileSync(pluginsPath, 'utf8'));
87
- const plugins = pluginsData.plugins || {};
88
- result.plugins = Object.keys(plugins);
89
- result.superpowers = 'superpowers@claude-plugins-official' in plugins;
90
- } catch { /* invalid JSON */ }
91
- }
92
-
93
- // ── Custom skills ───────────────────────────────────────────────────────
94
- const skillsPath = join(targetPath, '.claude', 'skills');
95
- if (existsSync(skillsPath)) {
96
- try {
97
- result.customSkills = readdirSync(skillsPath).filter(f => !f.startsWith('.'));
98
- } catch { /* ignore */ }
99
- }
100
-
101
- // ── Custom commands ─────────────────────────────────────────────────────
102
- const commandsPath = join(targetPath, '.claude', 'commands');
103
- if (existsSync(commandsPath)) {
104
- try {
105
- result.customCommands = readdirSync(commandsPath).filter(f => f.endsWith('.md'));
106
- } catch { /* ignore */ }
107
- }
108
- } catch {
109
- // Fail-safe
110
- }
111
-
112
- return result;
113
- }
114
-
115
- /**
116
- * Cross-reference configured MCPs with the MCP registry's phase matrix.
117
- *
118
- * @param {Array<{name: string}>} mcpServers - Detected MCP server list
119
- * @param {Object} phaseMatrix - From mcp-registry.json → phaseMatrix
120
- * @param {string} currentPhase - Current morph-spec phase
121
- * @returns {{ available: string[], recommended: string[], missing: string[] }}
122
- */
123
- export function mapMcpsToPhases(mcpServers, phaseMatrix, currentPhase) {
124
- const configuredNames = new Set(mcpServers.map(m => m.name.toLowerCase()));
125
- const recommended = (phaseMatrix[currentPhase] || []);
126
-
127
- return {
128
- available: recommended.filter(name => configuredNames.has(name)),
129
- recommended,
130
- missing: recommended.filter(name => !configuredNames.has(name)),
131
- };
132
- }
133
-
134
- /**
135
- * Format a human-readable one-liner summary of detected config.
136
- *
137
- * @param {{ plugins: string[], mcpServers: Array<{name: string}>, superpowers: boolean }} config
138
- * @returns {string}
139
- */
140
- export function formatConfigSummary(config) {
141
- const parts = [];
142
- if (config.mcpServers.length > 0) {
143
- parts.push(`MCPs: ${config.mcpServers.map(m => m.name).join(', ')}`);
144
- }
145
- if (config.plugins.length > 0) {
146
- parts.push(`Plugins: ${config.plugins.length}`);
147
- }
148
- if (config.superpowers) {
149
- parts.push('superpowers: yes');
150
- }
151
- return parts.join(' | ') || 'No Claude config detected';
152
- }
1
+ /**
2
+ * @fileoverview Claude Config Detector
3
+ *
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
8
+ */
9
+
10
+ import { existsSync, readFileSync, readdirSync } from 'fs';
11
+ import { join } from 'path';
12
+ import { homedir } from 'os';
13
+
14
+ /**
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[] }}
19
+ */
20
+ export function detectClaudeConfig(targetPath) {
21
+ const result = {
22
+ plugins: [],
23
+ mcpServers: [],
24
+ superpowers: false,
25
+ customSkills: [],
26
+ customCommands: [],
27
+ hooks: [],
28
+ };
29
+
30
+ const mcpJsonNames = new Set();
31
+
32
+ try {
33
+ // ── MCP Servers from .mcp.json (primary — Claude Code reads this) ───────
34
+ const mcpJsonPath = join(targetPath, '.mcp.json');
35
+ if (existsSync(mcpJsonPath)) {
36
+ try {
37
+ const mcpJson = JSON.parse(readFileSync(mcpJsonPath, 'utf8'));
38
+ const mcps = mcpJson.mcpServers || {};
39
+ for (const [name, config] of Object.entries(mcps)) {
40
+ result.mcpServers.push({ name, config });
41
+ mcpJsonNames.add(name);
42
+ }
43
+ } catch { /* invalid JSON */ }
44
+ }
45
+
46
+ // ── MCP Servers + hooks from settings (legacy + hooks/permissions) ───────
47
+ const settingsPaths = [
48
+ join(targetPath, '.claude', 'settings.json'),
49
+ join(targetPath, '.claude', 'settings.local.json'),
50
+ ];
51
+
52
+ for (const sp of settingsPaths) {
53
+ if (existsSync(sp)) {
54
+ try {
55
+ const settings = JSON.parse(readFileSync(sp, 'utf8'));
56
+ // Legacy MCP entries — skip if already in .mcp.json, but allow later settings files to override earlier ones
57
+ const mcps = settings.mcpServers || {};
58
+ for (const [name, config] of Object.entries(mcps)) {
59
+ if (mcpJsonNames.has(name)) continue; // .mcp.json takes priority
60
+ const idx = result.mcpServers.findIndex(m => m.name === name);
61
+ if (idx >= 0) {
62
+ result.mcpServers[idx] = { name, config }; // later file wins
63
+ } else {
64
+ result.mcpServers.push({ name, config });
65
+ }
66
+ }
67
+
68
+ // Collect hooks
69
+ if (settings.hooks) {
70
+ for (const [event, eventHooks] of Object.entries(settings.hooks)) {
71
+ for (const group of (Array.isArray(eventHooks) ? eventHooks : [])) {
72
+ for (const h of (group.hooks || [])) {
73
+ result.hooks.push(`${event}:${h.type || 'unknown'}`);
74
+ }
75
+ }
76
+ }
77
+ }
78
+ } catch { /* invalid JSON */ }
79
+ }
80
+ }
81
+
82
+ // ── Plugins from global config ──────────────────────────────────────────
83
+ const pluginsPath = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');
84
+ if (existsSync(pluginsPath)) {
85
+ try {
86
+ const pluginsData = JSON.parse(readFileSync(pluginsPath, 'utf8'));
87
+ const plugins = pluginsData.plugins || {};
88
+ result.plugins = Object.keys(plugins);
89
+ result.superpowers = 'superpowers@claude-plugins-official' in plugins;
90
+ } catch { /* invalid JSON */ }
91
+ }
92
+
93
+ // ── Custom skills ───────────────────────────────────────────────────────
94
+ const skillsPath = join(targetPath, '.claude', 'skills');
95
+ if (existsSync(skillsPath)) {
96
+ try {
97
+ result.customSkills = readdirSync(skillsPath).filter(f => !f.startsWith('.'));
98
+ } catch { /* ignore */ }
99
+ }
100
+
101
+ // ── Custom commands ─────────────────────────────────────────────────────
102
+ const commandsPath = join(targetPath, '.claude', 'commands');
103
+ if (existsSync(commandsPath)) {
104
+ try {
105
+ result.customCommands = readdirSync(commandsPath).filter(f => f.endsWith('.md'));
106
+ } catch { /* ignore */ }
107
+ }
108
+ } catch {
109
+ // Fail-safe
110
+ }
111
+
112
+ return result;
113
+ }
114
+
115
+ /**
116
+ * Cross-reference configured MCPs with the MCP registry's phase matrix.
117
+ *
118
+ * @param {Array<{name: string}>} mcpServers - Detected MCP server list
119
+ * @param {Object} phaseMatrix - From mcp-registry.json → phaseMatrix
120
+ * @param {string} currentPhase - Current morph-spec phase
121
+ * @returns {{ available: string[], recommended: string[], missing: string[] }}
122
+ */
123
+ export function mapMcpsToPhases(mcpServers, phaseMatrix, currentPhase) {
124
+ const configuredNames = new Set(mcpServers.map(m => m.name.toLowerCase()));
125
+ const recommended = (phaseMatrix[currentPhase] || []);
126
+
127
+ return {
128
+ available: recommended.filter(name => configuredNames.has(name)),
129
+ recommended,
130
+ missing: recommended.filter(name => !configuredNames.has(name)),
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Format a human-readable one-liner summary of detected config.
136
+ *
137
+ * @param {{ plugins: string[], mcpServers: Array<{name: string}>, superpowers: boolean }} config
138
+ * @returns {string}
139
+ */
140
+ export function formatConfigSummary(config) {
141
+ const parts = [];
142
+ if (config.mcpServers.length > 0) {
143
+ parts.push(`MCPs: ${config.mcpServers.map(m => m.name).join(', ')}`);
144
+ }
145
+ if (config.plugins.length > 0) {
146
+ parts.push(`Plugins: ${config.plugins.length}`);
147
+ }
148
+ if (config.superpowers) {
149
+ parts.push('superpowers: yes');
150
+ }
151
+ return parts.join(' | ') || 'No Claude config detected';
152
+ }