@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
|
@@ -181,13 +181,13 @@ async function checkpointCommand(featureName, note, options) {
|
|
|
181
181
|
try {
|
|
182
182
|
const spinner = ora('Creating checkpoint...').start();
|
|
183
183
|
|
|
184
|
-
const checkpoint = StateManager.addCheckpoint(featureName, note);
|
|
184
|
+
const checkpoint = await StateManager.addCheckpoint(featureName, note);
|
|
185
185
|
|
|
186
186
|
spinner.succeed(`Checkpoint registered for ${chalk.cyan(featureName)}`);
|
|
187
187
|
logger.blank();
|
|
188
|
-
logger.dim(` ${checkpoint.note}`);
|
|
188
|
+
logger.dim(` ${checkpoint.summary?.note || note}`);
|
|
189
189
|
logger.dim(` Phase: ${checkpoint.phase}`);
|
|
190
|
-
logger.dim(` Tasks completed: ${checkpoint.completedTasks}`);
|
|
190
|
+
logger.dim(` Tasks completed: ${checkpoint.summary?.completedTasks ?? 0}`);
|
|
191
191
|
logger.blank();
|
|
192
192
|
|
|
193
193
|
} catch (error) {
|
|
@@ -208,8 +208,10 @@ async function listCommand(options) {
|
|
|
208
208
|
const features = StateManager.listFeatures();
|
|
209
209
|
|
|
210
210
|
// Project info
|
|
211
|
-
|
|
212
|
-
|
|
211
|
+
const projectName = summary.project?.name || '(unnamed)';
|
|
212
|
+
const projectType = summary.project?.type || '(unknown)';
|
|
213
|
+
logger.info(`Project: ${chalk.cyan(projectName)}`);
|
|
214
|
+
logger.info(`Type: ${chalk.cyan(projectType)}`);
|
|
213
215
|
logger.blank();
|
|
214
216
|
|
|
215
217
|
if (features.length === 0) {
|
|
@@ -232,21 +234,21 @@ async function listCommand(options) {
|
|
|
232
234
|
archived: '📦'
|
|
233
235
|
}[feature.status] || '❓';
|
|
234
236
|
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
+
const tasks = feature.tasks || {};
|
|
238
|
+
const progress = (tasks.total || 0) > 0
|
|
239
|
+
? `${tasks.completed || 0}/${tasks.total}`
|
|
237
240
|
: '0/0';
|
|
238
241
|
|
|
239
242
|
logger.info(`${statusEmoji} ${chalk.bold(name)}`);
|
|
240
|
-
logger.dim(` Phase: ${feature.phase.padEnd(16)} │ Tasks: ${progress}`);
|
|
241
|
-
logger.dim(` Agents: ${feature.activeAgents.slice(0, 5).join(', ') || 'None'}`);
|
|
243
|
+
logger.dim(` Phase: ${(feature.phase || 'unknown').padEnd(16)} │ Tasks: ${progress}`);
|
|
244
|
+
logger.dim(` Agents: ${(feature.activeAgents || []).slice(0, 5).join(', ') || 'None'}`);
|
|
242
245
|
logger.blank();
|
|
243
246
|
});
|
|
244
247
|
|
|
245
248
|
// Summary
|
|
246
249
|
logger.header('Summary:');
|
|
247
|
-
logger.info(`Total Features: ${chalk.cyan(summary.metadata.
|
|
248
|
-
logger.info(`Completed: ${chalk.cyan(summary.metadata
|
|
249
|
-
logger.info(`Estimated Cost: ${chalk.cyan(`$${summary.metadata.totalCostEstimated.toFixed(2)}/month`)}`);
|
|
250
|
+
logger.info(`Total Features: ${chalk.cyan(summary.metadata?.totalFeatures ?? features.length)}`);
|
|
251
|
+
logger.info(`Completed: ${chalk.cyan(summary.metadata?.completedFeatures ?? 0)}`);
|
|
250
252
|
logger.blank();
|
|
251
253
|
|
|
252
254
|
} catch (error) {
|
|
@@ -9,7 +9,7 @@ import { dirname, join } from 'path';
|
|
|
9
9
|
import chalk from 'chalk';
|
|
10
10
|
|
|
11
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
-
const taskManagerPath = join(__dirname, '..', '..', 'bin', 'task-manager.cjs');
|
|
12
|
+
const taskManagerPath = join(__dirname, '..', '..', '..', 'bin', 'task-manager.cjs');
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Execute task-manager.js with given arguments
|
|
@@ -3,11 +3,90 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { writeFileSync, mkdirSync } from 'fs';
|
|
6
|
-
import { dirname } from 'path';
|
|
6
|
+
import { dirname, join } from 'path';
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
import { logger } from '../../utils/logger.js';
|
|
9
9
|
import { getTemplateById, resolveTemplatePathById } from '../../core/templates/template-registry.js';
|
|
10
10
|
import { renderTemplate } from '../../core/templates/template-renderer.js';
|
|
11
|
+
import { renderHOP, listAvailableHOPs } from '../../lib/hops/hop-composer.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Render HOP (Higher-Order Prompt) template command
|
|
15
|
+
*/
|
|
16
|
+
export async function hopRenderCommand(hopId, outputPath, variablesJson, options) {
|
|
17
|
+
logger.header(`Rendering HOP: ${hopId}`);
|
|
18
|
+
logger.blank();
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
// Parse variables
|
|
22
|
+
let variables = {};
|
|
23
|
+
try {
|
|
24
|
+
variables = JSON.parse(variablesJson || '{}');
|
|
25
|
+
} catch (error) {
|
|
26
|
+
logger.error('Invalid JSON in variables argument');
|
|
27
|
+
logger.dim(` ${error.message}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Add defaults
|
|
32
|
+
variables.DATE = variables.DATE || new Date().toISOString().split('T')[0];
|
|
33
|
+
variables.YEAR = variables.YEAR || new Date().getFullYear().toString();
|
|
34
|
+
variables.TIMESTAMP = variables.TIMESTAMP || new Date().toISOString();
|
|
35
|
+
|
|
36
|
+
// Render via hop-composer
|
|
37
|
+
const frameworkRoot = join(process.cwd(), 'framework');
|
|
38
|
+
const rendered = renderHOP(hopId, variables, { frameworkRoot });
|
|
39
|
+
|
|
40
|
+
if (options?.dryRun || !outputPath) {
|
|
41
|
+
logger.info(chalk.cyan('📋 HOP Preview:'));
|
|
42
|
+
logger.blank();
|
|
43
|
+
console.log(rendered);
|
|
44
|
+
logger.blank();
|
|
45
|
+
if (outputPath) logger.dim(`Would be written to: ${outputPath}`);
|
|
46
|
+
} else {
|
|
47
|
+
mkdirSync(dirname(outputPath), { recursive: true });
|
|
48
|
+
writeFileSync(outputPath, rendered, 'utf-8');
|
|
49
|
+
logger.success(`✅ HOP rendered: ${chalk.cyan(outputPath)}`);
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
logger.error(`Failed to render HOP: ${error.message}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* List available HOPs
|
|
59
|
+
*/
|
|
60
|
+
export async function hopListCommand(options) {
|
|
61
|
+
logger.header('Available HOPs (Higher-Order Prompts)');
|
|
62
|
+
logger.blank();
|
|
63
|
+
|
|
64
|
+
const frameworkRoot = join(process.cwd(), 'framework');
|
|
65
|
+
const hops = listAvailableHOPs(frameworkRoot);
|
|
66
|
+
|
|
67
|
+
if (hops.length === 0) {
|
|
68
|
+
logger.warn('No HOPs found in framework/templates/meta-prompts/');
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const byType = {};
|
|
73
|
+
for (const hop of hops) {
|
|
74
|
+
const type = hop.type || 'other';
|
|
75
|
+
if (!byType[type]) byType[type] = [];
|
|
76
|
+
byType[type].push(hop);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
for (const [type, list] of Object.entries(byType)) {
|
|
80
|
+
console.log(chalk.cyan(`\n ${type.toUpperCase()}`));
|
|
81
|
+
for (const hop of list) {
|
|
82
|
+
console.log(` ${chalk.white(hop.id.padEnd(30))} ${chalk.gray(hop.description || hop.path)}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
logger.blank();
|
|
87
|
+
logger.dim(`Total: ${hops.length} HOPs`);
|
|
88
|
+
logger.dim(`Render: morph-spec hop render <hop-id> <output-file> '<variables-json>'`);
|
|
89
|
+
}
|
|
11
90
|
|
|
12
91
|
/**
|
|
13
92
|
* Render template command
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thread Template CLI command — Thread type management
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* morph-spec thread-template list
|
|
6
|
+
* morph-spec thread-template use <type> --feature=<feature>
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import { THREAD_TYPES } from '../../lib/threads/thread-manager.js';
|
|
11
|
+
|
|
12
|
+
const THREAD_TYPE_INFO = {
|
|
13
|
+
[THREAD_TYPES.BASE]: {
|
|
14
|
+
name: 'B-Thread (Base)',
|
|
15
|
+
description: 'Standard single-agent thread. Sequential execution, all approval gates required.',
|
|
16
|
+
useCase: 'Default for simple and medium features',
|
|
17
|
+
phases: 'All standard phases',
|
|
18
|
+
approvalGates: 'Manual (all)',
|
|
19
|
+
maxConcurrent: 1
|
|
20
|
+
},
|
|
21
|
+
[THREAD_TYPES.PARALLEL]: {
|
|
22
|
+
name: 'P-Thread (Parallel)',
|
|
23
|
+
description: 'Multiple agents run simultaneously on different feature domains.',
|
|
24
|
+
useCase: 'Full-stack features with independent backend/frontend/infra work',
|
|
25
|
+
phases: 'All phases (parallel IMPLEMENT)',
|
|
26
|
+
approvalGates: 'Manual (all)',
|
|
27
|
+
maxConcurrent: 5
|
|
28
|
+
},
|
|
29
|
+
[THREAD_TYPES.FUSION]: {
|
|
30
|
+
name: 'F-Thread (Fusion)',
|
|
31
|
+
description: 'N agents independently generate solutions; best-of-N result is selected.',
|
|
32
|
+
useCase: 'High-uncertainty decisions, critical architecture choices, prototype evaluation',
|
|
33
|
+
phases: 'DESIGN → parallel IMPLEMENT → AGGREGATE → SYNC',
|
|
34
|
+
approvalGates: 'Manual select (or auto best-of-n)',
|
|
35
|
+
maxConcurrent: 3
|
|
36
|
+
},
|
|
37
|
+
[THREAD_TYPES.LONG_RUNNING]: {
|
|
38
|
+
name: 'L-Thread (Long-Running)',
|
|
39
|
+
description: 'Autonomous agent with stop hooks every 30 minutes for validation.',
|
|
40
|
+
useCase: 'Large scope features (>10 files), autonomous implementation without interruption',
|
|
41
|
+
phases: 'IMPLEMENT (continuous) with periodic validation',
|
|
42
|
+
approvalGates: 'Auto (stop hooks validate)',
|
|
43
|
+
maxConcurrent: 1
|
|
44
|
+
},
|
|
45
|
+
[THREAD_TYPES.ZERO_TOUCH]: {
|
|
46
|
+
name: 'Z-Thread (Zero-Touch)',
|
|
47
|
+
description: 'Fully autonomous execution. Auto-approves all gates, auto-commits on success.',
|
|
48
|
+
useCase: 'Maximum-trust features with >95% checkpoint pass rate',
|
|
49
|
+
phases: 'All phases (auto-approved)',
|
|
50
|
+
approvalGates: 'Auto (trust-based)',
|
|
51
|
+
maxConcurrent: 1
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export async function threadTemplateListCommand(options) {
|
|
56
|
+
console.log(chalk.cyan('\n Thread Types\n'));
|
|
57
|
+
console.log(' ' + '─'.repeat(70));
|
|
58
|
+
|
|
59
|
+
for (const [type, info] of Object.entries(THREAD_TYPE_INFO)) {
|
|
60
|
+
console.log(`\n ${chalk.bold(info.name)}`);
|
|
61
|
+
console.log(` ${info.description}`);
|
|
62
|
+
console.log(` ${chalk.gray('Use case:')} ${info.useCase}`);
|
|
63
|
+
console.log(` ${chalk.gray('Phases:')} ${info.phases}`);
|
|
64
|
+
console.log(` ${chalk.gray('Gates:')} ${info.approvalGates}`);
|
|
65
|
+
console.log(` ${chalk.gray('Max concurrent:')} ${info.maxConcurrent}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
console.log('\n ' + '─'.repeat(70));
|
|
69
|
+
console.log(chalk.gray('\n Set thread type: morph-spec thread-template use <type> --feature=<feature>\n'));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function threadTemplateUseCommand(type, options) {
|
|
73
|
+
if (!options.feature) {
|
|
74
|
+
console.error(chalk.red('--feature is required'));
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!THREAD_TYPE_INFO[type]) {
|
|
79
|
+
console.error(chalk.red(`Unknown thread type: ${type}`));
|
|
80
|
+
console.error(chalk.gray(`Valid types: ${Object.keys(THREAD_TYPE_INFO).join(', ')}`));
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const info = THREAD_TYPE_INFO[type];
|
|
85
|
+
|
|
86
|
+
// Special checks
|
|
87
|
+
if (type === THREAD_TYPES.ZERO_TOUCH) {
|
|
88
|
+
console.log(chalk.yellow('\n ⚠ Z-Thread requires maximum trust level (>95% checkpoint pass rate)'));
|
|
89
|
+
console.log(chalk.gray(' Check trust: morph-spec trust status\n'));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Update feature state with thread type
|
|
93
|
+
try {
|
|
94
|
+
const { updateFeature } = await import('../../core/state/state-manager.js');
|
|
95
|
+
await updateFeature(options.feature, 'threadType', type);
|
|
96
|
+
console.log(chalk.green(`\n ✓ Thread type set: ${type} for feature: ${options.feature}`));
|
|
97
|
+
console.log(` ${info.name}: ${info.description}`);
|
|
98
|
+
console.log('');
|
|
99
|
+
} catch (err) {
|
|
100
|
+
console.error(chalk.red(` Error updating state: ${err.message}`));
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threads CLI command — Thread lifecycle management
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* morph-spec threads list [--feature=<f>] [--status=<s>]
|
|
6
|
+
* morph-spec threads status <id>
|
|
7
|
+
* morph-spec threads kill <id>
|
|
8
|
+
* morph-spec threads analytics [--feature=<f>]
|
|
9
|
+
* morph-spec threads wait-all <feature>
|
|
10
|
+
* morph-spec threads wait-any <feature>
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import chalk from 'chalk';
|
|
14
|
+
import {
|
|
15
|
+
listThreads, getThread, killThread, getThreadAnalytics,
|
|
16
|
+
THREAD_STATUS, THREAD_TYPES
|
|
17
|
+
} from '../../lib/threads/thread-manager.js';
|
|
18
|
+
import { generateAsciiChart, generateTimelineChart } from '../../lib/analytics/analytics-engine.js';
|
|
19
|
+
|
|
20
|
+
const STATUS_COLORS = {
|
|
21
|
+
[THREAD_STATUS.PENDING]: chalk.yellow,
|
|
22
|
+
[THREAD_STATUS.RUNNING]: chalk.cyan,
|
|
23
|
+
[THREAD_STATUS.COMPLETED]: chalk.green,
|
|
24
|
+
[THREAD_STATUS.FAILED]: chalk.red,
|
|
25
|
+
[THREAD_STATUS.KILLED]: chalk.gray
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const TYPE_ICONS = {
|
|
29
|
+
[THREAD_TYPES.BASE]: '◆',
|
|
30
|
+
[THREAD_TYPES.PARALLEL]: '⫸',
|
|
31
|
+
[THREAD_TYPES.FUSION]: '⊕',
|
|
32
|
+
[THREAD_TYPES.LONG_RUNNING]: '∞',
|
|
33
|
+
[THREAD_TYPES.ZERO_TOUCH]: '⚡'
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function formatDuration(seconds) {
|
|
37
|
+
if (!seconds) return '—';
|
|
38
|
+
if (seconds < 60) return `${seconds}s`;
|
|
39
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
|
|
40
|
+
return `${Math.floor(seconds / 3600)}h ${Math.floor((seconds % 3600) / 60)}m`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* List threads
|
|
45
|
+
*/
|
|
46
|
+
export async function threadsListCommand(options) {
|
|
47
|
+
try {
|
|
48
|
+
const threads = listThreads({
|
|
49
|
+
feature: options.feature,
|
|
50
|
+
status: options.status,
|
|
51
|
+
type: options.type
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (threads.length === 0) {
|
|
55
|
+
console.log(chalk.gray('No threads found.'));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
console.log(chalk.cyan(`\n Threads (${threads.length})\n`));
|
|
60
|
+
console.log(
|
|
61
|
+
' ' + 'ID'.padEnd(10) + ' ' +
|
|
62
|
+
'Type'.padEnd(14) + ' ' +
|
|
63
|
+
'Status'.padEnd(12) + ' ' +
|
|
64
|
+
'Feature'.padEnd(20) + ' ' +
|
|
65
|
+
'Agent'.padEnd(20) + ' ' +
|
|
66
|
+
'Duration'
|
|
67
|
+
);
|
|
68
|
+
console.log(' ' + '─'.repeat(90));
|
|
69
|
+
|
|
70
|
+
for (const t of threads) {
|
|
71
|
+
const icon = TYPE_ICONS[t.type] || '◆';
|
|
72
|
+
const colorFn = STATUS_COLORS[t.status] || chalk.white;
|
|
73
|
+
const shortId = t.id.substring(0, 8);
|
|
74
|
+
|
|
75
|
+
console.log(
|
|
76
|
+
' ' +
|
|
77
|
+
chalk.gray(shortId) + ' ' +
|
|
78
|
+
`${icon} ${t.type}`.padEnd(14) + ' ' +
|
|
79
|
+
colorFn(t.status.padEnd(12)) + ' ' +
|
|
80
|
+
(t.feature || '—').padEnd(20) + ' ' +
|
|
81
|
+
(t.agent || '—').padEnd(20) + ' ' +
|
|
82
|
+
formatDuration(t.duration)
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
console.log('');
|
|
86
|
+
} catch (err) {
|
|
87
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Show thread status detail
|
|
94
|
+
*/
|
|
95
|
+
export async function threadsStatusCommand(id) {
|
|
96
|
+
try {
|
|
97
|
+
const thread = getThread(id);
|
|
98
|
+
if (!thread) {
|
|
99
|
+
console.error(chalk.red(`Thread not found: ${id}`));
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const colorFn = STATUS_COLORS[thread.status] || chalk.white;
|
|
104
|
+
const icon = TYPE_ICONS[thread.type] || '◆';
|
|
105
|
+
|
|
106
|
+
console.log(chalk.cyan('\n Thread Detail\n'));
|
|
107
|
+
console.log(` ID: ${chalk.gray(thread.id)}`);
|
|
108
|
+
console.log(` Type: ${icon} ${thread.type}`);
|
|
109
|
+
console.log(` Status: ${colorFn(thread.status)}`);
|
|
110
|
+
console.log(` Feature: ${thread.feature || '—'}`);
|
|
111
|
+
console.log(` Agent: ${thread.agent || '—'}`);
|
|
112
|
+
console.log(` Mission: ${thread.mission || '—'}`);
|
|
113
|
+
console.log(` Created: ${thread.createdAt}`);
|
|
114
|
+
console.log(` Started: ${thread.startedAt || '—'}`);
|
|
115
|
+
console.log(` Ended: ${thread.completedAt || '—'}`);
|
|
116
|
+
console.log(` Duration: ${formatDuration(thread.duration)}`);
|
|
117
|
+
|
|
118
|
+
if (thread.metrics) {
|
|
119
|
+
console.log('\n Metrics:');
|
|
120
|
+
console.log(` Tool calls: ${thread.metrics.toolCalls || 0}`);
|
|
121
|
+
console.log(` Tokens used: ${(thread.metrics.tokensUsed || 0).toLocaleString()}`);
|
|
122
|
+
console.log(` Checkpoints OK: ${thread.metrics.checkpointsPassed || 0}`);
|
|
123
|
+
console.log(` Checkpoints KO: ${thread.metrics.checkpointsFailed || 0}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (thread.events?.length > 0) {
|
|
127
|
+
console.log(`\n Recent events (${thread.events.length}):`);
|
|
128
|
+
thread.events.slice(-5).forEach(e => {
|
|
129
|
+
console.log(` ${chalk.gray(e.timestamp)} ${e.type}`);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (thread.failureReason) {
|
|
134
|
+
console.log(`\n ${chalk.red('Failure:')} ${thread.failureReason}`);
|
|
135
|
+
}
|
|
136
|
+
console.log('');
|
|
137
|
+
} catch (err) {
|
|
138
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Kill a thread
|
|
145
|
+
*/
|
|
146
|
+
export async function threadsKillCommand(id) {
|
|
147
|
+
try {
|
|
148
|
+
const thread = killThread(id);
|
|
149
|
+
console.log(chalk.yellow(`✓ Thread ${id.substring(0, 8)} killed.`));
|
|
150
|
+
console.log(` Status: ${thread.status} | Duration: ${formatDuration(thread.duration)}`);
|
|
151
|
+
} catch (err) {
|
|
152
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Show threads analytics dashboard
|
|
159
|
+
*/
|
|
160
|
+
export async function threadsAnalyticsCommand(options) {
|
|
161
|
+
try {
|
|
162
|
+
const feature = options.feature;
|
|
163
|
+
if (!feature) {
|
|
164
|
+
console.error(chalk.red('--feature is required for analytics'));
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const analytics = getThreadAnalytics(feature);
|
|
169
|
+
|
|
170
|
+
console.log(chalk.cyan(`\n Thread Analytics — ${feature}\n`));
|
|
171
|
+
console.log(` Total threads: ${analytics.total}`);
|
|
172
|
+
console.log(` By status:`);
|
|
173
|
+
for (const [status, count] of Object.entries(analytics.byStatus)) {
|
|
174
|
+
const colorFn = STATUS_COLORS[status] || chalk.white;
|
|
175
|
+
console.log(` ${colorFn(status.padEnd(12))} ${count}`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
console.log('\n By type:');
|
|
179
|
+
for (const [type, count] of Object.entries(analytics.byType)) {
|
|
180
|
+
const icon = TYPE_ICONS[type] || '◆';
|
|
181
|
+
console.log(` ${icon} ${type.padEnd(14)} ${count}`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
console.log(`\n Avg duration: ${formatDuration(analytics.avgDuration)}`);
|
|
185
|
+
console.log(` Total tool calls: ${analytics.totalToolCalls}`);
|
|
186
|
+
console.log(` Checkpoint pass rate: ${Math.round(analytics.checkpointPassRate * 100)}%`);
|
|
187
|
+
|
|
188
|
+
if (analytics.total > 0) {
|
|
189
|
+
const statusChart = generateAsciiChart(analytics.byStatus, { title: 'Threads by Status' });
|
|
190
|
+
console.log('\n' + statusChart);
|
|
191
|
+
}
|
|
192
|
+
console.log('');
|
|
193
|
+
} catch (err) {
|
|
194
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Wait for all threads in a feature to complete
|
|
201
|
+
*/
|
|
202
|
+
export async function threadsWaitAllCommand(feature, options) {
|
|
203
|
+
const timeoutMs = (options.timeout || 60) * 60 * 1000;
|
|
204
|
+
const pollInterval = 5000;
|
|
205
|
+
const start = Date.now();
|
|
206
|
+
|
|
207
|
+
console.log(chalk.cyan(`\n Waiting for all threads in: ${feature}\n`));
|
|
208
|
+
|
|
209
|
+
while (Date.now() - start < timeoutMs) {
|
|
210
|
+
const threads = listThreads({ feature });
|
|
211
|
+
const active = threads.filter(t =>
|
|
212
|
+
t.status === THREAD_STATUS.PENDING || t.status === THREAD_STATUS.RUNNING
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
if (active.length === 0) {
|
|
216
|
+
const failed = threads.filter(t => t.status === THREAD_STATUS.FAILED);
|
|
217
|
+
if (failed.length > 0) {
|
|
218
|
+
console.log(chalk.yellow(` ⚠ ${failed.length} thread(s) failed`));
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
console.log(chalk.green(` ✓ All ${threads.length} threads completed`));
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
process.stdout.write(`\r Still running: ${active.length} thread(s)...`);
|
|
226
|
+
await new Promise(r => setTimeout(r, pollInterval));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
console.error(chalk.red('\n Timeout waiting for threads'));
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Wait for any thread to complete
|
|
235
|
+
*/
|
|
236
|
+
export async function threadsWaitAnyCommand(feature, options) {
|
|
237
|
+
const timeoutMs = (options.timeout || 60) * 60 * 1000;
|
|
238
|
+
const pollInterval = 3000;
|
|
239
|
+
const start = Date.now();
|
|
240
|
+
|
|
241
|
+
console.log(chalk.cyan(`\n Waiting for first thread to complete in: ${feature}\n`));
|
|
242
|
+
|
|
243
|
+
while (Date.now() - start < timeoutMs) {
|
|
244
|
+
const threads = listThreads({ feature });
|
|
245
|
+
const done = threads.find(t =>
|
|
246
|
+
t.status === THREAD_STATUS.COMPLETED || t.status === THREAD_STATUS.FAILED
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
if (done) {
|
|
250
|
+
const colorFn = STATUS_COLORS[done.status] || chalk.white;
|
|
251
|
+
console.log(` ${colorFn('✓')} Thread ${done.id.substring(0, 8)} ${done.status}`);
|
|
252
|
+
if (done.agent) console.log(` Agent: ${done.agent}`);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
await new Promise(r => setTimeout(r, pollInterval));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
console.error(chalk.red('\n Timeout waiting for thread'));
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|