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