@polymorphism-tech/morph-spec 4.21.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/CLAUDE.md +57 -71
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +224 -491
  4. package/claude-plugin.json +1 -1
  5. package/docs/CHEATSHEET.md +1 -1
  6. package/docs/QUICKSTART.md +1 -1
  7. package/framework/CLAUDE.md +55 -38
  8. package/framework/MORPH.md +244 -0
  9. package/framework/agents.json +127 -1183
  10. package/framework/commands/morph-apply.md +56 -240
  11. package/framework/commands/morph-archive.md +48 -85
  12. package/framework/commands/morph-preflight.md +7 -7
  13. package/framework/commands/morph-proposal.md +35 -191
  14. package/framework/commands/morph-status.md +19 -50
  15. package/framework/commands/morph-troubleshoot.md +2 -2
  16. package/framework/evals/rubrics/README.md +44 -0
  17. package/framework/evals/rubrics/architecture.md +46 -0
  18. package/framework/evals/rubrics/code-quality.md +50 -0
  19. package/framework/evals/rubrics/contracts.md +47 -0
  20. package/framework/evals/rubrics/test-coverage.md +50 -0
  21. package/framework/evals/scenarios/dotnet-crud.yaml +42 -0
  22. package/framework/evals/scenarios/nextjs-auth.yaml +38 -0
  23. package/framework/evals/suites/standard.yaml +5 -0
  24. package/framework/evals/trace-schema.md +67 -0
  25. package/framework/hooks/claude-code/core/README.md +3 -0
  26. package/framework/hooks/claude-code/core/denylist.json +14 -0
  27. package/framework/hooks/claude-code/{pre-tool-use → core}/enforce-phase-writes.js +5 -5
  28. package/framework/hooks/claude-code/core/post-edit-typecheck.js +69 -0
  29. package/framework/hooks/claude-code/core/pre-bash-denylist.js +29 -0
  30. package/framework/hooks/claude-code/{pre-tool-use → core}/protect-spec-files.js +4 -6
  31. package/framework/hooks/claude-code/{stop → core}/validate-completion.js +13 -82
  32. package/framework/hooks/shared/phase-utils.js +20 -28
  33. package/framework/hooks/shared/state-reader.js +4 -5
  34. package/framework/{skills/level-0-meta/mcp-registry.json → mcp/registry.json} +254 -210
  35. package/framework/phases.json +40 -114
  36. package/framework/rubrics/eval-rubric.json +66 -0
  37. package/framework/skills/README.md +39 -64
  38. package/framework/skills/core/README.md +3 -0
  39. package/framework/skills/core/morph-brainstorming/SKILL.md +102 -0
  40. package/framework/skills/core/morph-eval/SKILL.md +57 -0
  41. package/framework/skills/core/morph-implement/SKILL.md +83 -0
  42. package/framework/skills/core/morph-plan/SKILL.md +64 -0
  43. package/framework/skills/core/morph-review/SKILL.md +62 -0
  44. package/framework/skills/core/morph-stack-scan/SKILL.md +109 -0
  45. package/framework/skills/core/morph-standards/SKILL.md +49 -0
  46. package/framework/standards/STANDARDS.json +32 -0
  47. package/framework/standards/frontend/design-system/ai-image-generation.md +244 -0
  48. package/framework/standards/integration/mcp/mcp-tools.md +48 -0
  49. package/framework/templates/CLAUDE.md.core.guide.md +29 -0
  50. package/framework/templates/CLAUDE.md.core.template +22 -0
  51. package/framework/templates/REGISTRY.json +4 -4
  52. package/framework/workflows/configs/fast-track.json +5 -10
  53. package/framework/workflows/configs/full.json +4 -8
  54. package/framework/workflows/configs/quick.json +32 -0
  55. package/framework/workflows/configs/standard.json +57 -64
  56. package/package.json +7 -6
  57. package/src/commands/mcp/mcp-setup.js +58 -31
  58. package/src/commands/project/doctor.js +69 -70
  59. package/src/commands/project/init.js +26 -5
  60. package/src/commands/project/status.js +18 -19
  61. package/src/commands/state/approve.js +1 -1
  62. package/src/commands/validation/validate-feature.js +18 -112
  63. package/src/core/paths/output-schema.js +133 -151
  64. package/src/core/state/phase-state-machine.js +10 -24
  65. package/src/core/state/state-manager.js +17 -17
  66. package/src/core/workflows/workflow-detector.js +214 -31
  67. package/src/core/workflows/workflow-router.js +204 -0
  68. package/src/dashboard/app/pages/AgentMap.jsx +32 -33
  69. package/src/dashboard/app/pages/LiveState.jsx +1 -1
  70. package/src/dashboard/app/pages/PhasePipeline.jsx +2 -4
  71. package/src/dashboard/app/pages/SkillsLibrary.jsx +2 -3
  72. package/src/dashboard/app/pages/Workflows.jsx +2 -2
  73. package/src/dashboard/scanner/agents-scanner.js +30 -35
  74. package/src/lib/agents/metrics-aggregator.js +1 -1
  75. package/src/lib/agents/output-collector.js +7 -8
  76. package/src/lib/context/context-builder.js +289 -0
  77. package/src/lib/detectors/claude-config-detector.js +19 -3
  78. package/src/lib/generators/context-generator.js +20 -42
  79. package/src/lib/generators/index.js +1 -0
  80. package/src/lib/generators/mandate-generator.js +512 -0
  81. package/src/lib/generators/metadata-extractor.js +1 -1
  82. package/src/lib/generators/recap-generator.js +3 -4
  83. package/src/lib/installers/mcp-installer.js +300 -92
  84. package/src/lib/phase-chain/phase-validator.js +36 -40
  85. package/src/lib/stack/stack-profile.js +74 -91
  86. package/src/lib/tasks/task-parser.js +1 -1
  87. package/src/lib/tasks/wave-resolver.js +353 -0
  88. package/src/lib/validators/checkpoint-persistence.js +2 -2
  89. package/src/lib/validators/content/cross-phase-validator.js +281 -0
  90. package/src/lib/validators/content/index.js +1 -0
  91. package/src/lib/validators/shared/emit-validator-dispatch.js +6 -56
  92. package/src/scripts/global-install.js +2 -27
  93. package/src/utils/agents-installer.js +224 -447
  94. package/src/utils/claude-settings-manager.js +57 -15
  95. package/src/utils/file-copier.js +1 -0
  96. package/src/utils/hooks-installer.js +15 -117
  97. package/src/utils/skills-installer.js +12 -18
  98. package/bin/detect-agents.js +0 -226
  99. package/bin/task-manager.js +0 -1036
  100. package/framework/agents/README.md +0 -13
  101. package/framework/agents/ai-agents/ai-system-architect.md +0 -194
  102. package/framework/agents/ai-agents/vector-search-expert.md +0 -247
  103. package/framework/agents/architecture/architecture-expert.md +0 -98
  104. package/framework/agents/architecture/morph-spec-architect.md +0 -66
  105. package/framework/agents/architecture/packages-validator.md +0 -89
  106. package/framework/agents/architecture/security-expert.md +0 -79
  107. package/framework/agents/architecture/seo-growth-hacker.md +0 -168
  108. package/framework/agents/architecture/standards-architect.md +0 -153
  109. package/framework/agents/backend/dotnet-senior.md +0 -163
  110. package/framework/agents/backend/ef-modeler.md +0 -119
  111. package/framework/agents/backend/hangfire-orchestrator.md +0 -133
  112. package/framework/agents/backend/ms-agent-expert.md +0 -55
  113. package/framework/agents/backend/neon-expert.md +0 -379
  114. package/framework/agents/frontend/css-specialist.md +0 -89
  115. package/framework/agents/frontend/design-system-validator.md +0 -100
  116. package/framework/agents/frontend/gsap-animator.md +0 -131
  117. package/framework/agents/frontend/nextjs-component-validator.md +0 -97
  118. package/framework/agents/frontend/nextjs-expert.md +0 -201
  119. package/framework/agents/frontend/ui-designer.md +0 -259
  120. package/framework/agents/infrastructure/infra-architect.md +0 -215
  121. package/framework/agents/integrations/asaas-financial.md +0 -138
  122. package/framework/agents/integrations/neon-auth.md +0 -151
  123. package/framework/agents/integrations/resend-email.md +0 -127
  124. package/framework/hooks/claude-code/post-tool-use/agent-complete.js +0 -122
  125. package/framework/hooks/claude-code/post-tool-use/dispatch.js +0 -363
  126. package/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +0 -78
  127. package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +0 -194
  128. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +0 -199
  129. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +0 -55
  130. package/framework/hooks/claude-code/session-start/inject-morph-context.js +0 -323
  131. package/framework/hooks/claude-code/session-start/post-compact-restore.js +0 -41
  132. package/framework/hooks/claude-code/session-start/resume-context.js +0 -77
  133. package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +0 -90
  134. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +0 -114
  135. package/framework/hooks/shared/compact-restore.js +0 -123
  136. package/framework/hooks/shared/dispatch-helpers.js +0 -147
  137. package/framework/hooks/shared/skill-reminder-helpers.js +0 -79
  138. package/framework/hooks/shared/stale-task-reset.js +0 -57
  139. package/framework/hooks/shared/worktree-helpers.js +0 -75
  140. package/framework/skills/level-0-meta/README.md +0 -7
  141. package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +0 -472
  142. package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +0 -174
  143. package/framework/skills/level-0-meta/morph-brainstorming/references/proposal-example.md +0 -138
  144. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +0 -145
  145. package/framework/skills/level-0-meta/morph-code-review/SKILL.md +0 -250
  146. package/framework/skills/level-0-meta/morph-code-review/references/review-example.md +0 -164
  147. package/framework/skills/level-0-meta/morph-code-review/references/review-guidelines.md +0 -100
  148. package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +0 -151
  149. package/framework/skills/level-0-meta/morph-code-review-nextjs/SKILL.md +0 -165
  150. package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +0 -254
  151. package/framework/skills/level-0-meta/morph-code-review-nextjs/scripts/scan-nextjs.mjs +0 -189
  152. package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +0 -344
  153. package/framework/skills/level-0-meta/morph-frontend-review/scripts/scan-accessibility.mjs +0 -376
  154. package/framework/skills/level-0-meta/morph-init/SKILL.md +0 -537
  155. package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +0 -225
  156. package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +0 -382
  157. package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-dev-server.mjs +0 -153
  158. package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-stack.mjs +0 -234
  159. package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +0 -106
  160. package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +0 -243
  161. package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +0 -169
  162. package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +0 -148
  163. package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +0 -110
  164. package/framework/skills/level-1-workflows/README.md +0 -7
  165. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +0 -269
  166. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +0 -453
  167. package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +0 -44
  168. package/framework/skills/level-1-workflows/morph-phase-design/references/spec-authoring-guide.md +0 -55
  169. package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +0 -253
  170. package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
  171. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +0 -493
  172. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +0 -50
  173. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +0 -46
  174. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +0 -47
  175. package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +0 -132
  176. package/framework/skills/level-1-workflows/morph-phase-implement/references/vsa-implementation-guide.md +0 -92
  177. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +0 -257
  178. package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
  179. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +0 -291
  180. package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -143
  181. package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +0 -404
  182. package/framework/skills/level-1-workflows/morph-phase-tasks/scripts/validate-tasks.mjs +0 -112
  183. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +0 -788
  184. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +0 -147
  185. package/framework/squad-templates/backend-only.json +0 -47
  186. package/framework/squad-templates/frontend-only.json +0 -56
  187. package/framework/squad-templates/full-stack.json +0 -74
  188. package/src/commands/agents/agent-metrics.js +0 -174
  189. package/src/commands/agents/audit-dispatch.js +0 -82
  190. package/src/commands/agents/detect-agents.js +0 -275
  191. package/src/commands/agents/dispatch-agents.js +0 -461
  192. package/src/commands/agents/index.js +0 -13
  193. package/src/commands/agents/manage-agents.js +0 -352
  194. package/src/commands/agents/project-metrics.js +0 -74
  195. package/src/commands/phase/phase-reset.js +0 -74
  196. package/src/commands/scope/escalate.js +0 -217
  197. package/src/commands/state/advance-phase.js +0 -600
  198. package/src/commands/state/phase-runner.js +0 -215
  199. package/src/commands/state/state.js +0 -444
  200. package/src/commands/task/expand.js +0 -100
  201. package/src/commands/tasks/index.js +0 -4
  202. package/src/commands/tasks/task.js +0 -105
  203. package/src/commands/templates/index.js +0 -5
  204. package/src/commands/templates/template-render.js +0 -93
  205. package/src/commands/trust/trust.js +0 -118
  206. package/src/lib/agents/agents-manager.js +0 -349
  207. package/src/lib/build/build-runner.js +0 -99
  208. package/src/lib/checkpoints/checkpoint-hooks.js +0 -111
  209. package/src/lib/contracts/sprint-contract-generator.js +0 -137
  210. package/src/lib/scope/impact-analyzer.js +0 -106
  211. package/src/lib/trust/trust-manager.js +0 -157
  212. package/src/utils/tier-helpers.js +0 -24
  213. /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
- }