@polymorphism-tech/morph-spec 4.2.0 → 4.3.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 (140) hide show
  1. package/CLAUDE.md +108 -946
  2. package/bin/morph-spec.js +284 -9
  3. package/bin/task-manager.cjs +102 -14
  4. package/bin/validate.js +4 -4
  5. package/docs/{v3.0 → next-generation}/AGENTS.md +1 -1
  6. package/docs/next-generation/CONTEXT-OPTIMIZATION.md +267 -0
  7. package/docs/next-generation/EXECUTION-FLOW.md +274 -0
  8. package/docs/next-generation/META-PROMPTS.md +235 -0
  9. package/docs/next-generation/MIGRATION-GUIDE.md +253 -0
  10. package/docs/next-generation/THREAD-MANAGEMENT.md +240 -0
  11. package/package.json +5 -5
  12. package/src/commands/agents/agents-fuse.js +97 -0
  13. package/src/commands/agents/micro-agent.js +112 -0
  14. package/src/commands/agents/spawn-team.js +69 -4
  15. package/src/commands/agents/squad-template.js +146 -0
  16. package/src/commands/analytics/analytics.js +176 -0
  17. package/src/commands/context/context-prime.js +63 -0
  18. package/src/commands/context/core-four.js +54 -0
  19. package/src/commands/mcp/mcp.js +102 -0
  20. package/src/commands/project/detect-agents.js +32 -2
  21. package/src/commands/project/detect.js +11 -1
  22. package/src/commands/project/doctor.js +573 -356
  23. package/src/commands/project/init.js +9 -2
  24. package/src/commands/project/update.js +13 -3
  25. package/src/commands/state/advance-phase.js +448 -416
  26. package/src/commands/state/state.js +14 -12
  27. package/src/commands/tasks/task.js +1 -1
  28. package/src/commands/templates/template-render.js +80 -1
  29. package/src/commands/threads/thread-template.js +103 -0
  30. package/src/commands/threads/threads.js +261 -0
  31. package/src/commands/trust/trust.js +205 -0
  32. package/src/{orchestrator.js → core/orchestrator.js} +8 -8
  33. package/src/core/state/state-manager.js +37 -17
  34. package/src/core/workflows/workflow-detector.js +114 -3
  35. package/src/lib/agents/micro-agent-factory.js +161 -0
  36. package/src/lib/analytics/analytics-engine.js +345 -0
  37. package/src/lib/checkpoints/checkpoint-hooks.js +298 -258
  38. package/src/lib/context/context-bundler.js +240 -0
  39. package/src/lib/context/context-optimizer.js +212 -0
  40. package/src/lib/context/context-tracker.js +273 -0
  41. package/src/lib/context/core-four-tracker.js +201 -0
  42. package/src/lib/context/mcp-optimizer.js +200 -0
  43. package/src/lib/detectors/index.js +1 -1
  44. package/src/lib/detectors/standards-generator.js +77 -17
  45. package/src/lib/detectors/structure-detector.js +67 -39
  46. package/src/lib/execution/fusion-executor.js +304 -0
  47. package/src/lib/execution/parallel-executor.js +270 -0
  48. package/src/lib/generators/context-generator.js +3 -3
  49. package/src/lib/generators/recap-generator.js +32 -12
  50. package/src/lib/hooks/hook-executor.js +169 -0
  51. package/src/lib/hooks/stop-hook-executor.js +286 -0
  52. package/src/lib/hops/hop-composer.js +221 -0
  53. package/src/lib/threads/thread-coordinator.js +238 -0
  54. package/src/lib/threads/thread-manager.js +317 -0
  55. package/src/lib/tracking/artifact-trail.js +202 -0
  56. package/src/lib/trust/trust-manager.js +269 -0
  57. package/src/lib/validators/design-system/design-system-validator.js +2 -2
  58. package/src/lib/validators/validation-runner.js +14 -30
  59. package/src/utils/hooks-installer.js +69 -0
  60. package/stacks/blazor-azure/.morph/config/agents.json +72 -3
  61. package/stacks/nextjs-supabase/.morph/config/agents.json +3 -3
  62. package/docs/llm-interaction-config.md +0 -735
  63. package/docs/v3.0/EXECUTION-FLOW.md +0 -1304
  64. package/src/commands/utils/migrate-state.js +0 -158
  65. package/src/commands/utils/upgrade.js +0 -346
  66. package/src/lib/validators/architecture-validator.js +0 -60
  67. package/src/lib/validators/content-validator.js +0 -164
  68. package/src/lib/validators/package-validator.js +0 -61
  69. package/src/lib/validators/ui-contrast-validator.js +0 -44
  70. package/stacks/blazor-azure/.claude/commands/morph-apply.md +0 -221
  71. package/stacks/blazor-azure/.claude/commands/morph-archive.md +0 -79
  72. package/stacks/blazor-azure/.claude/commands/morph-deploy.md +0 -529
  73. package/stacks/blazor-azure/.claude/commands/morph-infra.md +0 -209
  74. package/stacks/blazor-azure/.claude/commands/morph-preflight.md +0 -227
  75. package/stacks/blazor-azure/.claude/commands/morph-proposal.md +0 -122
  76. package/stacks/blazor-azure/.claude/commands/morph-status.md +0 -86
  77. package/stacks/blazor-azure/.claude/commands/morph-troubleshoot.md +0 -122
  78. package/stacks/blazor-azure/.claude/skills/level-0-meta/README.md +0 -7
  79. package/stacks/blazor-azure/.claude/skills/level-0-meta/code-review.md +0 -226
  80. package/stacks/blazor-azure/.claude/skills/level-0-meta/morph-checklist.md +0 -117
  81. package/stacks/blazor-azure/.claude/skills/level-0-meta/simulation-checklist.md +0 -77
  82. package/stacks/blazor-azure/.claude/skills/level-1-workflows/README.md +0 -7
  83. package/stacks/blazor-azure/.claude/skills/level-1-workflows/morph-replicate.md +0 -213
  84. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-clarify.md +0 -131
  85. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-design.md +0 -213
  86. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-setup.md +0 -106
  87. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-tasks.md +0 -164
  88. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-uiux.md +0 -169
  89. package/stacks/blazor-azure/.claude/skills/level-2-domains/README.md +0 -14
  90. package/stacks/blazor-azure/.claude/skills/level-2-domains/ai-agents/ai-system-architect.md +0 -192
  91. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/po-pm-advisor.md +0 -197
  92. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/prompt-engineer.md +0 -189
  93. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/seo-growth-hacker.md +0 -320
  94. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/standards-architect.md +0 -156
  95. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/api-designer.md +0 -59
  96. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/dotnet-senior.md +0 -77
  97. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ef-modeler.md +0 -58
  98. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/hangfire-orchestrator.md +0 -126
  99. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ms-agent-expert.md +0 -45
  100. package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/blazor-builder.md +0 -210
  101. package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/nextjs-expert.md +0 -154
  102. package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/ui-ux-designer.md +0 -191
  103. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/azure-architect.md +0 -142
  104. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/azure-deploy-specialist.md +0 -699
  105. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/bicep-architect.md +0 -126
  106. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/container-specialist.md +0 -131
  107. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/devops-engineer.md +0 -119
  108. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/asaas-financial.md +0 -130
  109. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/azure-identity.md +0 -142
  110. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/clerk-auth.md +0 -108
  111. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/hangfire-orchestrator.md +0 -64
  112. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/resend-email.md +0 -119
  113. package/stacks/blazor-azure/.claude/skills/level-2-domains/quality/code-analyzer.md +0 -235
  114. package/stacks/blazor-azure/.claude/skills/level-2-domains/quality/testing-specialist.md +0 -126
  115. package/stacks/blazor-azure/.claude/skills/level-3-technologies/README.md +0 -7
  116. package/stacks/blazor-azure/.claude/skills/level-4-patterns/README.md +0 -7
  117. package/stacks/blazor-azure/.morph/archive/.gitkeep +0 -25
  118. package/stacks/blazor-azure/.morph/features/.gitkeep +0 -25
  119. package/stacks/blazor-azure/.morph/schemas/agent.schema.json +0 -296
  120. package/stacks/blazor-azure/.morph/schemas/tasks.schema.json +0 -220
  121. package/stacks/blazor-azure/.morph/specs/.gitkeep +0 -20
  122. package/stacks/blazor-azure/.morph/test-infra/example.bicep +0 -59
  123. package/stacks/nextjs-supabase/.claude/commands/morph-apply.md +0 -221
  124. package/stacks/nextjs-supabase/.claude/commands/morph-archive.md +0 -79
  125. package/stacks/nextjs-supabase/.claude/commands/morph-deploy.md +0 -529
  126. package/stacks/nextjs-supabase/.claude/commands/morph-infra.md +0 -209
  127. package/stacks/nextjs-supabase/.claude/commands/morph-preflight.md +0 -227
  128. package/stacks/nextjs-supabase/.claude/commands/morph-proposal.md +0 -122
  129. package/stacks/nextjs-supabase/.claude/commands/morph-status.md +0 -86
  130. package/stacks/nextjs-supabase/.claude/commands/morph-troubleshoot.md +0 -122
  131. package/stacks/nextjs-supabase/.claude/settings.local.json +0 -6
  132. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/backend/dotnet-supabase.md +0 -244
  133. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/frontend/nextjs-supabase.md +0 -335
  134. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/infrastructure/easypanel-deployer.md +0 -189
  135. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/integrations/supabase-expert.md +0 -50
  136. /package/docs/{v3.0 → next-generation}/ANALYSIS.md +0 -0
  137. /package/docs/{v3.0 → next-generation}/ARCHITECTURE.md +0 -0
  138. /package/docs/{v3.0 → next-generation}/FEATURES.md +0 -0
  139. /package/docs/{v3.0 → next-generation}/README.md +0 -0
  140. /package/docs/{v3.0 → next-generation}/ROADMAP.md +0 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Squad Template CLI command
