monomind 1.17.0 → 1.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/engineering/engineering-security-engineer.md +1 -1
- package/.claude/commands/mastermind/_repeat.md +4 -0
- package/.claude/commands/mastermind/master.md +52 -1
- package/.claude/scheduled_tasks.lock +1 -1
- package/.claude/skills/mastermind/_repeat.md +2 -0
- package/package.json +1 -1
- package/packages/@monomind/cli/.claude/agents/engineering/engineering-security-engineer.md +1 -1
- package/packages/@monomind/cli/.claude/commands/mastermind/_repeat.md +4 -0
- package/packages/@monomind/cli/.claude/commands/mastermind/master.md +52 -1
- package/packages/@monomind/cli/.claude/skills/mastermind/_repeat.md +2 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-analyzer.test.js +42 -59
- package/packages/@monomind/cli/dist/src/browser/dashboard/server.js +18 -0
- package/packages/@monomind/cli/dist/src/browser/dashboard/ui.html +37 -125
- package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.d.ts +17 -0
- package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.js +320 -0
- package/packages/@monomind/cli/dist/src/commands/agent-ops.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/commands/agent-ops.js +329 -0
- package/packages/@monomind/cli/dist/src/commands/agent.js +5 -907
- package/packages/@monomind/cli/dist/src/commands/analyze-ast.d.ts +26 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-ast.js +284 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-boundaries.d.ts +14 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-boundaries.js +295 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-diff.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-diff.js +395 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-graph.d.ts +14 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-graph.js +304 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-imports.d.ts +11 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-imports.js +287 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-symbols.d.ts +14 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-symbols.js +302 -0
- package/packages/@monomind/cli/dist/src/commands/analyze.d.ts +38 -0
- package/packages/@monomind/cli/dist/src/commands/analyze.js +12 -1827
- package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.d.ts +26 -0
- package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.js +189 -0
- package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.d.ts +19 -0
- package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.js +388 -0
- package/packages/@monomind/cli/dist/src/commands/doctor.js +51 -942
- package/packages/@monomind/cli/dist/src/commands/hive-mind-comms.d.ts +11 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-comms.js +242 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-helpers.d.ts +35 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-helpers.js +203 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-ops.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-ops.js +233 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-spawn.d.ts +12 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-spawn.js +274 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind.js +10 -1129
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.d.ts +4 -4
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.js +19 -819
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.js +334 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-routing.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-routing.js +399 -0
- package/packages/@monomind/cli/dist/src/commands/init-subcommands.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/init-subcommands.js +156 -0
- package/packages/@monomind/cli/dist/src/commands/init-upgrade.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/commands/init-upgrade.js +203 -0
- package/packages/@monomind/cli/dist/src/commands/init-wizard.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/commands/init-wizard.js +246 -0
- package/packages/@monomind/cli/dist/src/commands/init.js +6 -623
- package/packages/@monomind/cli/dist/src/commands/memory-admin.d.ts +10 -0
- package/packages/@monomind/cli/dist/src/commands/memory-admin.js +433 -0
- package/packages/@monomind/cli/dist/src/commands/memory-crud.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/commands/memory-crud.js +342 -0
- package/packages/@monomind/cli/dist/src/commands/memory-list.d.ts +10 -0
- package/packages/@monomind/cli/dist/src/commands/memory-list.js +321 -0
- package/packages/@monomind/cli/dist/src/commands/memory-transfer.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/commands/memory-transfer.js +372 -0
- package/packages/@monomind/cli/dist/src/commands/memory.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/commands/memory.js +10 -1441
- package/packages/@monomind/cli/dist/src/commands/neural-core.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/neural-core.js +274 -0
- package/packages/@monomind/cli/dist/src/commands/neural-optimize.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/commands/neural-optimize.js +332 -0
- package/packages/@monomind/cli/dist/src/commands/neural-registry.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/commands/neural-registry.js +290 -0
- package/packages/@monomind/cli/dist/src/commands/neural.js +3 -974
- package/packages/@monomind/cli/dist/src/commands/platforms.js +327 -7
- package/packages/@monomind/cli/dist/src/commands/security-cve.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/commands/security-cve.js +310 -0
- package/packages/@monomind/cli/dist/src/commands/security-misc.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/commands/security-misc.js +293 -0
- package/packages/@monomind/cli/dist/src/commands/security-scan.d.ts +18 -0
- package/packages/@monomind/cli/dist/src/commands/security-scan.js +328 -0
- package/packages/@monomind/cli/dist/src/commands/security.js +3 -958
- package/packages/@monomind/cli/dist/src/commands/session.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/swarm.js +23 -17
- package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +77 -0
- package/packages/@monomind/cli/dist/src/parser.js +11 -6
- package/packages/@monomind/cli/dist/src/routing/llm-caller.js +1 -2
- package/packages/@monomind/cli/package.json +2 -3
- package/packages/@monomind/cli/scripts/understand-analyze.mjs +1 -1
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coverage-aware routing commands: coverage-route, coverage-suggest
|
|
3
|
+
*/
|
|
4
|
+
import { output } from '../output.js';
|
|
5
|
+
import { callMCPTool } from '../mcp-client.js';
|
|
6
|
+
import { readCoverageFromDisk, classifyCoverageGap, suggestAgentsForFile, } from './hooks-coverage-utils.js';
|
|
7
|
+
export const coverageRouteCommand = {
|
|
8
|
+
name: 'coverage-route',
|
|
9
|
+
description: 'Route task to agents based on test coverage gaps (monovector integration)',
|
|
10
|
+
options: [
|
|
11
|
+
{
|
|
12
|
+
name: 'task',
|
|
13
|
+
short: 't',
|
|
14
|
+
description: 'Task description to route',
|
|
15
|
+
type: 'string',
|
|
16
|
+
required: true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'threshold',
|
|
20
|
+
description: 'Coverage threshold percentage (default: 80)',
|
|
21
|
+
type: 'number',
|
|
22
|
+
default: 80
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'no-monovector',
|
|
26
|
+
description: 'Disable monovector integration',
|
|
27
|
+
type: 'boolean',
|
|
28
|
+
default: false
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
examples: [
|
|
32
|
+
{ command: 'monomind hooks coverage-route -t "fix bug in auth"', description: 'Route with coverage awareness' },
|
|
33
|
+
{ command: 'monomind hooks coverage-route -t "add tests" --threshold 90', description: 'Route with custom threshold' }
|
|
34
|
+
],
|
|
35
|
+
action: async (ctx) => {
|
|
36
|
+
const task = ctx.args[0] || ctx.flags.task;
|
|
37
|
+
const threshold = ctx.flags.threshold || 80;
|
|
38
|
+
const useMonovector = !ctx.flags['no-monovector'];
|
|
39
|
+
if (!task) {
|
|
40
|
+
output.printError('Task description is required. Use --task or -t flag.');
|
|
41
|
+
return { success: false, exitCode: 1 };
|
|
42
|
+
}
|
|
43
|
+
const spinner = output.createSpinner({ text: 'Analyzing coverage and routing task...' });
|
|
44
|
+
spinner.start();
|
|
45
|
+
const diskCoverage = readCoverageFromDisk();
|
|
46
|
+
if (diskCoverage.found) {
|
|
47
|
+
spinner.succeed(`Coverage data loaded from ${diskCoverage.source}`);
|
|
48
|
+
const taskLower = task.toLowerCase();
|
|
49
|
+
const taskWords = taskLower.split(/\s+/).filter(w => w.length > 2);
|
|
50
|
+
const scoredFiles = diskCoverage.entries
|
|
51
|
+
.filter(e => e.lines < threshold)
|
|
52
|
+
.map(e => {
|
|
53
|
+
const fileNameLower = e.filePath.toLowerCase();
|
|
54
|
+
let relevance = 0;
|
|
55
|
+
for (const word of taskWords) {
|
|
56
|
+
if (fileNameLower.includes(word))
|
|
57
|
+
relevance += 2;
|
|
58
|
+
}
|
|
59
|
+
const coveragePenalty = e.lines / 100;
|
|
60
|
+
return { ...e, relevance, score: relevance + (1 - coveragePenalty) };
|
|
61
|
+
})
|
|
62
|
+
.sort((a, b) => b.score - a.score);
|
|
63
|
+
const gaps = scoredFiles.slice(0, 8).map(e => {
|
|
64
|
+
const { gapType, priority } = classifyCoverageGap(e.lines, threshold);
|
|
65
|
+
return {
|
|
66
|
+
filePath: e.filePath,
|
|
67
|
+
coveragePercent: e.lines,
|
|
68
|
+
gapType,
|
|
69
|
+
priority,
|
|
70
|
+
suggestedAgents: suggestAgentsForFile(e.filePath),
|
|
71
|
+
reason: `${e.lines.toFixed(1)}% coverage, below ${threshold}%`,
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
const criticalGaps = gaps.filter(g => g.gapType === 'critical').length;
|
|
75
|
+
const primaryAgent = taskLower.includes('test') ? 'tester' :
|
|
76
|
+
taskLower.includes('security') || taskLower.includes('auth') ? 'security-auditor' :
|
|
77
|
+
taskLower.includes('fix') || taskLower.includes('bug') ? 'coder' : 'tester';
|
|
78
|
+
const suggestions = [];
|
|
79
|
+
if (criticalGaps > 0)
|
|
80
|
+
suggestions.push(`${criticalGaps} critical coverage gaps need immediate attention`);
|
|
81
|
+
if (diskCoverage.summary.overallLineCoverage < threshold) {
|
|
82
|
+
suggestions.push(`Overall line coverage (${diskCoverage.summary.overallLineCoverage.toFixed(1)}%) is below ${threshold}% threshold`);
|
|
83
|
+
}
|
|
84
|
+
if (scoredFiles.length > 8)
|
|
85
|
+
suggestions.push(`${scoredFiles.length - 8} additional files with low coverage`);
|
|
86
|
+
const result = {
|
|
87
|
+
success: true,
|
|
88
|
+
task,
|
|
89
|
+
coverageAware: true,
|
|
90
|
+
gaps,
|
|
91
|
+
routing: {
|
|
92
|
+
primaryAgent,
|
|
93
|
+
confidence: gaps.length > 0 ? 0.85 : 0.6,
|
|
94
|
+
reason: gaps.length > 0
|
|
95
|
+
? `Routing to ${primaryAgent} based on ${gaps.length} coverage gaps related to task`
|
|
96
|
+
: `No coverage gaps found related to task, routing to ${primaryAgent}`,
|
|
97
|
+
coverageImpact: gaps.length > 0 ? 'high' : 'low',
|
|
98
|
+
},
|
|
99
|
+
suggestions,
|
|
100
|
+
metrics: {
|
|
101
|
+
filesAnalyzed: diskCoverage.summary.totalFiles,
|
|
102
|
+
totalGaps: scoredFiles.length,
|
|
103
|
+
criticalGaps,
|
|
104
|
+
avgCoverage: diskCoverage.summary.overallLineCoverage,
|
|
105
|
+
},
|
|
106
|
+
source: diskCoverage.source,
|
|
107
|
+
};
|
|
108
|
+
if (ctx.flags.format === 'json') {
|
|
109
|
+
output.printJson(result);
|
|
110
|
+
return { success: true, data: result };
|
|
111
|
+
}
|
|
112
|
+
output.writeln();
|
|
113
|
+
output.printBox([
|
|
114
|
+
`Agent: ${output.highlight(result.routing.primaryAgent)}`,
|
|
115
|
+
`Confidence: ${(result.routing.confidence * 100).toFixed(1)}%`,
|
|
116
|
+
`Coverage-Aware: ${output.success('Yes')} (from ${diskCoverage.source})`,
|
|
117
|
+
`Reason: ${result.routing.reason}`
|
|
118
|
+
].join('\n'), 'Coverage-Aware Routing');
|
|
119
|
+
if (gaps.length > 0) {
|
|
120
|
+
output.writeln();
|
|
121
|
+
output.writeln(output.bold('Priority Coverage Gaps'));
|
|
122
|
+
output.printTable({
|
|
123
|
+
columns: [
|
|
124
|
+
{ key: 'filePath', header: 'File', width: 35, format: (v) => {
|
|
125
|
+
const s = String(v);
|
|
126
|
+
return s.length > 32 ? '...' + s.slice(-32) : s;
|
|
127
|
+
} },
|
|
128
|
+
{ key: 'coveragePercent', header: 'Coverage', width: 10, align: 'right', format: (v) => `${Number(v).toFixed(1)}%` },
|
|
129
|
+
{ key: 'gapType', header: 'Type', width: 10 },
|
|
130
|
+
{ key: 'suggestedAgents', header: 'Agent', width: 15, format: (v) => Array.isArray(v) ? v[0] || '' : String(v) }
|
|
131
|
+
],
|
|
132
|
+
data: gaps.slice(0, 8)
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
if (result.metrics.filesAnalyzed > 0) {
|
|
136
|
+
output.writeln();
|
|
137
|
+
output.writeln(output.bold('Coverage Metrics'));
|
|
138
|
+
output.printList([
|
|
139
|
+
`Files Analyzed: ${result.metrics.filesAnalyzed}`,
|
|
140
|
+
`Total Gaps: ${result.metrics.totalGaps}`,
|
|
141
|
+
`Critical Gaps: ${result.metrics.criticalGaps}`,
|
|
142
|
+
`Average Coverage: ${result.metrics.avgCoverage.toFixed(1)}%`
|
|
143
|
+
]);
|
|
144
|
+
}
|
|
145
|
+
if (suggestions.length > 0) {
|
|
146
|
+
output.writeln();
|
|
147
|
+
output.writeln(output.bold('Suggestions'));
|
|
148
|
+
output.printList(suggestions.map(s => output.dim(s)));
|
|
149
|
+
}
|
|
150
|
+
return { success: true, data: result };
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
const result = await callMCPTool('hooks_coverage-route', { task, threshold, useMonovector });
|
|
154
|
+
spinner.stop();
|
|
155
|
+
if (ctx.flags.format === 'json') {
|
|
156
|
+
output.printJson(result);
|
|
157
|
+
return { success: true, data: result };
|
|
158
|
+
}
|
|
159
|
+
output.writeln();
|
|
160
|
+
output.printBox([
|
|
161
|
+
`Agent: ${output.highlight(result.routing.primaryAgent)}`,
|
|
162
|
+
`Confidence: ${(result.routing.confidence * 100).toFixed(1)}%`,
|
|
163
|
+
`Coverage-Aware: ${result.coverageAware ? output.success('Yes') : output.dim('No coverage data')}`,
|
|
164
|
+
`Reason: ${result.routing.reason}`
|
|
165
|
+
].join('\n'), 'Coverage-Aware Routing');
|
|
166
|
+
if (result.gaps.length > 0) {
|
|
167
|
+
output.writeln();
|
|
168
|
+
output.writeln(output.bold('Priority Coverage Gaps'));
|
|
169
|
+
output.printTable({
|
|
170
|
+
columns: [
|
|
171
|
+
{ key: 'filePath', header: 'File', width: 35, format: (v) => {
|
|
172
|
+
const s = String(v);
|
|
173
|
+
return s.length > 32 ? '...' + s.slice(-32) : s;
|
|
174
|
+
} },
|
|
175
|
+
{ key: 'coveragePercent', header: 'Coverage', width: 10, align: 'right', format: (v) => `${Number(v).toFixed(1)}%` },
|
|
176
|
+
{ key: 'gapType', header: 'Type', width: 10 },
|
|
177
|
+
{ key: 'suggestedAgents', header: 'Agent', width: 15, format: (v) => Array.isArray(v) ? v[0] || '' : String(v) }
|
|
178
|
+
],
|
|
179
|
+
data: result.gaps.slice(0, 8)
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
if (result.metrics.filesAnalyzed > 0) {
|
|
183
|
+
output.writeln();
|
|
184
|
+
output.writeln(output.bold('Coverage Metrics'));
|
|
185
|
+
output.printList([
|
|
186
|
+
`Files Analyzed: ${result.metrics.filesAnalyzed}`,
|
|
187
|
+
`Total Gaps: ${result.metrics.totalGaps}`,
|
|
188
|
+
`Critical Gaps: ${result.metrics.criticalGaps}`,
|
|
189
|
+
`Average Coverage: ${result.metrics.avgCoverage.toFixed(1)}%`
|
|
190
|
+
]);
|
|
191
|
+
}
|
|
192
|
+
if (result.suggestions.length > 0) {
|
|
193
|
+
output.writeln();
|
|
194
|
+
output.writeln(output.bold('Suggestions'));
|
|
195
|
+
output.printList(result.suggestions.map(s => output.dim(s)));
|
|
196
|
+
}
|
|
197
|
+
return { success: true, data: result };
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
spinner.fail('No coverage data found');
|
|
201
|
+
output.writeln();
|
|
202
|
+
output.printWarning('No coverage data found. Run your test suite with coverage first.');
|
|
203
|
+
output.writeln();
|
|
204
|
+
output.printList([
|
|
205
|
+
'Jest: npx jest --coverage',
|
|
206
|
+
'Vitest: npx vitest --coverage',
|
|
207
|
+
'nyc: npx nyc npm test',
|
|
208
|
+
'c8: npx c8 npm test',
|
|
209
|
+
]);
|
|
210
|
+
output.writeln();
|
|
211
|
+
output.writeln(output.dim('Expected files: coverage/coverage-summary.json, coverage/lcov.info, or .nyc_output/out.json'));
|
|
212
|
+
return { success: false, exitCode: 1 };
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
export const coverageSuggestCommand = {
|
|
217
|
+
name: 'coverage-suggest',
|
|
218
|
+
description: 'Suggest coverage improvements for a path (monovector integration)',
|
|
219
|
+
options: [
|
|
220
|
+
{
|
|
221
|
+
name: 'path',
|
|
222
|
+
short: 'p',
|
|
223
|
+
description: 'Path to analyze for coverage suggestions',
|
|
224
|
+
type: 'string',
|
|
225
|
+
required: true
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
name: 'threshold',
|
|
229
|
+
description: 'Coverage threshold percentage (default: 80)',
|
|
230
|
+
type: 'number',
|
|
231
|
+
default: 80
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: 'limit',
|
|
235
|
+
short: 'l',
|
|
236
|
+
description: 'Maximum number of suggestions (default: 20)',
|
|
237
|
+
type: 'number',
|
|
238
|
+
default: 20
|
|
239
|
+
}
|
|
240
|
+
],
|
|
241
|
+
examples: [
|
|
242
|
+
{ command: 'monomind hooks coverage-suggest -p src/', description: 'Suggest improvements for src/' },
|
|
243
|
+
{ command: 'monomind hooks coverage-suggest -p src/services --threshold 90', description: 'Stricter threshold' }
|
|
244
|
+
],
|
|
245
|
+
action: async (ctx) => {
|
|
246
|
+
const targetPath = ctx.args[0] || ctx.flags.path;
|
|
247
|
+
const threshold = ctx.flags.threshold || 80;
|
|
248
|
+
const limit = ctx.flags.limit || 20;
|
|
249
|
+
if (!targetPath) {
|
|
250
|
+
output.printError('Path is required. Use --path or -p flag.');
|
|
251
|
+
return { success: false, exitCode: 1 };
|
|
252
|
+
}
|
|
253
|
+
const spinner = output.createSpinner({ text: `Analyzing coverage for ${targetPath}...` });
|
|
254
|
+
spinner.start();
|
|
255
|
+
const diskCoverage = readCoverageFromDisk();
|
|
256
|
+
if (diskCoverage.found) {
|
|
257
|
+
spinner.succeed(`Coverage data loaded from ${diskCoverage.source}`);
|
|
258
|
+
const pathLower = targetPath.toLowerCase().replace(/\\/g, '/');
|
|
259
|
+
const matchingEntries = diskCoverage.entries.filter(e => {
|
|
260
|
+
const fileLower = e.filePath.toLowerCase().replace(/\\/g, '/');
|
|
261
|
+
return fileLower.includes(pathLower);
|
|
262
|
+
});
|
|
263
|
+
const belowThreshold = matchingEntries.filter(e => e.lines < threshold);
|
|
264
|
+
const suggestions = belowThreshold.slice(0, limit).map(e => {
|
|
265
|
+
const { gapType, priority } = classifyCoverageGap(e.lines, threshold);
|
|
266
|
+
return {
|
|
267
|
+
filePath: e.filePath,
|
|
268
|
+
coveragePercent: e.lines,
|
|
269
|
+
gapType,
|
|
270
|
+
priority,
|
|
271
|
+
suggestedAgents: suggestAgentsForFile(e.filePath),
|
|
272
|
+
reason: e.lines === 0 ? 'No coverage at all' :
|
|
273
|
+
e.lines < 20 ? 'Very low coverage, needs tests' :
|
|
274
|
+
e.lines < 50 ? 'Below 50%, add more tests' :
|
|
275
|
+
`Below ${threshold}% threshold`,
|
|
276
|
+
};
|
|
277
|
+
});
|
|
278
|
+
const totalLinesCov = matchingEntries.length > 0
|
|
279
|
+
? matchingEntries.reduce((acc, e) => acc + e.lines, 0) / matchingEntries.length
|
|
280
|
+
: 0;
|
|
281
|
+
const totalBranchesCov = matchingEntries.length > 0
|
|
282
|
+
? matchingEntries.reduce((acc, e) => acc + e.branches, 0) / matchingEntries.length
|
|
283
|
+
: 0;
|
|
284
|
+
const prioritizedFiles = belowThreshold.slice(0, 5).map(e => e.filePath);
|
|
285
|
+
const result = {
|
|
286
|
+
success: true,
|
|
287
|
+
path: targetPath,
|
|
288
|
+
suggestions,
|
|
289
|
+
summary: {
|
|
290
|
+
totalFiles: matchingEntries.length,
|
|
291
|
+
overallLineCoverage: totalLinesCov,
|
|
292
|
+
overallBranchCoverage: totalBranchesCov,
|
|
293
|
+
filesBelowThreshold: belowThreshold.length,
|
|
294
|
+
},
|
|
295
|
+
prioritizedFiles,
|
|
296
|
+
monovectorAvailable: false,
|
|
297
|
+
source: diskCoverage.source,
|
|
298
|
+
};
|
|
299
|
+
if (ctx.flags.format === 'json') {
|
|
300
|
+
output.printJson(result);
|
|
301
|
+
return { success: true, data: result };
|
|
302
|
+
}
|
|
303
|
+
output.writeln();
|
|
304
|
+
output.printBox([
|
|
305
|
+
`Path: ${output.highlight(targetPath)}`,
|
|
306
|
+
`Files Analyzed: ${result.summary.totalFiles}`,
|
|
307
|
+
`Line Coverage: ${result.summary.overallLineCoverage.toFixed(1)}%`,
|
|
308
|
+
`Branch Coverage: ${result.summary.overallBranchCoverage.toFixed(1)}%`,
|
|
309
|
+
`Below Threshold: ${result.summary.filesBelowThreshold} files`,
|
|
310
|
+
`Source: ${output.highlight(diskCoverage.source)}`
|
|
311
|
+
].join('\n'), 'Coverage Summary');
|
|
312
|
+
if (suggestions.length > 0) {
|
|
313
|
+
output.writeln();
|
|
314
|
+
output.writeln(output.bold('Coverage Improvement Suggestions'));
|
|
315
|
+
output.printTable({
|
|
316
|
+
columns: [
|
|
317
|
+
{ key: 'filePath', header: 'File', width: 40, format: (v) => {
|
|
318
|
+
const s = String(v);
|
|
319
|
+
return s.length > 37 ? '...' + s.slice(-37) : s;
|
|
320
|
+
} },
|
|
321
|
+
{ key: 'coveragePercent', header: 'Coverage', width: 10, align: 'right', format: (v) => `${Number(v).toFixed(1)}%` },
|
|
322
|
+
{ key: 'gapType', header: 'Priority', width: 10 },
|
|
323
|
+
{ key: 'reason', header: 'Reason', width: 25 }
|
|
324
|
+
],
|
|
325
|
+
data: suggestions.slice(0, 15)
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
output.writeln();
|
|
330
|
+
output.printSuccess('All files meet coverage threshold!');
|
|
331
|
+
}
|
|
332
|
+
if (prioritizedFiles.length > 0) {
|
|
333
|
+
output.writeln();
|
|
334
|
+
output.writeln(output.bold('Priority Files (Top 5)'));
|
|
335
|
+
output.printList(prioritizedFiles.slice(0, 5).map(f => output.highlight(f)));
|
|
336
|
+
}
|
|
337
|
+
return { success: true, data: result };
|
|
338
|
+
}
|
|
339
|
+
try {
|
|
340
|
+
const result = await callMCPTool('hooks_coverage-suggest', { path: targetPath, threshold, limit });
|
|
341
|
+
spinner.stop();
|
|
342
|
+
if (ctx.flags.format === 'json') {
|
|
343
|
+
output.printJson(result);
|
|
344
|
+
return { success: true, data: result };
|
|
345
|
+
}
|
|
346
|
+
output.writeln();
|
|
347
|
+
output.printBox([
|
|
348
|
+
`Path: ${output.highlight(result.path)}`,
|
|
349
|
+
`Files Analyzed: ${result.summary.totalFiles}`,
|
|
350
|
+
`Line Coverage: ${result.summary.overallLineCoverage.toFixed(1)}%`,
|
|
351
|
+
`Branch Coverage: ${result.summary.overallBranchCoverage.toFixed(1)}%`,
|
|
352
|
+
`Below Threshold: ${result.summary.filesBelowThreshold} files`,
|
|
353
|
+
`Keyword routing: ${result.monovectorAvailable ? output.success('Available') : output.dim('Unavailable')}`
|
|
354
|
+
].join('\n'), 'Coverage Summary');
|
|
355
|
+
if (result.suggestions.length > 0) {
|
|
356
|
+
output.writeln();
|
|
357
|
+
output.writeln(output.bold('Coverage Improvement Suggestions'));
|
|
358
|
+
output.printTable({
|
|
359
|
+
columns: [
|
|
360
|
+
{ key: 'filePath', header: 'File', width: 40, format: (v) => {
|
|
361
|
+
const s = String(v);
|
|
362
|
+
return s.length > 37 ? '...' + s.slice(-37) : s;
|
|
363
|
+
} },
|
|
364
|
+
{ key: 'coveragePercent', header: 'Coverage', width: 10, align: 'right', format: (v) => `${Number(v).toFixed(1)}%` },
|
|
365
|
+
{ key: 'gapType', header: 'Priority', width: 10 },
|
|
366
|
+
{ key: 'reason', header: 'Reason', width: 25 }
|
|
367
|
+
],
|
|
368
|
+
data: result.suggestions.slice(0, 15)
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
output.writeln();
|
|
373
|
+
output.printSuccess('All files meet coverage threshold!');
|
|
374
|
+
}
|
|
375
|
+
if (result.prioritizedFiles.length > 0) {
|
|
376
|
+
output.writeln();
|
|
377
|
+
output.writeln(output.bold('Priority Files (Top 5)'));
|
|
378
|
+
output.printList(result.prioritizedFiles.slice(0, 5).map(f => output.highlight(f)));
|
|
379
|
+
}
|
|
380
|
+
return { success: true, data: result };
|
|
381
|
+
}
|
|
382
|
+
catch {
|
|
383
|
+
spinner.fail('No coverage data found');
|
|
384
|
+
output.writeln();
|
|
385
|
+
output.printWarning('No coverage data found. Run your test suite with coverage first.');
|
|
386
|
+
output.writeln();
|
|
387
|
+
output.printList([
|
|
388
|
+
'Jest: npx jest --coverage',
|
|
389
|
+
'Vitest: npx vitest --coverage',
|
|
390
|
+
'nyc: npx nyc npm test',
|
|
391
|
+
'c8: npx c8 npm test',
|
|
392
|
+
]);
|
|
393
|
+
output.writeln();
|
|
394
|
+
output.writeln(output.dim('Expected files: coverage/coverage-summary.json, coverage/lcov.info, or .nyc_output/out.json'));
|
|
395
|
+
return { success: false, exitCode: 1 };
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
//# sourceMappingURL=hooks-coverage-routing.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Init subcommands: check, skills, hooks
|
|
3
|
+
*/
|
|
4
|
+
import type { Command } from '../types.js';
|
|
5
|
+
export declare const checkCommand: Command;
|
|
6
|
+
export declare const skillsCommand: Command;
|
|
7
|
+
export declare const hooksCommand: Command;
|
|
8
|
+
//# sourceMappingURL=init-subcommands.d.ts.map
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Init subcommands: check, skills, hooks
|
|
3
|
+
*/
|
|
4
|
+
import { output } from '../output.js';
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { executeInit, DEFAULT_INIT_OPTIONS, MINIMAL_INIT_OPTIONS, } from '../init/index.js';
|
|
8
|
+
function isInitialized(cwd) {
|
|
9
|
+
const claudePath = path.join(cwd, '.claude', 'settings.json');
|
|
10
|
+
const monomindPath = path.join(cwd, '.monomind', 'config.yaml');
|
|
11
|
+
return {
|
|
12
|
+
claude: fs.existsSync(claudePath),
|
|
13
|
+
monomind: fs.existsSync(monomindPath),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export const checkCommand = {
|
|
17
|
+
name: 'check',
|
|
18
|
+
description: 'Check if MonoMind is initialized',
|
|
19
|
+
action: async (ctx) => {
|
|
20
|
+
const initialized = isInitialized(ctx.cwd);
|
|
21
|
+
const result = {
|
|
22
|
+
initialized: initialized.claude || initialized.monomind,
|
|
23
|
+
claude: initialized.claude,
|
|
24
|
+
monomind: initialized.monomind,
|
|
25
|
+
paths: {
|
|
26
|
+
claudeSettings: initialized.claude ? path.join(ctx.cwd, '.claude', 'settings.json') : null,
|
|
27
|
+
monomindConfig: initialized.monomind ? path.join(ctx.cwd, '.monomind', 'config.yaml') : null,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
if (ctx.flags.format === 'json') {
|
|
31
|
+
output.printJson(result);
|
|
32
|
+
return { success: true, data: result };
|
|
33
|
+
}
|
|
34
|
+
if (result.initialized) {
|
|
35
|
+
output.printSuccess('MonoMind is initialized');
|
|
36
|
+
if (initialized.claude) {
|
|
37
|
+
output.printInfo(` Claude Code: .claude/settings.json`);
|
|
38
|
+
}
|
|
39
|
+
if (initialized.monomind) {
|
|
40
|
+
output.printInfo(` Runtime: .monomind/config.yaml`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
output.printWarning('MonoMind is not initialized in this directory');
|
|
45
|
+
output.printInfo('Run "monomind init" to initialize');
|
|
46
|
+
}
|
|
47
|
+
return { success: true, data: result };
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
export const skillsCommand = {
|
|
51
|
+
name: 'skills',
|
|
52
|
+
description: 'Initialize only skills',
|
|
53
|
+
options: [
|
|
54
|
+
{ name: 'all', description: 'Install all skills', type: 'boolean', default: false },
|
|
55
|
+
{ name: 'core', description: 'Install core skills', type: 'boolean', default: true },
|
|
56
|
+
{ name: 'memory', description: 'Install memory skills', type: 'boolean', default: false },
|
|
57
|
+
{ name: 'github', description: 'Install GitHub skills', type: 'boolean', default: false },
|
|
58
|
+
],
|
|
59
|
+
action: async (ctx) => {
|
|
60
|
+
const options = {
|
|
61
|
+
...MINIMAL_INIT_OPTIONS,
|
|
62
|
+
targetDir: ctx.cwd,
|
|
63
|
+
force: ctx.flags.force,
|
|
64
|
+
components: {
|
|
65
|
+
settings: false,
|
|
66
|
+
skills: true,
|
|
67
|
+
commands: false,
|
|
68
|
+
agents: false,
|
|
69
|
+
helpers: false,
|
|
70
|
+
statusline: false,
|
|
71
|
+
mcp: false,
|
|
72
|
+
runtime: false,
|
|
73
|
+
claudeMd: false,
|
|
74
|
+
graphify: false,
|
|
75
|
+
},
|
|
76
|
+
skills: {
|
|
77
|
+
all: ctx.flags.all,
|
|
78
|
+
core: ctx.flags.core,
|
|
79
|
+
memory: ctx.flags.memory,
|
|
80
|
+
github: ctx.flags.github,
|
|
81
|
+
browser: false,
|
|
82
|
+
advanced: false,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
const spinner = output.createSpinner({ text: 'Installing skills...' });
|
|
86
|
+
spinner.start();
|
|
87
|
+
const result = await executeInit(options);
|
|
88
|
+
if (result.success) {
|
|
89
|
+
spinner.succeed(`Installed ${result.summary.skillsCount} skills`);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
spinner.fail('Failed to install skills');
|
|
93
|
+
for (const error of result.errors) {
|
|
94
|
+
output.printError(error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return { success: result.success, data: result };
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
export const hooksCommand = {
|
|
101
|
+
name: 'hooks',
|
|
102
|
+
description: 'Initialize only hooks configuration',
|
|
103
|
+
options: [
|
|
104
|
+
{ name: 'all', description: 'Enable all hooks', type: 'boolean', default: true },
|
|
105
|
+
{ name: 'minimal', description: 'Enable only essential hooks', type: 'boolean', default: false },
|
|
106
|
+
],
|
|
107
|
+
action: async (ctx) => {
|
|
108
|
+
const minimal = ctx.flags.minimal;
|
|
109
|
+
const options = {
|
|
110
|
+
...DEFAULT_INIT_OPTIONS,
|
|
111
|
+
targetDir: ctx.cwd,
|
|
112
|
+
force: ctx.flags.force,
|
|
113
|
+
components: {
|
|
114
|
+
settings: true,
|
|
115
|
+
skills: false,
|
|
116
|
+
commands: false,
|
|
117
|
+
agents: false,
|
|
118
|
+
helpers: false,
|
|
119
|
+
statusline: false,
|
|
120
|
+
mcp: false,
|
|
121
|
+
runtime: false,
|
|
122
|
+
claudeMd: false,
|
|
123
|
+
graphify: false,
|
|
124
|
+
},
|
|
125
|
+
hooks: minimal
|
|
126
|
+
? {
|
|
127
|
+
preToolUse: true,
|
|
128
|
+
postToolUse: true,
|
|
129
|
+
userPromptSubmit: false,
|
|
130
|
+
sessionStart: false,
|
|
131
|
+
stop: false,
|
|
132
|
+
preCompact: false,
|
|
133
|
+
notification: false,
|
|
134
|
+
teammateIdle: false,
|
|
135
|
+
taskCompleted: false,
|
|
136
|
+
timeout: 5000,
|
|
137
|
+
continueOnError: true,
|
|
138
|
+
}
|
|
139
|
+
: DEFAULT_INIT_OPTIONS.hooks,
|
|
140
|
+
};
|
|
141
|
+
const spinner = output.createSpinner({ text: 'Creating hooks configuration...' });
|
|
142
|
+
spinner.start();
|
|
143
|
+
const result = await executeInit(options);
|
|
144
|
+
if (result.success) {
|
|
145
|
+
spinner.succeed(`Created settings.json with ${result.summary.hooksEnabled} hooks enabled`);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
spinner.fail('Failed to create hooks configuration');
|
|
149
|
+
for (const error of result.errors) {
|
|
150
|
+
output.printError(error);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return { success: result.success, data: result };
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
//# sourceMappingURL=init-subcommands.js.map
|