@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.
- package/CLAUDE.md +108 -946
- package/bin/morph-spec.js +284 -9
- package/bin/task-manager.cjs +102 -14
- package/bin/validate.js +4 -4
- package/docs/{v3.0 → next-generation}/AGENTS.md +1 -1
- package/docs/next-generation/CONTEXT-OPTIMIZATION.md +267 -0
- package/docs/next-generation/EXECUTION-FLOW.md +274 -0
- package/docs/next-generation/META-PROMPTS.md +235 -0
- package/docs/next-generation/MIGRATION-GUIDE.md +253 -0
- package/docs/next-generation/THREAD-MANAGEMENT.md +240 -0
- package/package.json +5 -5
- package/src/commands/agents/agents-fuse.js +97 -0
- package/src/commands/agents/micro-agent.js +112 -0
- package/src/commands/agents/spawn-team.js +69 -4
- package/src/commands/agents/squad-template.js +146 -0
- package/src/commands/analytics/analytics.js +176 -0
- package/src/commands/context/context-prime.js +63 -0
- package/src/commands/context/core-four.js +54 -0
- package/src/commands/mcp/mcp.js +102 -0
- package/src/commands/project/detect-agents.js +32 -2
- package/src/commands/project/detect.js +11 -1
- package/src/commands/project/doctor.js +573 -356
- package/src/commands/project/init.js +9 -2
- package/src/commands/project/update.js +13 -3
- package/src/commands/state/advance-phase.js +448 -416
- package/src/commands/state/state.js +14 -12
- package/src/commands/tasks/task.js +1 -1
- package/src/commands/templates/template-render.js +80 -1
- package/src/commands/threads/thread-template.js +103 -0
- package/src/commands/threads/threads.js +261 -0
- package/src/commands/trust/trust.js +205 -0
- package/src/{orchestrator.js → core/orchestrator.js} +8 -8
- package/src/core/state/state-manager.js +37 -17
- package/src/core/workflows/workflow-detector.js +114 -3
- package/src/lib/agents/micro-agent-factory.js +161 -0
- package/src/lib/analytics/analytics-engine.js +345 -0
- package/src/lib/checkpoints/checkpoint-hooks.js +298 -258
- package/src/lib/context/context-bundler.js +240 -0
- package/src/lib/context/context-optimizer.js +212 -0
- package/src/lib/context/context-tracker.js +273 -0
- package/src/lib/context/core-four-tracker.js +201 -0
- package/src/lib/context/mcp-optimizer.js +200 -0
- package/src/lib/detectors/index.js +1 -1
- package/src/lib/detectors/standards-generator.js +77 -17
- package/src/lib/detectors/structure-detector.js +67 -39
- package/src/lib/execution/fusion-executor.js +304 -0
- package/src/lib/execution/parallel-executor.js +270 -0
- package/src/lib/generators/context-generator.js +3 -3
- package/src/lib/generators/recap-generator.js +32 -12
- package/src/lib/hooks/hook-executor.js +169 -0
- package/src/lib/hooks/stop-hook-executor.js +286 -0
- package/src/lib/hops/hop-composer.js +221 -0
- package/src/lib/threads/thread-coordinator.js +238 -0
- package/src/lib/threads/thread-manager.js +317 -0
- package/src/lib/tracking/artifact-trail.js +202 -0
- package/src/lib/trust/trust-manager.js +269 -0
- package/src/lib/validators/design-system/design-system-validator.js +2 -2
- package/src/lib/validators/validation-runner.js +14 -30
- package/src/utils/hooks-installer.js +69 -0
- package/stacks/blazor-azure/.morph/config/agents.json +72 -3
- package/stacks/nextjs-supabase/.morph/config/agents.json +3 -3
- package/docs/llm-interaction-config.md +0 -735
- package/docs/v3.0/EXECUTION-FLOW.md +0 -1304
- package/src/commands/utils/migrate-state.js +0 -158
- package/src/commands/utils/upgrade.js +0 -346
- package/src/lib/validators/architecture-validator.js +0 -60
- package/src/lib/validators/content-validator.js +0 -164
- package/src/lib/validators/package-validator.js +0 -61
- package/src/lib/validators/ui-contrast-validator.js +0 -44
- package/stacks/blazor-azure/.claude/commands/morph-apply.md +0 -221
- package/stacks/blazor-azure/.claude/commands/morph-archive.md +0 -79
- package/stacks/blazor-azure/.claude/commands/morph-deploy.md +0 -529
- package/stacks/blazor-azure/.claude/commands/morph-infra.md +0 -209
- package/stacks/blazor-azure/.claude/commands/morph-preflight.md +0 -227
- package/stacks/blazor-azure/.claude/commands/morph-proposal.md +0 -122
- package/stacks/blazor-azure/.claude/commands/morph-status.md +0 -86
- package/stacks/blazor-azure/.claude/commands/morph-troubleshoot.md +0 -122
- package/stacks/blazor-azure/.claude/skills/level-0-meta/README.md +0 -7
- package/stacks/blazor-azure/.claude/skills/level-0-meta/code-review.md +0 -226
- package/stacks/blazor-azure/.claude/skills/level-0-meta/morph-checklist.md +0 -117
- package/stacks/blazor-azure/.claude/skills/level-0-meta/simulation-checklist.md +0 -77
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/README.md +0 -7
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/morph-replicate.md +0 -213
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-clarify.md +0 -131
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-design.md +0 -213
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-setup.md +0 -106
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-tasks.md +0 -164
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-uiux.md +0 -169
- package/stacks/blazor-azure/.claude/skills/level-2-domains/README.md +0 -14
- package/stacks/blazor-azure/.claude/skills/level-2-domains/ai-agents/ai-system-architect.md +0 -192
- package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/po-pm-advisor.md +0 -197
- package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/prompt-engineer.md +0 -189
- package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/seo-growth-hacker.md +0 -320
- package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/standards-architect.md +0 -156
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/api-designer.md +0 -59
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/dotnet-senior.md +0 -77
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ef-modeler.md +0 -58
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/hangfire-orchestrator.md +0 -126
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ms-agent-expert.md +0 -45
- package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/blazor-builder.md +0 -210
- package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/nextjs-expert.md +0 -154
- package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/ui-ux-designer.md +0 -191
- package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/azure-architect.md +0 -142
- package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/azure-deploy-specialist.md +0 -699
- package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/bicep-architect.md +0 -126
- package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/container-specialist.md +0 -131
- package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/devops-engineer.md +0 -119
- package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/asaas-financial.md +0 -130
- package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/azure-identity.md +0 -142
- package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/clerk-auth.md +0 -108
- package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/hangfire-orchestrator.md +0 -64
- package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/resend-email.md +0 -119
- package/stacks/blazor-azure/.claude/skills/level-2-domains/quality/code-analyzer.md +0 -235
- package/stacks/blazor-azure/.claude/skills/level-2-domains/quality/testing-specialist.md +0 -126
- package/stacks/blazor-azure/.claude/skills/level-3-technologies/README.md +0 -7
- package/stacks/blazor-azure/.claude/skills/level-4-patterns/README.md +0 -7
- package/stacks/blazor-azure/.morph/archive/.gitkeep +0 -25
- package/stacks/blazor-azure/.morph/features/.gitkeep +0 -25
- package/stacks/blazor-azure/.morph/schemas/agent.schema.json +0 -296
- package/stacks/blazor-azure/.morph/schemas/tasks.schema.json +0 -220
- package/stacks/blazor-azure/.morph/specs/.gitkeep +0 -20
- package/stacks/blazor-azure/.morph/test-infra/example.bicep +0 -59
- package/stacks/nextjs-supabase/.claude/commands/morph-apply.md +0 -221
- package/stacks/nextjs-supabase/.claude/commands/morph-archive.md +0 -79
- package/stacks/nextjs-supabase/.claude/commands/morph-deploy.md +0 -529
- package/stacks/nextjs-supabase/.claude/commands/morph-infra.md +0 -209
- package/stacks/nextjs-supabase/.claude/commands/morph-preflight.md +0 -227
- package/stacks/nextjs-supabase/.claude/commands/morph-proposal.md +0 -122
- package/stacks/nextjs-supabase/.claude/commands/morph-status.md +0 -86
- package/stacks/nextjs-supabase/.claude/commands/morph-troubleshoot.md +0 -122
- package/stacks/nextjs-supabase/.claude/settings.local.json +0 -6
- package/stacks/nextjs-supabase/.claude/skills/level-2-domains/backend/dotnet-supabase.md +0 -244
- package/stacks/nextjs-supabase/.claude/skills/level-2-domains/frontend/nextjs-supabase.md +0 -335
- package/stacks/nextjs-supabase/.claude/skills/level-2-domains/infrastructure/easypanel-deployer.md +0 -189
- package/stacks/nextjs-supabase/.claude/skills/level-2-domains/integrations/supabase-expert.md +0 -50
- /package/docs/{v3.0 → next-generation}/ANALYSIS.md +0 -0
- /package/docs/{v3.0 → next-generation}/ARCHITECTURE.md +0 -0
- /package/docs/{v3.0 → next-generation}/FEATURES.md +0 -0
- /package/docs/{v3.0 → next-generation}/README.md +0 -0
- /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 =
|
|
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}`);
|