3
+ *
4
+ * Usage:
5
+ * morph-spec squad-template list
6
+ * morph-spec squad-template use <template-id> --feature=<feature>
7
+ * morph-spec squad-template show <template-id>
8
+ */
9
+
10
+ import chalk from 'chalk';
11
+ import { readFileSync, existsSync, readdirSync } from 'fs';
12
+ import { join } from 'path';
13
+
14
+ const SQUAD_TEMPLATES_DIR = join(process.cwd(), 'framework/squad-templates');
15
+
16
+ function loadTemplate(id) {
17
+ const filePath = join(SQUAD_TEMPLATES_DIR, `${id}.json`);
18
+ if (!existsSync(filePath)) return null;
19
+ try {
20
+ return JSON.parse(readFileSync(filePath, 'utf8'));
21
+ } catch {
22
+ return null;
23
+ }
24
+ }
25
+
26
+ function listAllTemplates() {
27
+ if (!existsSync(SQUAD_TEMPLATES_DIR)) return [];
28
+ return readdirSync(SQUAD_TEMPLATES_DIR)
29
+ .filter(f => f.endsWith('.json'))
30
+ .map(f => {
31
+ try {
32
+ return JSON.parse(readFileSync(join(SQUAD_TEMPLATES_DIR, f), 'utf8'));
33
+ } catch {
34
+ return null;
35
+ }
36
+ })
37
+ .filter(Boolean);
38
+ }
39
+
40
+ export async function squadTemplateListCommand(options) {
41
+ try {
42
+ const templates = listAllTemplates();
43
+
44
+ if (templates.length === 0) {
45
+ console.log(chalk.gray('\n No squad templates found.\n'));
46
+ return;
47
+ }
48
+
49
+ console.log(chalk.cyan(`\n Squad Templates (${templates.length})\n`));
50
+
51
+ for (const t of templates) {
52
+ console.log(` ${chalk.bold(t.id)} — ${t.name}`);
53
+ console.log(` ${chalk.gray(t.description)}`);
54
+ console.log(` Agents: ${t.agents.map(a => a.id).join(', ')}`);
55
+ console.log(` Parallel: ${t.parallel ? chalk.green('yes') : chalk.gray('no')} | Coordination: ${t.coordination}`);
56
+ console.log(` Gain: ${chalk.cyan(t.estimatedParallelGain)}`);
57
+ console.log('');
58
+ }
59
+ } catch (err) {
60
+ console.error(chalk.red(`Error: ${err.message}`));
61
+ process.exit(1);
62
+ }
63
+ }
64
+
65
+ export async function squadTemplateUseCommand(templateId, options) {
66
+ try {
67
+ if (!options.feature) {
68
+ console.error(chalk.red('--feature is required'));
69
+ process.exit(1);
70
+ }
71
+
72
+ const template = loadTemplate(templateId);
73
+ if (!template) {
74
+ console.error(chalk.red(`Template not found: ${templateId}`));
75
+ console.error(chalk.gray('Run `morph-spec squad-template list` to see available templates'));
76
+ process.exit(1);
77
+ }
78
+
79
+ console.log(chalk.cyan(`\n Configuring Squad Template: ${templateId}\n`));
80
+ console.log(` Feature: ${options.feature}`);
81
+ console.log(` Template: ${template.name}`);
82
+ console.log(` Agents: ${template.agents.length}`);
83
+ console.log(` Parallel: ${template.parallel ? 'yes' : 'no'}`);
84
+
85
+ // Show spawn-team configuration
86
+ console.log(chalk.yellow('\n To spawn this squad, use the Task tool with these configs:\n'));
87
+
88
+ for (const agent of template.agents) {
89
+ console.log(` Agent: ${chalk.bold(agent.id)} (${agent.role})`);
90
+ console.log(` Standards: ${agent.standards?.join(', ') || '(none)'}`);
91
+ const deliverable = template.deliverables?.[agent.id];
92
+ if (deliverable) {
93
+ console.log(` Deliverable: ${deliverable}`);
94
+ }
95
+ console.log('');
96
+ }
97
+
98
+ console.log(chalk.gray(' See: morph-spec spawn-team --template=' + templateId + ' --feature=' + options.feature));
99
+ console.log('');
100
+ } catch (err) {
101
+ console.error(chalk.red(`Error: ${err.message}`));
102
+ process.exit(1);
103
+ }
104
+ }
105
+
106
+ export async function squadTemplateShowCommand(templateId, options) {
107
+ try {
108
+ const template = loadTemplate(templateId);
109
+ if (!template) {
110
+ console.error(chalk.red(`Template not found: ${templateId}`));
111
+ process.exit(1);
112
+ }
113
+
114
+ if (options.json) {
115
+ console.log(JSON.stringify(template, null, 2));
116
+ return;
117
+ }
118
+
119
+ console.log(chalk.cyan(`\n Squad Template: ${template.id}\n`));
120
+ console.log(` Name: ${template.name}`);
121
+ console.log(` Description: ${template.description}`);
122
+ console.log(` Parallel: ${template.parallel ? chalk.green('yes') : chalk.gray('no')}`);
123
+ console.log(` Coordinator: ${template.coordination}`);
124
+ console.log(` Wait all: ${template.waitForAll ? 'yes' : 'no'}`);
125
+ console.log(` Gain: ${template.estimatedParallelGain}`);
126
+
127
+ console.log('\n Agents:');
128
+ for (const agent of template.agents) {
129
+ console.log(` ${chalk.bold(agent.id)} — ${agent.role} (${agent.domain})`);
130
+ if (agent.standards?.length > 0) {
131
+ console.log(` Standards: ${agent.standards.join(', ')}`);
132
+ }
133
+ }
134
+
135
+ if (template.deliverables) {
136
+ console.log('\n Deliverables:');
137
+ for (const [agent, deliverable] of Object.entries(template.deliverables)) {
138
+ console.log(` ${agent}: ${deliverable}`);
139
+ }
140
+ }
141
+ console.log('');
142
+ } catch (err) {
143
+ console.error(chalk.red(`Error: ${err.message}`));
144
+ process.exit(1);
145
+ }
146
+ }
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Analytics CLI command — Metrics dashboards
3
+ *
4
+ * Usage:
5
+ * morph-spec analytics feature <name>
6
+ * morph-spec analytics context <name>
7
+ * morph-spec analytics project [--period=30d]
8
+ */
9
+
10
+ import chalk from 'chalk';
11
+ import {
12
+ getFeatureAnalytics, getProjectAnalytics,
13
+ generateAsciiChart, generateTimelineChart,
14
+ recordEvent
15
+ } from '../../lib/analytics/analytics-engine.js';
16
+ import { readFileSync, existsSync } from 'fs';
17
+ import { join } from 'path';
18
+
19
+ // ============================================================================
20
+ // Feature Analytics Dashboard
21
+ // ============================================================================
22
+
23
+ export async function analyticsFeatureCommand(featureName, options) {
24
+ try {
25
+ const analytics = getFeatureAnalytics(featureName);
26
+
27
+ console.log(chalk.cyan(`\n Feature Analytics — ${featureName}\n`));
28
+ console.log(' ' + '─'.repeat(50));
29
+
30
+ console.log(`\n Threads:`);
31
+ for (const [status, count] of Object.entries(analytics.threads)) {
32
+ console.log(` ${status.padEnd(12)} ${count}`);
33
+ }
34
+
35
+ console.log(`\n Performance:`);
36
+ console.log(` Checkpoint pass rate: ${analytics.checkpointPassRate}%`);
37
+ console.log(` Total duration: ${formatDuration(analytics.totalDuration)}`);
38
+ console.log(` Total tool calls: ${analytics.totalToolCalls}`);
39
+ console.log(` Total tokens used: ${analytics.totalTokensUsed.toLocaleString()}`);
40
+ console.log(` Context optimizations: ${analytics.optimizationEvents}`);
41
+
42
+ if (Object.keys(analytics.threads).length > 0) {
43
+ console.log('\n' + generateAsciiChart(analytics.threads, { title: 'Threads by Status' }));
44
+ }
45
+
46
+ console.log('');
47
+ } catch (err) {
48
+ console.error(chalk.red(`Error: ${err.message}`));
49
+ process.exit(1);
50
+ }
51
+ }
52
+
53
+ // ============================================================================
54
+ // Context Analytics Dashboard
55
+ // ============================================================================
56
+
57
+ export async function analyticsContextCommand(featureName, options) {
58
+ try {
59
+ // Load context events from JSONL
60
+ const contextLogPath = join(process.cwd(), '.morph/analytics/context-log.jsonl');
61
+
62
+ if (!existsSync(contextLogPath)) {
63
+ console.log(chalk.gray(` No context analytics found for: ${featureName}`));
64
+ console.log(chalk.gray(' Run a session with context tracking to populate analytics.\n'));
65
+ return;
66
+ }
67
+
68
+ const lines = readFileSync(contextLogPath, 'utf8').trim().split('\n').filter(Boolean);
69
+ const events = lines.map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
70
+ const featureEvents = events.filter(e => e.feature === featureName);
71
+
72
+ const tokenSamples = featureEvents.filter(e => e.type === 'token_usage');
73
+ const criticalMoments = featureEvents.filter(e => e.type === 'context_critical');
74
+ const optimizations = featureEvents.filter(e => e.type === 'context_optimized');
75
+
76
+ console.log(chalk.cyan(`\n Context Analytics — ${featureName}\n`));
77
+ console.log(' ' + '─'.repeat(50));
78
+
79
+ if (tokenSamples.length === 0) {
80
+ console.log(chalk.gray(' No token usage data available.\n'));
81
+ return;
82
+ }
83
+
84
+ const avgTokens = Math.round(tokenSamples.reduce((s, e) => s + (e.data?.tokensUsed || 0), 0) / tokenSamples.length);
85
+ const peakTokens = Math.max(...tokenSamples.map(e => e.data?.tokensUsed || 0));
86
+ const avgUtilization = Math.round(tokenSamples.reduce((s, e) => s + (e.data?.utilization || 0), 0) / tokenSamples.length * 100);
87
+
88
+ console.log(`\n Token Usage:`);
89
+ console.log(` Average tokens: ${avgTokens.toLocaleString()}`);
90
+ console.log(` Peak tokens: ${peakTokens.toLocaleString()}`);
91
+ console.log(` Avg utilization: ${avgUtilization}%`);
92
+ console.log(` Critical moments: ${criticalMoments.length}`);
93
+ console.log(` Optimizations applied: ${optimizations.length}`);
94
+
95
+ // Optimization opportunities
96
+ if (criticalMoments.length > 0) {
97
+ console.log(chalk.yellow('\n ⚠ Optimization Opportunities:'));
98
+ const recs = criticalMoments.flatMap(e => e.data?.recommendations || []);
99
+ const uniqueStrategies = [...new Set(recs.map(r => r.strategy))];
100
+ for (const strategy of uniqueStrategies) {
101
+ const rec = recs.find(r => r.strategy === strategy);
102
+ console.log(` • ${strategy}: ${rec?.description || ''}`);
103
+ console.log(` Savings: ${rec?.estimatedSavings || 'unknown'}`);
104
+ }
105
+ }
106
+
107
+ console.log('');
108
+ } catch (err) {
109
+ console.error(chalk.red(`Error: ${err.message}`));
110
+ process.exit(1);
111
+ }
112
+ }
113
+
114
+ // ============================================================================
115
+ // Project-Wide Dashboard
116
+ // ============================================================================
117
+
118
+ export async function analyticsProjectCommand(options) {
119
+ try {
120
+ const analytics = getProjectAnalytics();
121
+
122
+ console.log(chalk.cyan(`\n Project Analytics Dashboard — ${analytics.period}\n`));
123
+ console.log(' ' + '═'.repeat(55));
124
+
125
+ console.log(`\n 📊 Overview`);
126
+ console.log(` Features tracked: ${analytics.features}`);
127
+ console.log(` Total threads: ${analytics.threads.total}`);
128
+ console.log(` Completed: ${analytics.threads.completed}`);
129
+ console.log(` Failed: ${analytics.threads.failed}`);
130
+
131
+ console.log(`\n ✓ Checkpoints`);
132
+ console.log(` Total run: ${analytics.checkpoints.total}`);
133
+ console.log(` Passed: ${analytics.checkpoints.passed}`);
134
+ console.log(` Pass rate: ${analytics.checkpoints.passRate}%`);
135
+
136
+ if (analytics.checkpoints.total > 0) {
137
+ const bar = buildBar(analytics.checkpoints.passRate, 30);
138
+ console.log(` ${bar}`);
139
+ }
140
+
141
+ console.log(`\n ⫸ Parallelization`);
142
+ console.log(` Avg concurrency: ${analytics.parallelization.avgConcurrency}x`);
143
+ console.log(` Parallel threads: ${analytics.parallelization.parallelThreads}`);
144
+
145
+ console.log(`\n ⚡ Trust & Auto-Approval`);
146
+ console.log(` Auto-approvals: ${analytics.trust.autoApprovals}`);
147
+ console.log(` Trust level changes: ${analytics.trust.trustChanges}`);
148
+
149
+ console.log(`\n 🧠 Context`);
150
+ console.log(` Avg tokens/session: ${analytics.context.avgTokensPerSession.toLocaleString()}`);
151
+ console.log(` Optimizations: ${analytics.context.optimizations}`);
152
+
153
+ console.log('\n ' + '─'.repeat(55));
154
+ console.log('');
155
+ } catch (err) {
156
+ console.error(chalk.red(`Error: ${err.message}`));
157
+ process.exit(1);
158
+ }
159
+ }
160
+
161
+ // ============================================================================
162
+ // Helpers
163
+ // ============================================================================
164
+
165
+ function formatDuration(seconds) {
166
+ if (!seconds) return '—';
167
+ if (seconds < 60) return `${seconds}s`;
168
+ if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
169
+ return `${Math.floor(seconds / 3600)}h ${Math.floor((seconds % 3600) / 60)}m`;
170
+ }
171
+
172
+ function buildBar(pct, width) {
173
+ const filled = Math.round(pct / 100 * width);
174
+ const color = pct >= 90 ? chalk.green : pct >= 70 ? chalk.yellow : chalk.red;
175
+ return color('[' + '█'.repeat(filled) + '░'.repeat(width - filled) + `] ${pct}%`);
176
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Context Prime CLI command — Load minimal context priming files
3
+ *
4
+ * Usage:
5
+ * morph-spec prime feature (outputs feature-prime.md to stdout)
6
+ * morph-spec prime bug
7
+ * morph-spec prime refactor
8
+ * morph-spec prime design
9
+ * morph-spec prime infra
10
+ */
11
+
12
+ import chalk from 'chalk';
13
+ import { readFileSync, existsSync } from 'fs';
14
+ import { join } from 'path';
15
+
16
+ const PRIME_FILES = {
17
+ feature: '.morph/context/feature-prime.md',
18
+ bug: '.morph/context/bug-prime.md',
19
+ refactor: '.morph/context/refactor-prime.md',
20
+ design: '.morph/context/design-prime.md',
21
+ infra: '.morph/context/infra-prime.md'
22
+ };
23
+
24
+ const PRIME_TYPES = Object.keys(PRIME_FILES);
25
+
26
+ export async function contextPrimeCommand(type, options) {
27
+ if (!type) {
28
+ console.log(chalk.cyan('\n Available prime types:\n'));
29
+ for (const [key, path] of Object.entries(PRIME_FILES)) {
30
+ const exists = existsSync(join(process.cwd(), path));
31
+ const status = exists ? chalk.green('✓') : chalk.red('✗');
32
+ console.log(` ${status} ${key.padEnd(10)} → ${path}`);
33
+ }
34
+ console.log('');
35
+ console.log(chalk.gray(' Usage: morph-spec prime <type>'));
36
+ console.log(chalk.gray(' Example: morph-spec prime feature\n'));
37
+ return;
38
+ }
39
+
40
+ if (!PRIME_TYPES.includes(type)) {
41
+ console.error(chalk.red(`Unknown prime type: ${type}`));
42
+ console.error(chalk.gray(`Valid types: ${PRIME_TYPES.join(', ')}`));
43
+ process.exit(1);
44
+ }
45
+
46
+ const filePath = join(process.cwd(), PRIME_FILES[type]);
47
+
48
+ if (!existsSync(filePath)) {
49
+ console.error(chalk.red(`Prime file not found: ${filePath}`));
50
+ console.error(chalk.gray('Run `morph-spec init` to initialize MORPH-SPEC files.'));
51
+ process.exit(1);
52
+ }
53
+
54
+ const content = readFileSync(filePath, 'utf8');
55
+
56
+ if (options.json) {
57
+ console.log(JSON.stringify({ type, path: filePath, content }));
58
+ return;
59
+ }
60
+
61
+ // Output to stdout — designed to be read into context
62
+ console.log(content);
63
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Core Four CLI command — Context/Model/Prompt/Tools dashboard
3
+ *
4
+ * Usage:
5
+ * morph-spec core-four <feature>
6
+ * morph-spec core-four <feature> --json
7
+ */
8
+
9
+ import chalk from 'chalk';
10
+ import { getCoreFour, formatCoreFour, initCoreFour } from '../../lib/context/core-four-tracker.js';
11
+ import { loadState } from '../../core/state/state-manager.js';
12
+
13
+ export async function coreFourCommand(featureName, options) {
14
+ try {
15
+ // Initialize with defaults if not already set
16
+ initCoreFour({
17
+ model: 'claude-sonnet-4-6',
18
+ contextWindow: 200000,
19
+ feature: featureName
20
+ });
21
+
22
+ // Try to populate context breakdown from feature state
23
+ try {
24
+ const state = loadState(false);
25
+ if (state?.features?.[featureName]) {
26
+ const feature = state.features[featureName];
27
+ // Attempt to estimate context breakdown from known files
28
+ const breakdown = {
29
+ 'CLAUDE.md': 23000,
30
+ standards: feature.activeAgents ? feature.activeAgents.length * 500 : 0,
31
+ spec: feature.outputs?.includes('spec') ? 8000 : 0,
32
+ conversation: 5000,
33
+ other: 2000
34
+ };
35
+ const { updateContextBreakdown } = await import('../../lib/context/core-four-tracker.js');
36
+ updateContextBreakdown(breakdown);
37
+ }
38
+ } catch {
39
+ // Best-effort
40
+ }
41
+
42
+ if (options.json) {
43
+ const data = getCoreFour();
44
+ console.log(JSON.stringify(data, null, 2));
45
+ return;
46
+ }
47
+
48
+ console.log('\n' + formatCoreFour());
49
+ console.log('');
50
+ } catch (err) {
51
+ console.error(chalk.red(`Error: ${err.message}`));
52
+ process.exit(1);
53
+ }
54
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * MCP CLI command — MCP server analysis and optimization
3
+ *
4
+ * Usage:
5
+ * morph-spec mcp optimize
6
+ * morph-spec mcp list
7
+ */
8
+
9
+ import chalk from 'chalk';
10
+ import { analyzeMCP } from '../../lib/context/mcp-optimizer.js';
11
+
12
+ const STATUS_COLORS = {
13
+ used: chalk.green,
14
+ unused: chalk.red,
15
+ unknown: chalk.gray
16
+ };
17
+
18
+ function formatTokens(n) {
19
+ return n >= 1000 ? `${(n / 1000).toFixed(1)}K` : String(n);
20
+ }
21
+
22
+ export async function mcpOptimizeCommand(options) {
23
+ try {
24
+ const analysis = analyzeMCP();
25
+
26
+ if (!analysis.found) {
27
+ console.log(chalk.yellow(`\n ⚠ ${analysis.message}\n`));
28
+ return;
29
+ }
30
+
31
+ console.log(chalk.cyan('\n MCP Optimization Analysis\n'));
32
+ console.log(' ' + '─'.repeat(60));
33
+
34
+ console.log(`\n Settings file: ${chalk.gray(analysis.settingsPath)}`);
35
+ console.log(` Total servers: ${analysis.totalServers}`);
36
+ console.log(` Total context overhead: ${chalk.yellow(formatTokens(analysis.totalOverhead) + ' tokens')}`);
37
+
38
+ if (analysis.unusedCount > 0) {
39
+ console.log(` Unused servers: ${chalk.red(analysis.unusedCount)}`);
40
+ console.log(` Potential savings: ${chalk.green(formatTokens(analysis.potentialSavings) + ' tokens')}`);
41
+ }
42
+
43
+ if (analysis.recommendations.length === 0) {
44
+ console.log(chalk.green('\n ✓ MCP configuration looks optimized!\n'));
45
+ return;
46
+ }
47
+
48
+ console.log(chalk.yellow('\n Recommendations:\n'));
49
+ for (const rec of analysis.recommendations) {
50
+ console.log(` ${chalk.bold(rec.title)}`);
51
+ console.log(` ${rec.description}`);
52
+ console.log(` Savings: ${chalk.green(formatTokens(rec.estimatedSavings) + ' tokens')}`);
53
+ console.log(` Action: ${chalk.cyan(rec.action)}`);
54
+ console.log('');
55
+ }
56
+
57
+ console.log(' ' + '─'.repeat(60));
58
+ console.log('');
59
+ } catch (err) {
60
+ console.error(chalk.red(`Error: ${err.message}`));
61
+ process.exit(1);
62
+ }
63
+ }
64
+
65
+ export async function mcpListCommand(options) {
66
+ try {
67
+ const analysis = analyzeMCP();
68
+
69
+ if (!analysis.found) {
70
+ console.log(chalk.yellow(`\n ⚠ ${analysis.message}\n`));
71
+ return;
72
+ }
73
+
74
+ if (analysis.servers.length === 0) {
75
+ console.log(chalk.gray('\n No MCP servers configured.\n'));
76
+ return;
77
+ }
78
+
79
+ console.log(chalk.cyan('\n Configured MCP Servers\n'));
80
+ console.log(' ' + 'Name'.padEnd(25) + ' ' + 'Status'.padEnd(10) + ' ' + 'Overhead');
81
+ console.log(' ' + '─'.repeat(50));
82
+
83
+ for (const server of analysis.servers) {
84
+ const colorFn = STATUS_COLORS[server.usageStatus] || chalk.white;
85
+ console.log(
86
+ ' ' +
87
+ server.name.padEnd(25) + ' ' +
88
+ colorFn(server.usageStatus.padEnd(10)) + ' ' +
89
+ formatTokens(server.overhead) + ' tokens'
90
+ );
91
+ }
92
+
93
+ console.log('\n ' + `Total overhead: ${formatTokens(analysis.totalOverhead)} tokens`);
94
+ if (analysis.recommendations.length > 0) {
95
+ console.log(chalk.yellow(` Run 'morph-spec mcp optimize' for recommendations`));
96
+ }
97
+ console.log('');
98
+ } catch (err) {
99
+ console.error(chalk.red(`Error: ${err.message}`));
100
+ process.exit(1);
101
+ }
102
+ }
@@ -9,7 +9,7 @@ import { loadStandardsForAgent, getStandardsListForAgent } from '../../lib/stand
9
9
  import { resolveAgentsConfigPath } from '../../lib/stacks/stack-resolver.js';
10
10
 
11
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
- const frameworkRoot = join(__dirname, '..', '..');
12
+ const frameworkRoot = join(__dirname, '..', '..', '..');
13
13
 
14
14
  function loadAgentsConfig() {
15
15
  try {
@@ -123,6 +123,34 @@ export function detectAgentsCommand(input, options) {
123
123
  const config = loadAgentsConfig();
124
124
  const result = detectAgents(userInput, config);
125
125
 
126
+ // Stack-aware filtering: penalize stack-mismatched agents
127
+ try {
128
+ const projectConfigPath = join(process.cwd(), '.morph/config/config.json');
129
+ const projectConfig = JSON.parse(readFileSync(projectConfigPath, 'utf8'));
130
+ const stack = projectConfig?.project?.stack || projectConfig?.project?.language || '';
131
+ const isJsStack = stack === 'nodejs' || stack === 'javascript' || stack === 'nextjs';
132
+
133
+ if (isJsStack) {
134
+ const dotnetKeywords = ['dotnet', '.net', 'csharp', 'c#', 'blazor', 'razor', 'azure', 'bicep',
135
+ 'ef core', 'entity framework', 'hangfire', 'mudblazor', 'fluent ui', 'asp.net'];
136
+ const hasExplicitDotnetRequest = dotnetKeywords.some(k => userInput.toLowerCase().includes(k));
137
+
138
+ if (!hasExplicitDotnetRequest) {
139
+ const dotnetAgents = new Set([
140
+ 'dotnet-senior', 'azure-architect', 'ef-modeler', 'bicep-architect',
141
+ 'blazor-builder', 'blazor-concurrency-validator', 'hangfire-orchestrator',
142
+ 'azure-deploy-specialist'
143
+ ]);
144
+ result.core = result.core.filter(id => !dotnetAgents.has(id));
145
+ result.specialists = result.specialists.filter(id => !dotnetAgents.has(id));
146
+ result.all = result.all.filter(id => !dotnetAgents.has(id));
147
+ result.matches = result.matches.filter(m => !dotnetAgents.has(m.id));
148
+ }
149
+ }
150
+ } catch {
151
+ // Config not found or unreadable — skip stack filtering
152
+ }
153
+
126
154
  // Enrich with complexity analysis
127
155
  try {
128
156
  result.complexity = analyzeRequestComplexity(userInput);
@@ -141,7 +169,9 @@ export function detectAgentsCommand(input, options) {
141
169
  }
142
170
 
143
171
  // Enrich with skill paths for each detected agent
144
- const allAgents = [...(config.agents.core || []), ...(config.agents.specialists || [])];
172
+ const allAgents = Object.entries(config.agents || {})
173
+ .filter(([id]) => !id.startsWith('_'))
174
+ .map(([id, agent]) => ({ id, ...agent }));
145
175
  result.skillPaths = result.all
146
176
  .map(id => {
147
177
  const agent = allAgents.find(a => a.id === id);
@@ -3,7 +3,7 @@ import ora from 'ora';
3
3
  import chalk from 'chalk';
4
4
  import { logger } from '../../utils/logger.js';
5
5
  import { detectProject, getDetectionSummary } from '../../lib/detectors/index.js';
6
- import { ensureDir, writeFile } from '../../utils/file-copier.js';
6
+ import { ensureDir, writeFile, readJson, writeJson, pathExists } from '../../utils/file-copier.js';
7
7
 
8
8
  export async function detectCommand(options) {
9
9
  const targetPath = options.path || process.cwd();
@@ -84,6 +84,16 @@ export async function detectCommand(options) {
84
84
  const standardsPath = join(standardsDir, 'inferred.md');
85
85
  await writeFile(standardsPath, results.inferred.markdown);
86
86
 
87
+ // Update config.json with detected stack and architecture
88
+ const configPath = join(targetPath, '.morph', 'config', 'config.json');
89
+ if (await pathExists(configPath)) {
90
+ const projectConfig = await readJson(configPath);
91
+ projectConfig.project = projectConfig.project || {};
92
+ projectConfig.project.stack = results.structure.stack;
93
+ projectConfig.project.architecture = results.structure.architecture;
94
+ await writeJson(configPath, projectConfig);
95
+ }
96
+
87
97
  spinner.succeed('Results saved!');
88
98
  logger.dim(` - ${logPath}`);
89
99
  logger.dim(` - ${standardsPath}`);