@polymorphism-tech/morph-spec 4.22.0 → 6.0.0
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 +57 -71
- package/README.md +2 -2
- package/bin/morph-spec.js +224 -491
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +55 -38
- package/framework/MORPH.md +244 -0
- package/framework/agents.json +127 -1198
- package/framework/commands/morph-apply.md +56 -240
- package/framework/commands/morph-archive.md +48 -85
- package/framework/commands/morph-preflight.md +7 -7
- package/framework/commands/morph-proposal.md +35 -191
- package/framework/commands/morph-status.md +19 -50
- package/framework/commands/morph-troubleshoot.md +2 -2
- package/framework/evals/rubrics/README.md +44 -0
- package/framework/evals/rubrics/architecture.md +46 -0
- package/framework/evals/rubrics/code-quality.md +50 -0
- package/framework/evals/rubrics/contracts.md +47 -0
- package/framework/evals/rubrics/test-coverage.md +50 -0
- package/framework/evals/scenarios/dotnet-crud.yaml +42 -0
- package/framework/evals/scenarios/nextjs-auth.yaml +38 -0
- package/framework/evals/suites/standard.yaml +5 -0
- package/framework/evals/trace-schema.md +67 -0
- package/framework/hooks/claude-code/core/README.md +3 -0
- package/framework/hooks/claude-code/core/denylist.json +14 -0
- package/framework/hooks/claude-code/{pre-tool-use → core}/enforce-phase-writes.js +5 -5
- package/framework/hooks/claude-code/core/post-edit-typecheck.js +69 -0
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +29 -0
- package/framework/hooks/claude-code/{pre-tool-use → core}/protect-spec-files.js +4 -6
- package/framework/hooks/claude-code/{stop → core}/validate-completion.js +13 -82
- package/framework/hooks/shared/phase-utils.js +20 -31
- package/framework/hooks/shared/state-reader.js +4 -5
- package/framework/{skills/level-0-meta/mcp-registry.json → mcp/registry.json} +254 -243
- package/framework/phases.json +41 -115
- package/framework/rubrics/eval-rubric.json +66 -0
- package/framework/skills/README.md +39 -64
- package/framework/skills/core/README.md +3 -0
- package/framework/skills/core/morph-brainstorming/SKILL.md +102 -0
- package/framework/skills/core/morph-eval/SKILL.md +57 -0
- package/framework/skills/core/morph-implement/SKILL.md +83 -0
- package/framework/skills/core/morph-plan/SKILL.md +64 -0
- package/framework/skills/core/morph-review/SKILL.md +62 -0
- package/framework/skills/core/morph-stack-scan/SKILL.md +109 -0
- package/framework/skills/core/morph-standards/SKILL.md +49 -0
- package/framework/templates/CLAUDE.md.core.guide.md +29 -0
- package/framework/templates/CLAUDE.md.core.template +22 -0
- package/framework/templates/REGISTRY.json +4 -4
- package/framework/workflows/configs/fast-track.json +5 -10
- package/framework/workflows/configs/full.json +4 -8
- package/framework/workflows/configs/quick.json +32 -0
- package/framework/workflows/configs/standard.json +57 -64
- package/package.json +7 -6
- package/src/commands/mcp/mcp-setup.js +58 -31
- package/src/commands/project/doctor.js +69 -70
- package/src/commands/project/init.js +26 -5
- package/src/commands/project/status.js +18 -19
- package/src/commands/state/approve.js +1 -1
- package/src/commands/validation/validate-feature.js +18 -112
- package/src/core/paths/output-schema.js +133 -152
- package/src/core/state/phase-state-machine.js +10 -24
- package/src/core/state/state-manager.js +17 -17
- package/src/core/workflows/workflow-detector.js +214 -31
- package/src/core/workflows/workflow-router.js +204 -0
- package/src/dashboard/app/pages/AgentMap.jsx +32 -33
- package/src/dashboard/app/pages/LiveState.jsx +1 -1
- package/src/dashboard/app/pages/PhasePipeline.jsx +2 -4
- package/src/dashboard/app/pages/SkillsLibrary.jsx +2 -3
- package/src/dashboard/app/pages/Workflows.jsx +2 -2
- package/src/dashboard/scanner/agents-scanner.js +30 -35
- package/src/lib/agents/metrics-aggregator.js +1 -1
- package/src/lib/agents/output-collector.js +7 -9
- package/src/lib/context/context-builder.js +289 -0
- package/src/lib/detectors/claude-config-detector.js +19 -3
- package/src/lib/generators/context-generator.js +20 -42
- package/src/lib/generators/index.js +1 -0
- package/src/lib/generators/mandate-generator.js +512 -0
- package/src/lib/generators/metadata-extractor.js +1 -1
- package/src/lib/generators/recap-generator.js +3 -4
- package/src/lib/installers/mcp-installer.js +300 -92
- package/src/lib/phase-chain/phase-validator.js +36 -40
- package/src/lib/stack/stack-profile.js +74 -91
- package/src/lib/tasks/task-parser.js +1 -1
- package/src/lib/tasks/wave-resolver.js +353 -0
- package/src/lib/validators/checkpoint-persistence.js +2 -2
- package/src/lib/validators/content/cross-phase-validator.js +281 -0
- package/src/lib/validators/content/index.js +1 -0
- package/src/lib/validators/shared/emit-validator-dispatch.js +6 -56
- package/src/scripts/global-install.js +2 -27
- package/src/utils/agents-installer.js +224 -447
- package/src/utils/claude-settings-manager.js +57 -15
- package/src/utils/file-copier.js +1 -0
- package/src/utils/hooks-installer.js +15 -117
- package/src/utils/skills-installer.js +12 -18
- package/bin/detect-agents.js +0 -226
- package/bin/task-manager.js +0 -1036
- package/framework/agents/README.md +0 -13
- package/framework/agents/ai-agents/ai-system-architect.md +0 -194
- package/framework/agents/ai-agents/vector-search-expert.md +0 -247
- package/framework/agents/architecture/architecture-expert.md +0 -98
- package/framework/agents/architecture/morph-spec-architect.md +0 -66
- package/framework/agents/architecture/packages-validator.md +0 -89
- package/framework/agents/architecture/security-expert.md +0 -79
- package/framework/agents/architecture/seo-growth-hacker.md +0 -168
- package/framework/agents/architecture/standards-architect.md +0 -153
- package/framework/agents/backend/dotnet-senior.md +0 -163
- package/framework/agents/backend/ef-modeler.md +0 -119
- package/framework/agents/backend/hangfire-orchestrator.md +0 -133
- package/framework/agents/backend/ms-agent-expert.md +0 -55
- package/framework/agents/backend/neon-expert.md +0 -379
- package/framework/agents/frontend/css-specialist.md +0 -89
- package/framework/agents/frontend/design-system-validator.md +0 -100
- package/framework/agents/frontend/gsap-animator.md +0 -131
- package/framework/agents/frontend/nextjs-component-validator.md +0 -97
- package/framework/agents/frontend/nextjs-expert.md +0 -201
- package/framework/agents/frontend/ui-designer.md +0 -259
- package/framework/agents/infrastructure/infra-architect.md +0 -215
- package/framework/agents/integrations/asaas-financial.md +0 -138
- package/framework/agents/integrations/neon-auth.md +0 -151
- package/framework/agents/integrations/resend-email.md +0 -127
- package/framework/hooks/claude-code/post-tool-use/agent-complete.js +0 -122
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +0 -363
- package/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +0 -78
- package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +0 -194
- package/framework/hooks/claude-code/pre-compact/save-morph-context.js +0 -199
- package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +0 -55
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +0 -323
- package/framework/hooks/claude-code/session-start/post-compact-restore.js +0 -41
- package/framework/hooks/claude-code/session-start/resume-context.js +0 -77
- package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +0 -90
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +0 -114
- package/framework/hooks/shared/compact-restore.js +0 -123
- package/framework/hooks/shared/dispatch-helpers.js +0 -147
- package/framework/hooks/shared/skill-reminder-helpers.js +0 -79
- package/framework/hooks/shared/stale-task-reset.js +0 -57
- package/framework/hooks/shared/worktree-helpers.js +0 -75
- package/framework/skills/level-0-meta/README.md +0 -7
- package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +0 -472
- package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +0 -174
- package/framework/skills/level-0-meta/morph-brainstorming/references/proposal-example.md +0 -138
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +0 -145
- package/framework/skills/level-0-meta/morph-code-review/SKILL.md +0 -250
- package/framework/skills/level-0-meta/morph-code-review/references/review-example.md +0 -164
- package/framework/skills/level-0-meta/morph-code-review/references/review-guidelines.md +0 -100
- package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +0 -151
- package/framework/skills/level-0-meta/morph-code-review-nextjs/SKILL.md +0 -165
- package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +0 -254
- package/framework/skills/level-0-meta/morph-code-review-nextjs/scripts/scan-nextjs.mjs +0 -189
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +0 -344
- package/framework/skills/level-0-meta/morph-frontend-review/scripts/scan-accessibility.mjs +0 -376
- package/framework/skills/level-0-meta/morph-gemini-image/SKILL.md +0 -290
- package/framework/skills/level-0-meta/morph-init/SKILL.md +0 -537
- package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +0 -225
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +0 -401
- package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-dev-server.mjs +0 -153
- package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-stack.mjs +0 -234
- package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +0 -106
- package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +0 -243
- package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +0 -169
- package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +0 -148
- package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +0 -110
- package/framework/skills/level-1-workflows/README.md +0 -7
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +0 -269
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +0 -453
- package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +0 -44
- package/framework/skills/level-1-workflows/morph-phase-design/references/spec-authoring-guide.md +0 -55
- package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +0 -253
- package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +0 -634
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +0 -50
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +0 -54
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +0 -47
- package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +0 -132
- package/framework/skills/level-1-workflows/morph-phase-implement/references/vsa-implementation-guide.md +0 -92
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +0 -257
- package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +0 -291
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -143
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +0 -404
- package/framework/skills/level-1-workflows/morph-phase-tasks/scripts/validate-tasks.mjs +0 -112
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +0 -995
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +0 -147
- package/framework/squad-templates/backend-only.json +0 -47
- package/framework/squad-templates/frontend-only.json +0 -56
- package/framework/squad-templates/full-stack.json +0 -74
- package/src/commands/agents/agent-metrics.js +0 -174
- package/src/commands/agents/audit-dispatch.js +0 -82
- package/src/commands/agents/detect-agents.js +0 -275
- package/src/commands/agents/dispatch-agents.js +0 -461
- package/src/commands/agents/index.js +0 -13
- package/src/commands/agents/manage-agents.js +0 -352
- package/src/commands/agents/project-metrics.js +0 -74
- package/src/commands/phase/phase-reset.js +0 -74
- package/src/commands/scope/escalate.js +0 -217
- package/src/commands/state/advance-phase.js +0 -600
- package/src/commands/state/phase-runner.js +0 -215
- package/src/commands/state/state.js +0 -444
- package/src/commands/task/expand.js +0 -100
- package/src/commands/tasks/index.js +0 -4
- package/src/commands/tasks/task.js +0 -105
- package/src/commands/templates/index.js +0 -5
- package/src/commands/templates/template-render.js +0 -93
- package/src/commands/trust/trust.js +0 -118
- package/src/lib/agents/agents-manager.js +0 -349
- package/src/lib/build/build-runner.js +0 -99
- package/src/lib/checkpoints/checkpoint-hooks.js +0 -111
- package/src/lib/contracts/sprint-contract-generator.js +0 -137
- package/src/lib/scope/impact-analyzer.js +0 -106
- package/src/lib/trust/trust-manager.js +0 -157
- package/src/utils/tier-helpers.js +0 -24
- /package/framework/hooks/claude-code/{post-tool-use → core}/loop-detect.js +0 -0
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* detect-agents — Detect relevant agents for a feature based on keywords
|
|
3
|
-
*
|
|
4
|
-
* Supports single string input (legacy) and multi-source TextSource[] input
|
|
5
|
-
* with weighted keyword scoring. Also provides refreshAgents() for additive
|
|
6
|
-
* agent detection at phase boundaries.
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* morph-spec detect-agents <feature> [--proposal <path>] [--refresh] [--json] [--table]
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { readFileSync, existsSync } from 'fs';
|
|
13
|
-
import { join, dirname } from 'path';
|
|
14
|
-
import { fileURLToPath } from 'url';
|
|
15
|
-
import chalk from 'chalk';
|
|
16
|
-
import { collectPhaseOutputs } from '../../lib/agents/output-collector.js';
|
|
17
|
-
import { loadAgentsFile } from '../../lib/agents/agents-manager.js';
|
|
18
|
-
import { loadState, saveState, addAgent } from '../../core/state/state-manager.js';
|
|
19
|
-
|
|
20
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
|
-
|
|
22
|
-
/** Default path to agents.json in the installed package */
|
|
23
|
-
const AGENTS_JSON_PATH = join(__dirname, '../../../framework/agents.json');
|
|
24
|
-
|
|
25
|
-
/** Match weight constants */
|
|
26
|
-
const MATCH_WEIGHTS = {
|
|
27
|
-
heading: 3,
|
|
28
|
-
body: 1,
|
|
29
|
-
domain: 2,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Detect agents relevant to a feature.
|
|
34
|
-
*
|
|
35
|
-
* @param {string|Array<{source: string, content: string, weight: number}>} sourcesOrText - Text sources or legacy string
|
|
36
|
-
* @param {Object} agentsJson - Parsed agents.json content
|
|
37
|
-
* @param {Object} [opts] - Options
|
|
38
|
-
* @param {string[]} [opts.excludeIds] - Agent IDs to exclude from detection
|
|
39
|
-
* @returns {{ detected: Array<{id: string, reason: string, keywords_matched: string[], score: number, sources: Array}>, skipped: Array<{id: string, reason: string}> }}
|
|
40
|
-
*/
|
|
41
|
-
export function detectAgents(sourcesOrText, agentsJson, opts = {}) {
|
|
42
|
-
// Backward compatibility: wrap string as single source
|
|
43
|
-
const sources = typeof sourcesOrText === 'string'
|
|
44
|
-
? [{ source: 'proposal.md', content: sourcesOrText, weight: 1.0 }]
|
|
45
|
-
: sourcesOrText;
|
|
46
|
-
|
|
47
|
-
const allAgents = agentsJson.agents || {};
|
|
48
|
-
const excludeIds = new Set(opts.excludeIds || []);
|
|
49
|
-
|
|
50
|
-
const detected = [];
|
|
51
|
-
const skipped = [];
|
|
52
|
-
|
|
53
|
-
for (const [agentId, agentData] of Object.entries(allAgents)) {
|
|
54
|
-
if (excludeIds.has(agentId)) continue;
|
|
55
|
-
if (!agentData || typeof agentData !== 'object' || !agentData.tier) continue;
|
|
56
|
-
|
|
57
|
-
// Always-active agents (active_phases includes '*')
|
|
58
|
-
if (Array.isArray(agentData.active_phases) && agentData.active_phases.includes('*')) {
|
|
59
|
-
detected.push({
|
|
60
|
-
id: agentId,
|
|
61
|
-
reason: 'active_phases: *',
|
|
62
|
-
keywords_matched: [],
|
|
63
|
-
score: 0,
|
|
64
|
-
sources: [],
|
|
65
|
-
});
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Score across all sources
|
|
70
|
-
const keywords = agentData.keywords || [];
|
|
71
|
-
const domains = agentData.domains || [];
|
|
72
|
-
let totalScore = 0;
|
|
73
|
-
const allMatched = new Set();
|
|
74
|
-
const sourceBreakdown = [];
|
|
75
|
-
|
|
76
|
-
for (const src of sources) {
|
|
77
|
-
const textLower = src.content.toLowerCase();
|
|
78
|
-
const lines = src.content.split('\n');
|
|
79
|
-
let srcScore = 0;
|
|
80
|
-
const srcKeywords = [];
|
|
81
|
-
|
|
82
|
-
// Check keywords
|
|
83
|
-
for (const kw of keywords) {
|
|
84
|
-
const kwLower = kw.toLowerCase();
|
|
85
|
-
if (!textLower.includes(kwLower)) continue;
|
|
86
|
-
|
|
87
|
-
allMatched.add(kw);
|
|
88
|
-
srcKeywords.push(kw);
|
|
89
|
-
|
|
90
|
-
// Check if keyword appears in a heading line
|
|
91
|
-
const inHeading = lines.some(line =>
|
|
92
|
-
/^#{1,6}\s/.test(line) && line.toLowerCase().includes(kwLower)
|
|
93
|
-
);
|
|
94
|
-
srcScore += (inHeading ? MATCH_WEIGHTS.heading : MATCH_WEIGHTS.body) * src.weight;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Check domains
|
|
98
|
-
for (const domain of domains) {
|
|
99
|
-
const domLower = domain.toLowerCase();
|
|
100
|
-
if (!textLower.includes(domLower)) continue;
|
|
101
|
-
|
|
102
|
-
allMatched.add(domain);
|
|
103
|
-
srcKeywords.push(domain);
|
|
104
|
-
srcScore += MATCH_WEIGHTS.domain * src.weight;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (srcScore > 0) {
|
|
108
|
-
sourceBreakdown.push({
|
|
109
|
-
source: src.source,
|
|
110
|
-
keywords: srcKeywords,
|
|
111
|
-
score: srcScore,
|
|
112
|
-
});
|
|
113
|
-
totalScore += srcScore;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (totalScore > 0) {
|
|
118
|
-
detected.push({
|
|
119
|
-
id: agentId,
|
|
120
|
-
reason: 'keyword',
|
|
121
|
-
keywords_matched: [...allMatched],
|
|
122
|
-
score: totalScore,
|
|
123
|
-
sources: sourceBreakdown,
|
|
124
|
-
});
|
|
125
|
-
} else {
|
|
126
|
-
skipped.push({
|
|
127
|
-
id: agentId,
|
|
128
|
-
reason: 'no_keyword_match',
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return { detected, skipped };
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Refresh agent detection using all available phase outputs.
|
|
138
|
-
* Additive only — never removes existing agents.
|
|
139
|
-
*
|
|
140
|
-
* @param {string} projectRoot - Absolute path
|
|
141
|
-
* @param {string} featureName - Feature name
|
|
142
|
-
* @returns {Promise<{added: Array, unchanged: number, total: number}>}
|
|
143
|
-
*/
|
|
144
|
-
export async function refreshAgents(projectRoot, featureName) {
|
|
145
|
-
const state = loadState(false);
|
|
146
|
-
const feature = state?.features?.[featureName];
|
|
147
|
-
if (!feature) {
|
|
148
|
-
return { added: [], unchanged: 0, total: 0 };
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const currentAgents = new Set(feature.activeAgents || []);
|
|
152
|
-
|
|
153
|
-
const sources = collectPhaseOutputs(projectRoot, featureName);
|
|
154
|
-
if (sources.length === 0) {
|
|
155
|
-
return { added: [], unchanged: currentAgents.size, total: currentAgents.size };
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
let agentsJson;
|
|
159
|
-
try {
|
|
160
|
-
agentsJson = loadAgentsFile(AGENTS_JSON_PATH);
|
|
161
|
-
} catch {
|
|
162
|
-
return { added: [], unchanged: currentAgents.size, total: currentAgents.size };
|
|
163
|
-
}
|
|
164
|
-
const { detected } = detectAgents(sources, agentsJson);
|
|
165
|
-
|
|
166
|
-
const added = detected.filter(a => !currentAgents.has(a.id));
|
|
167
|
-
|
|
168
|
-
// Additive: add new agents to state
|
|
169
|
-
for (const agent of added) {
|
|
170
|
-
await addAgent(featureName, agent.id);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return {
|
|
174
|
-
added,
|
|
175
|
-
unchanged: currentAgents.size,
|
|
176
|
-
total: currentAgents.size + added.length,
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Display detection diff after refresh.
|
|
182
|
-
*
|
|
183
|
-
* @param {{added: Array, unchanged: number, total: number}} result
|
|
184
|
-
* @param {string} fromPhase
|
|
185
|
-
* @param {string} toPhase
|
|
186
|
-
*/
|
|
187
|
-
export function displayDetectionDiff(result, fromPhase, toPhase) {
|
|
188
|
-
console.log(chalk.cyan(`\nAgent Detection Refresh (${fromPhase} \u2192 ${toPhase}):`));
|
|
189
|
-
|
|
190
|
-
for (const agent of result.added) {
|
|
191
|
-
const kws = agent.keywords_matched.join('", "');
|
|
192
|
-
console.log(chalk.green(` + ${agent.id.padEnd(24)} (keyword: "${kws}" | score: ${agent.score})`));
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
console.log(chalk.gray(` = ${result.unchanged} agents unchanged`));
|
|
196
|
-
console.log(chalk.white(` Total: ${result.total} active agents\n`));
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* CLI command: morph-spec detect-agents <feature>
|
|
201
|
-
*/
|
|
202
|
-
export async function detectAgentsCommand(featureName, options = {}) {
|
|
203
|
-
if (!featureName) {
|
|
204
|
-
console.error(chalk.red('Usage: morph-spec detect-agents <feature> [--proposal <path>] [--refresh] [--json] [--table]'));
|
|
205
|
-
process.exit(1);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// --refresh mode: use all phase outputs and mutate state
|
|
209
|
-
if (options.refresh) {
|
|
210
|
-
const result = await refreshAgents(process.cwd(), featureName);
|
|
211
|
-
if (options.json) {
|
|
212
|
-
console.log(JSON.stringify(result, null, 2));
|
|
213
|
-
} else {
|
|
214
|
-
displayDetectionDiff(result, 'manual', 'refresh');
|
|
215
|
-
}
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Resolve proposal path
|
|
220
|
-
const proposalPath = options.proposal
|
|
221
|
-
|| join(process.cwd(), `.morph/features/${featureName}/0-proposal/proposal.md`);
|
|
222
|
-
|
|
223
|
-
if (!existsSync(proposalPath)) {
|
|
224
|
-
console.error(chalk.red(`Proposal not found: ${proposalPath}`));
|
|
225
|
-
console.error(chalk.gray('Create the proposal first or use --proposal <path> to specify a custom path.'));
|
|
226
|
-
process.exit(1);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Load agents.json
|
|
230
|
-
const agentsPath = options.agentsJson || AGENTS_JSON_PATH;
|
|
231
|
-
let agentsJson;
|
|
232
|
-
try {
|
|
233
|
-
agentsJson = loadAgentsFile(agentsPath);
|
|
234
|
-
} catch (err) {
|
|
235
|
-
console.error(chalk.red(err.message));
|
|
236
|
-
process.exit(1);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const proposalText = readFileSync(proposalPath, 'utf8');
|
|
240
|
-
|
|
241
|
-
const result = detectAgents(proposalText, agentsJson, {
|
|
242
|
-
excludeIds: options.exclude ? options.exclude.split(',') : [],
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
const output = {
|
|
246
|
-
feature: featureName,
|
|
247
|
-
...result,
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
if (options.table) {
|
|
251
|
-
// Human-readable table
|
|
252
|
-
console.log(chalk.bold(`\n Detected Agents \u2014 ${featureName}`));
|
|
253
|
-
console.log(' ' + '\u2500'.repeat(62));
|
|
254
|
-
|
|
255
|
-
if (result.detected.length > 0) {
|
|
256
|
-
console.log(` ${'ID'.padEnd(28)} ${'Reason'.padEnd(16)} Keywords`);
|
|
257
|
-
console.log(' ' + '\u2500'.repeat(62));
|
|
258
|
-
for (const agent of result.detected) {
|
|
259
|
-
const reason = agent.reason === 'active_phases: *' ? 'always' : agent.reason;
|
|
260
|
-
const kw = agent.keywords_matched.length > 0 ? agent.keywords_matched.join(', ') : '\u2014';
|
|
261
|
-
console.log(
|
|
262
|
-
` ${agent.id.padEnd(28)} ${chalk.cyan(reason.padEnd(16))} ${kw}`
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
} else {
|
|
266
|
-
console.log(chalk.gray(' No agents detected.'));
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
console.log(`\n Skipped: ${result.skipped.length} agents (no keyword match)`);
|
|
270
|
-
console.log();
|
|
271
|
-
} else {
|
|
272
|
-
// JSON output (default)
|
|
273
|
-
console.log(JSON.stringify(output, null, 2));
|
|
274
|
-
}
|
|
275
|
-
}
|