@polymorphism-tech/morph-spec 4.10.6 → 4.10.7
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 +3 -0
- package/README.md +2 -2
- package/bin/morph-spec.js +90 -1
- package/bin/task-manager.js +59 -6
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/agents.json +345 -708
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +10 -5
- package/framework/skills/README.md +0 -1
- package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +42 -13
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +14 -10
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
- package/package.json +1 -1
- package/src/commands/agents/agent-metrics.js +174 -0
- package/src/commands/agents/audit-dispatch.js +82 -0
- package/src/commands/agents/detect-agents.js +275 -0
- package/src/commands/agents/dispatch-agents.js +41 -135
- package/src/commands/agents/index.js +9 -0
- package/src/commands/agents/manage-agents.js +352 -0
- package/src/commands/project/doctor.js +59 -0
- package/src/commands/project/update.js +1 -17
- package/src/commands/state/advance-phase.js +11 -3
- package/src/core/state/state-manager.js +81 -0
- package/src/lib/agents/agents-manager.js +349 -0
- package/src/lib/agents/metrics-calculator.js +125 -0
- package/src/lib/agents/output-collector.js +75 -0
- package/src/lib/generators/context-generator.js +8 -7
- package/src/lib/tasks/task-parser.js +94 -3
- package/src/lib/validators/checkpoint-persistence.js +39 -0
- package/src/lib/validators/content/content-validator.js +9 -1
- package/src/lib/validators/spec-validator.js +2 -2
- package/src/lib/validators/validation-runner.js +20 -0
- package/src/scripts/setup-infra.js +1 -17
- package/src/utils/agent-id-mapper.js +11 -0
- package/src/utils/agents-installer.js +40 -8
- package/src/utils/claude-settings-manager.js +1 -1
- package/src/utils/hooks-installer.js +0 -245
- package/src/utils/tier-helpers.js +24 -0
- package/framework/agents/architecture/po-pm-advisor.md +0 -203
- package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +0 -68
|
@@ -69,11 +69,16 @@ async function dispatch(command, payload) {
|
|
|
69
69
|
// Non-blocking
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
// Always inject validator dispatch — deferred if --skip-validation
|
|
73
|
+
const validatorText = await buildValidatorDispatchContext(featureName, payload);
|
|
74
|
+
if (validatorText) {
|
|
75
|
+
if (skipValidation) {
|
|
76
|
+
contextParts.push(
|
|
77
|
+
'⏩ DEFERRED TO CHECKPOINT — validators skipped on this task but MUST run at next checkpoint:\n\n' +
|
|
78
|
+
validatorText
|
|
79
|
+
);
|
|
80
|
+
dispatchResult = 'task_done_deferred_validators';
|
|
81
|
+
} else {
|
|
77
82
|
contextParts.push(validatorText);
|
|
78
83
|
dispatchResult = 'task_done_with_validators';
|
|
79
84
|
}
|
|
@@ -17,7 +17,6 @@ Each skill is a directory: `{name}/SKILL.md` + optional `scripts/`, `references/
|
|
|
17
17
|
| `verification-before-completion` | Gate check before marking any work done |
|
|
18
18
|
| `code-review` | .NET/C# review checklist (naming, arch, async, DI, DTOs) |
|
|
19
19
|
| `morph-replicate` | Convert HTML prototypes to Blazor components |
|
|
20
|
-
| `terminal-title` | Manually override the terminal window title for the current task |
|
|
21
20
|
|
|
22
21
|
### level-0-meta extras
|
|
23
22
|
|
|
@@ -4,7 +4,7 @@ description: MORPH-SPEC Phase 3 (Clarify). Iterative clarification loop driven b
|
|
|
4
4
|
argument-hint: "[feature-name]"
|
|
5
5
|
user-invocable: false
|
|
6
6
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
|
|
7
|
-
cliVersion: "4.10.
|
|
7
|
+
cliVersion: "4.10.7"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MORPH Clarify — Phase 3
|
|
@@ -3,7 +3,7 @@ name: morph-phase-codebase-analysis
|
|
|
3
3
|
description: MORPH-SPEC Design sub-phase that analyzes existing codebase and database schema, producing schema-analysis.md with real column names, types, relationships, and field mismatches. Use at the start of Design phase before generating contracts.cs to prevent incorrect field names or types.
|
|
4
4
|
user-invocable: false
|
|
5
5
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
|
|
6
|
-
cliVersion: "4.10.
|
|
6
|
+
cliVersion: "4.10.7"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# MORPH Codebase Analysis - Design Sub-phase
|
|
@@ -4,7 +4,7 @@ description: MORPH-SPEC Phase 2 (Design). Schema-first interactive design: reads
|
|
|
4
4
|
argument-hint: "[feature-name]"
|
|
5
5
|
user-invocable: false
|
|
6
6
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion, Agent
|
|
7
|
-
cliVersion: "4.10.
|
|
7
|
+
cliVersion: "4.10.7"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MORPH Design — Phase 2
|
|
@@ -5,7 +5,7 @@ argument-hint: "[feature-name]"
|
|
|
5
5
|
context: fork
|
|
6
6
|
agent: general-purpose
|
|
7
7
|
user-invocable: false
|
|
8
|
-
cliVersion: "4.10.
|
|
8
|
+
cliVersion: "4.10.7"
|
|
9
9
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion, Agent
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -112,7 +112,7 @@ Use `AskUserQuestion` with header `"{MCP}"` and options:
|
|
|
112
112
|
| Action | Tool | Alternative |
|
|
113
113
|
|--------|------|-------------|
|
|
114
114
|
| Check execution mode (single/subagents/agent-teams) | **Bash** `npx morph-spec dispatch-agents $ARGUMENTS implement` | Task groups + subagent prompts |
|
|
115
|
-
| **Dispatch implementers by domain** (subagents) | **Agent** `subagent_type=<taskGroups[group].
|
|
115
|
+
| **Dispatch implementers by domain** (subagents) | **Agent** `subagent_type=<taskGroups[group].subagentType>` + `prompt=taskPrompt` from dispatch config | Backend + frontend + infra in parallel |
|
|
116
116
|
| **Create Agent Team** (critical multi-domain features) | Natural language — see Step 0.5b | — |
|
|
117
117
|
| Read task details | **Read** tasks.md, spec.md, contracts.cs/.ts | — |
|
|
118
118
|
| Create new files | **Write** source files | — |
|
|
@@ -216,21 +216,44 @@ The strategy was defined and approved in the Plan phase. Use it directly:
|
|
|
216
216
|
| `"subagents"` | → Dispatch Task subagents in parallel (below) |
|
|
217
217
|
| `"agent-teams"` | → Create Agent Team with plan approval (Step 0.5b) |
|
|
218
218
|
|
|
219
|
-
If `plan.md` doesn't exist or the field is missing,
|
|
219
|
+
If `plan.md` doesn't exist or the field is missing, auto-detect via dispatch-agents:
|
|
220
220
|
|
|
221
221
|
```bash
|
|
222
222
|
npx morph-spec dispatch-agents $ARGUMENTS implement
|
|
223
223
|
```
|
|
224
224
|
|
|
225
|
+
Check the `mode` field in the JSON output to determine the execution strategy.
|
|
226
|
+
|
|
225
227
|
---
|
|
226
228
|
|
|
227
229
|
#### Subagents Mode (if strategy is `"subagents"`):
|
|
228
230
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
+
**Step 0.5a: Get dispatch config:**
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
npx morph-spec dispatch-agents $ARGUMENTS implement
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Parse the JSON output. Extract `taskGroups` and each group's `subagentType`.
|
|
238
|
+
|
|
239
|
+
**Step 0.5b: Verify agent files exist:**
|
|
240
|
+
|
|
241
|
+
For each group, verify `.claude/agents/{subagentType}.md` exists:
|
|
231
242
|
|
|
232
|
-
|
|
233
|
-
|
|
243
|
+
```bash
|
|
244
|
+
ls .claude/agents/{subagentType}.md
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
If missing → **STOP** with: `"Agent file .claude/agents/{subagentType}.md not found. Run: npx morph-spec setup-infra"`. Do NOT fall back to general-purpose agents for missing files.
|
|
248
|
+
|
|
249
|
+
**Step 0.5c: Dispatch subagents:**
|
|
250
|
+
|
|
251
|
+
1. For each group with >= 3 tasks and no cross-group dependencies, use **Agent tool** with `subagent_type` from dispatch config:
|
|
252
|
+
|
|
253
|
+
> **Mapping:** `taskGroups[group].subagentType` = `subagent_type` in Agent tool.
|
|
254
|
+
> Example: `subagentType: "morph-dotnet-senior"` → `Agent(subagent_type="morph-dotnet-senior", prompt=taskPrompt)`.
|
|
255
|
+
>
|
|
256
|
+
> **MANDATORY:** Use the exact `subagentType` value. Do NOT use `general-purpose` agents when a `subagentType` is available.
|
|
234
257
|
|
|
235
258
|
Each subagent receives a prompt with:
|
|
236
259
|
- Full spec.md and contracts.cs/.ts content
|
|
@@ -239,8 +262,10 @@ Each subagent receives a prompt with:
|
|
|
239
262
|
- Domain isolation: modify ONLY files in their domain
|
|
240
263
|
- **MANDATORY verification before every `task done`** (see Step 2, item 6b)
|
|
241
264
|
|
|
242
|
-
|
|
243
|
-
|
|
265
|
+
2. Dispatch subagents for groups in **parallel** (when no cross-group dependencies)
|
|
266
|
+
3. After all complete, validate integration: `npx morph-spec validate-feature $ARGUMENTS --phase implement`
|
|
267
|
+
|
|
268
|
+
**Fallback:** If `dispatch-agents` fails (non-zero exit or invalid JSON), log a warning and fall back to sequential single-agent mode (Step 2).
|
|
244
269
|
|
|
245
270
|
**If strategy is `"single"`** → continue to sequential task loop below.
|
|
246
271
|
|
|
@@ -410,22 +435,26 @@ Implementers are dispatched via `dispatch-agents`:
|
|
|
410
435
|
npx morph-spec dispatch-agents $ARGUMENTS implement
|
|
411
436
|
```
|
|
412
437
|
|
|
413
|
-
Use the `
|
|
438
|
+
Use the `subagentType` from each group in the Agent tool:
|
|
414
439
|
```
|
|
415
|
-
Agent(subagent_type=<
|
|
440
|
+
Agent(subagent_type=<subagentType>, prompt=<filled prompts/implementer-prompt.md>)
|
|
416
441
|
```
|
|
417
442
|
|
|
443
|
+
> The `subagentType` maps agent IDs to `.claude/agents/` filenames:
|
|
444
|
+
> - Tier 1-3: `morph-{id}` (e.g., `morph-dotnet-senior`)
|
|
445
|
+
> - Tier 4: `morph-validator-{id}` (e.g., `morph-validator-architecture-expert`)
|
|
446
|
+
|
|
418
447
|
### Review (Two-Stage)
|
|
419
448
|
|
|
420
449
|
**Stage 1 — Spec Compliance (mandatory):**
|
|
421
450
|
```
|
|
422
|
-
Agent(subagent_type=<
|
|
451
|
+
Agent(subagent_type=<validator subagentType>, prompt=<filled prompts/spec-reviewer-prompt.md>)
|
|
423
452
|
```
|
|
424
453
|
Use validators from `dispatch-agents --mode validate`. Spec compliance MUST pass before proceeding.
|
|
425
454
|
|
|
426
455
|
**Stage 2 — Code Quality (mandatory):**
|
|
427
456
|
```
|
|
428
|
-
Agent(subagent_type=standards-architect, prompt=<filled prompts/code-quality-reviewer-prompt.md>)
|
|
457
|
+
Agent(subagent_type=morph-standards-architect, prompt=<filled prompts/code-quality-reviewer-prompt.md>)
|
|
429
458
|
```
|
|
430
459
|
`critical` issues block. `important` issues must be fixed. `suggestion` is optional.
|
|
431
460
|
|
|
@@ -4,7 +4,7 @@ description: MORPH-SPEC Phase 4 (Plan). Generates a detailed implementation plan
|
|
|
4
4
|
argument-hint: "[feature-name]"
|
|
5
5
|
user-invocable: false
|
|
6
6
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
|
|
7
|
-
cliVersion: "4.10.
|
|
7
|
+
cliVersion: "4.10.7"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MORPH Plan — Phase 4
|
|
@@ -4,7 +4,7 @@ description: MORPH-SPEC Phase 1 (Setup). Reads project context, detects tech sta
|
|
|
4
4
|
argument-hint: "[feature-name]"
|
|
5
5
|
user-invocable: false
|
|
6
6
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
|
|
7
|
-
cliVersion: "4.10.
|
|
7
|
+
cliVersion: "4.10.7"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MORPH Setup — Phase 1
|
|
@@ -27,7 +27,7 @@ Initialize the context and prepare the environment for an approved feature.
|
|
|
27
27
|
| Action | Tool | Alternative |
|
|
28
28
|
|--------|------|-------------|
|
|
29
29
|
| Verify state | **Bash** `npx morph-spec state get` | — |
|
|
30
|
-
| Detect agents + standards | **
|
|
30
|
+
| Detect agents + standards | **Bash** `npx morph-spec detect-agents $ARGUMENTS --json` → parse → **Bash** `npx morph-spec state add-agent` | **Read** agents.json manually |
|
|
31
31
|
| Read project context | **Read** `.morph/context/README.md` | — |
|
|
32
32
|
| Read config | **Read** `.morph/config/config.json` | — |
|
|
33
33
|
| Detect VSA architecture | **Read** `.morph/config/config.json` → check `config.architecture.style` | — |
|
|
@@ -38,8 +38,8 @@ Initialize the context and prepare the environment for an approved feature.
|
|
|
38
38
|
**MCPs for this phase:** GitHub (optional — repo metadata).
|
|
39
39
|
|
|
40
40
|
**Anti-patterns:**
|
|
41
|
-
- Do not
|
|
42
|
-
- Do not use a Task agent for
|
|
41
|
+
- Do not read agents.json manually for keyword matching — use `npx morph-spec detect-agents` CLI
|
|
42
|
+
- Do not use a Task agent for agent detection (CLI is faster and consistent)
|
|
43
43
|
- Do not use WebSearch for local project info (use Read/Glob)
|
|
44
44
|
|
|
45
45
|
---
|
|
@@ -87,18 +87,22 @@ Proceed with normal keyword detection for all other agents. The architect was al
|
|
|
87
87
|
|
|
88
88
|
#### Step 2.1: Keyword Detection
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
Use the `detect-agents` CLI to match agent keywords against the proposal (excludes architects already handled in Step 2.0):
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
```bash
|
|
93
|
+
npx morph-spec detect-agents $ARGUMENTS --exclude domain-architect,vsa-architect --json
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
This reads the proposal from `.morph/features/$ARGUMENTS/0-proposal/proposal.md` and agents.json, returning JSON with `detected[]` and `skipped[]` arrays. Agents with `active_phases: ['*']` are always included.
|
|
97
|
+
|
|
98
|
+
Parse the JSON output and add each detected agent to state:
|
|
97
99
|
|
|
98
100
|
```bash
|
|
99
101
|
npx morph-spec state add-agent $ARGUMENTS {agent-id}
|
|
100
102
|
```
|
|
101
103
|
|
|
104
|
+
Run `add-agent` once per detected agent ID from the `detected[]` array.
|
|
105
|
+
|
|
102
106
|
**To load standards for detected agents:**
|
|
103
107
|
- For each active agent, read the standards referenced by the `standards[]` field in `agents.json`
|
|
104
108
|
- Standard files are located at `.morph/framework/standards/{path}`
|
|
@@ -4,7 +4,7 @@ description: MORPH-SPEC Phase 4 (Tasks). Breaks approved spec into bottom-up ord
|
|
|
4
4
|
argument-hint: "[feature-name]"
|
|
5
5
|
user-invocable: false
|
|
6
6
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
|
|
7
|
-
cliVersion: "4.10.
|
|
7
|
+
cliVersion: "4.10.7"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MORPH Tasks — Phase 4
|
|
@@ -4,7 +4,7 @@ description: MORPH-SPEC Phase 1.5 (UI/UX). Creates design-system.md, mockups.md,
|
|
|
4
4
|
argument-hint: "[feature-name]"
|
|
5
5
|
user-invocable: false
|
|
6
6
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
|
|
7
|
-
cliVersion: "4.10.
|
|
7
|
+
cliVersion: "4.10.7"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MORPH UI/UX Design - Phase 1.5
|
|
@@ -4,7 +4,7 @@ description: Guided workflow for mid-implementation scope escalation — analyze
|
|
|
4
4
|
user-invocable: true
|
|
5
5
|
argument-hint: "[feature-name]"
|
|
6
6
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
|
|
7
|
-
cliVersion: "4.10.
|
|
7
|
+
cliVersion: "4.10.7"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Scope Escalation Workflow
|
package/package.json
CHANGED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent-metrics — Display agent performance metrics for a feature
|
|
3
|
+
*
|
|
4
|
+
* Reads dispatchLog from state.json, computes per-agent metrics
|
|
5
|
+
* (dispatch count, task coverage, completion rate, avg duration),
|
|
6
|
+
* and renders a table or JSON output.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* morph-spec agent-metrics <feature>
|
|
10
|
+
* morph-spec agent-metrics <feature> --json
|
|
11
|
+
* morph-spec agent-metrics --all
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { loadState, stateExists } from '../../core/state/state-manager.js';
|
|
15
|
+
import { calculateMetrics, calculateAggregatedMetrics, formatDuration } from '../../lib/agents/metrics-calculator.js';
|
|
16
|
+
import chalk from 'chalk';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* CLI command: morph-spec agent-metrics [feature]
|
|
20
|
+
*
|
|
21
|
+
* @param {string} [featureName] - Feature name (required unless --all)
|
|
22
|
+
* @param {Object} [options] - CLI options
|
|
23
|
+
* @param {boolean} [options.json] - Output as JSON instead of table
|
|
24
|
+
* @param {boolean} [options.all] - Aggregate metrics across all features
|
|
25
|
+
*/
|
|
26
|
+
export async function agentMetricsCommand(featureName, options = {}) {
|
|
27
|
+
if (!options.all && !featureName) {
|
|
28
|
+
console.error(chalk.red('Usage: morph-spec agent-metrics <feature> [--json] [--all]'));
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!stateExists()) {
|
|
33
|
+
console.error(chalk.red('No state.json found.'));
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const state = loadState();
|
|
38
|
+
|
|
39
|
+
if (options.all) {
|
|
40
|
+
return renderAggregated(state.features || {}, options);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const feature = state.features?.[featureName];
|
|
44
|
+
if (!feature) {
|
|
45
|
+
console.error(chalk.red(`Feature '${featureName}' not found.`));
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const log = feature.dispatchLog || [];
|
|
50
|
+
if (log.length === 0) {
|
|
51
|
+
console.log(chalk.gray('\n No dispatch events recorded.\n'));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const result = calculateMetrics(log);
|
|
56
|
+
result.feature = featureName;
|
|
57
|
+
|
|
58
|
+
if (options.json) {
|
|
59
|
+
console.log(JSON.stringify(result, null, 2));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
renderTable(featureName, result);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Render per-feature metrics table.
|
|
68
|
+
*/
|
|
69
|
+
function renderTable(featureName, result) {
|
|
70
|
+
const { agents, summary } = result;
|
|
71
|
+
|
|
72
|
+
console.log(chalk.bold(`\n Agent Performance \u2014 ${featureName}`));
|
|
73
|
+
console.log(' ' + '\u2500'.repeat(70));
|
|
74
|
+
|
|
75
|
+
// Header
|
|
76
|
+
console.log(
|
|
77
|
+
' ' +
|
|
78
|
+
pad('Agent', 28) +
|
|
79
|
+
pad('Dispatches', 12) +
|
|
80
|
+
pad('Tasks', 8) +
|
|
81
|
+
pad('Completed', 12) +
|
|
82
|
+
pad('Avg Duration', 14)
|
|
83
|
+
);
|
|
84
|
+
console.log(' ' + '\u2500'.repeat(70));
|
|
85
|
+
|
|
86
|
+
for (const agent of agents) {
|
|
87
|
+
const tasks = agent.tasksAssigned > 0 ? String(agent.tasksAssigned) : '\u2014';
|
|
88
|
+
const completed = agent.tasksAssigned > 0
|
|
89
|
+
? `${agent.tasksCompleted}/${agent.tasksAssigned}`
|
|
90
|
+
: '\u2014';
|
|
91
|
+
const duration = formatDuration(agent.avgDurationMs);
|
|
92
|
+
|
|
93
|
+
console.log(
|
|
94
|
+
' ' +
|
|
95
|
+
pad(agent.id, 28) +
|
|
96
|
+
pad(String(agent.dispatches), 12) +
|
|
97
|
+
pad(tasks, 8) +
|
|
98
|
+
pad(completed, 12) +
|
|
99
|
+
pad(duration, 14)
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
console.log();
|
|
104
|
+
const rate = summary.totalTasks > 0
|
|
105
|
+
? `${Math.round(summary.completionRate * 100)}%`
|
|
106
|
+
: '0%';
|
|
107
|
+
console.log(` Summary: ${summary.completedTasks} tasks completed of ${summary.totalTasks} (${rate}), avg ${formatDuration(summary.avgTaskDurationMs)}/task`);
|
|
108
|
+
console.log(` Dispatches: ${summary.totalDispatches} total`);
|
|
109
|
+
console.log();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Render aggregated metrics across all features.
|
|
114
|
+
*/
|
|
115
|
+
function renderAggregated(features, options) {
|
|
116
|
+
const featureNames = Object.keys(features);
|
|
117
|
+
if (featureNames.length === 0) {
|
|
118
|
+
console.log(chalk.gray('\n No features found.\n'));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const result = calculateAggregatedMetrics(features);
|
|
123
|
+
|
|
124
|
+
if (options.json) {
|
|
125
|
+
console.log(JSON.stringify(result, null, 2));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log(chalk.bold(`\n Agent Performance \u2014 All Features (${result.featureCount})`));
|
|
130
|
+
console.log(' ' + '\u2500'.repeat(70));
|
|
131
|
+
|
|
132
|
+
console.log(
|
|
133
|
+
' ' +
|
|
134
|
+
pad('Agent', 28) +
|
|
135
|
+
pad('Dispatches', 12) +
|
|
136
|
+
pad('Tasks', 8) +
|
|
137
|
+
pad('Completed', 12) +
|
|
138
|
+
pad('Avg Duration', 14)
|
|
139
|
+
);
|
|
140
|
+
console.log(' ' + '\u2500'.repeat(70));
|
|
141
|
+
|
|
142
|
+
for (const agent of result.agents) {
|
|
143
|
+
const tasks = agent.tasksAssigned > 0 ? String(agent.tasksAssigned) : '\u2014';
|
|
144
|
+
const completed = agent.tasksAssigned > 0
|
|
145
|
+
? `${agent.tasksCompleted}/${agent.tasksAssigned}`
|
|
146
|
+
: '\u2014';
|
|
147
|
+
const duration = formatDuration(agent.avgDurationMs);
|
|
148
|
+
|
|
149
|
+
console.log(
|
|
150
|
+
' ' +
|
|
151
|
+
pad(agent.id, 28) +
|
|
152
|
+
pad(String(agent.dispatches), 12) +
|
|
153
|
+
pad(tasks, 8) +
|
|
154
|
+
pad(completed, 12) +
|
|
155
|
+
pad(duration, 14)
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
console.log();
|
|
160
|
+
const { summary } = result;
|
|
161
|
+
const rate = summary.totalTasks > 0
|
|
162
|
+
? `${Math.round(summary.completionRate * 100)}%`
|
|
163
|
+
: '0%';
|
|
164
|
+
console.log(` Summary: ${summary.completedTasks} tasks completed of ${summary.totalTasks} (${rate}), avg ${formatDuration(summary.avgTaskDurationMs)}/task`);
|
|
165
|
+
console.log(` Dispatches: ${summary.totalDispatches} total across ${result.featureCount} features`);
|
|
166
|
+
console.log();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Right-pad a string to a given width.
|
|
171
|
+
*/
|
|
172
|
+
function pad(str, width) {
|
|
173
|
+
return str.length >= width ? str : str + ' '.repeat(width - str.length);
|
|
174
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* audit-dispatch — Display dispatch audit log for a feature
|
|
3
|
+
*
|
|
4
|
+
* Reads dispatchLog from state.json and presents a summary:
|
|
5
|
+
* - Total dispatches
|
|
6
|
+
* - Per-dispatch: timestamp, phase, mode, agents with subagentTypes
|
|
7
|
+
* - Agent coverage: which agents handled which tasks
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* morph-spec audit-dispatch <feature>
|
|
11
|
+
* morph-spec audit-dispatch <feature> --json
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { loadState, stateExists } from '../../core/state/state-manager.js';
|
|
15
|
+
import chalk from 'chalk';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* CLI command: morph-spec audit-dispatch <feature>
|
|
19
|
+
*
|
|
20
|
+
* @param {string} featureName - Feature name
|
|
21
|
+
* @param {Object} [options] - CLI options
|
|
22
|
+
* @param {boolean} [options.json] - Output as JSON instead of table
|
|
23
|
+
*/
|
|
24
|
+
export async function auditDispatchCommand(featureName, options = {}) {
|
|
25
|
+
if (!featureName) {
|
|
26
|
+
console.error(chalk.red('Usage: morph-spec audit-dispatch <feature>'));
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!stateExists()) {
|
|
31
|
+
console.error(chalk.red('No state.json found.'));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const state = loadState();
|
|
36
|
+
const feature = state.features?.[featureName];
|
|
37
|
+
if (!feature) {
|
|
38
|
+
console.error(chalk.red(`Feature '${featureName}' not found.`));
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const log = feature.dispatchLog || [];
|
|
43
|
+
|
|
44
|
+
if (options.json) {
|
|
45
|
+
console.log(JSON.stringify(log, null, 2));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log(chalk.bold(`\n Dispatch Audit — ${featureName}`));
|
|
50
|
+
console.log(' ' + '─'.repeat(50));
|
|
51
|
+
console.log(` Total dispatches: ${log.length}`);
|
|
52
|
+
|
|
53
|
+
if (log.length === 0) {
|
|
54
|
+
console.log(chalk.gray('\n No dispatch events recorded.\n'));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for (let i = 0; i < log.length; i++) {
|
|
59
|
+
const entry = log[i];
|
|
60
|
+
console.log(`\n #${i + 1}: ${entry.timestamp} — ${entry.phase} (${entry.mode})`);
|
|
61
|
+
for (const agent of entry.agents || []) {
|
|
62
|
+
const taskCount = agent.tasks?.length || 0;
|
|
63
|
+
console.log(` ${agent.id} → ${agent.subagentType} (${agent.group}) — ${taskCount} tasks`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Agent coverage summary
|
|
68
|
+
const coverage = {};
|
|
69
|
+
for (const entry of log) {
|
|
70
|
+
for (const agent of entry.agents || []) {
|
|
71
|
+
if (!coverage[agent.id]) coverage[agent.id] = { tasks: 0, dispatches: 0 };
|
|
72
|
+
coverage[agent.id].tasks += agent.tasks?.length || 0;
|
|
73
|
+
coverage[agent.id].dispatches += 1;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log(chalk.bold('\n Agent Coverage:'));
|
|
78
|
+
for (const [id, data] of Object.entries(coverage)) {
|
|
79
|
+
console.log(` ${id}: ${data.tasks} tasks across ${data.dispatches} dispatches`);
|
|
80
|
+
}
|
|
81
|
+
console.log();
|
|
82
|
+
}
|