@monoes/monomindcli 1.14.7 → 1.15.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/agents/reengineer-squad/boss.md +113 -0
- package/.claude/agents/reengineer-squad/critic-architect.md +132 -0
- package/.claude/agents/reengineer-squad/git-manager.md +145 -0
- package/.claude/agents/reengineer-squad/idea-generator.md +95 -0
- package/.claude/agents/reengineer-squad/implementer.md +112 -0
- package/.claude/agents/reengineer-squad/integration-planner.md +112 -0
- package/.claude/agents/reengineer-squad/source-analyst.md +103 -0
- package/.claude/agents/reengineer-squad/target-analyst.md +118 -0
- package/.claude/agents/reengineer-squad/tester.md +105 -0
- package/.claude/commands/mastermind/master.md +35 -14
- package/.claude/helpers/handlers/capture-handler.cjs +155 -18
- package/.claude/helpers/monolean-activate.cjs +20 -0
- package/.claude/helpers/monolean-config.cjs +76 -0
- package/.claude/helpers/monolean-instructions.cjs +109 -0
- package/.claude/helpers/monolean-propagate.cjs +9 -0
- package/.claude/helpers/monolean-tracker.cjs +18 -0
- package/.claude/helpers/skill-registry.json +2 -2
- package/.claude/skills/agent-browser-testing/SKILL.md +301 -18
- package/.claude/skills/mastermind/runorg.md +69 -23
- package/.claude/skills/monodesign/SKILL.md +32 -1
- package/.claude/skills/monodesign/adapt.md +53 -0
- package/.claude/skills/monodesign/agents/monodesign-asset-producer.md +100 -0
- package/.claude/skills/monodesign/animate.md +65 -0
- package/.claude/skills/monodesign/audit.md +89 -0
- package/.claude/skills/monodesign/bolder.md +50 -0
- package/.claude/skills/monodesign/clarify.md +64 -0
- package/.claude/skills/monodesign/colorize.md +68 -0
- package/.claude/skills/monodesign/craft.md +51 -0
- package/.claude/skills/monodesign/critique.md +66 -0
- package/.claude/skills/monodesign/delight.md +47 -0
- package/.claude/skills/monodesign/distill.md +56 -0
- package/.claude/skills/monodesign/document.md +80 -0
- package/.claude/skills/monodesign/extract.md +74 -0
- package/.claude/skills/monodesign/harden.md +65 -0
- package/.claude/skills/monodesign/live.md +59 -0
- package/.claude/skills/monodesign/onboard.md +50 -0
- package/.claude/skills/monodesign/optimize.md +64 -0
- package/.claude/skills/monodesign/overdrive.md +56 -0
- package/.claude/skills/monodesign/polish.md +68 -0
- package/.claude/skills/monodesign/quieter.md +57 -0
- package/.claude/skills/monodesign/reference/antipatterns-catalog.md +248 -76
- package/.claude/skills/monodesign/reference/codex.md +107 -0
- package/.claude/skills/monodesign/reference/craft.md +3 -0
- package/.claude/skills/monodesign/reference/hooks.md +99 -0
- package/.claude/skills/monodesign/reference/image-prompts.md +12 -0
- package/.claude/skills/monodesign/shape.md +71 -0
- package/.claude/skills/monodesign/teach.md +69 -0
- package/.claude/skills/monodesign/typeset.md +59 -0
- package/.claude/skills/monolean/SKILL.md +118 -0
- package/.claude/skills/monolean-audit/SKILL.md +41 -0
- package/.claude/skills/monolean-debt/SKILL.md +46 -0
- package/.claude/skills/monolean-help/SKILL.md +60 -0
- package/.claude/skills/monolean-review/SKILL.md +57 -0
- package/bin/cli.js +3 -1
- package/dist/dashboard/server.js +137 -0
- package/dist/src/__tests__/browse-adapters.test.d.ts +2 -0
- package/dist/src/__tests__/browse-adapters.test.d.ts.map +1 -0
- package/dist/src/__tests__/browse-adapters.test.js +51 -0
- package/dist/src/__tests__/browse-adapters.test.js.map +1 -0
- package/dist/src/__tests__/browse-analyzer.test.d.ts +2 -0
- package/dist/src/__tests__/browse-analyzer.test.d.ts.map +1 -0
- package/dist/src/__tests__/browse-analyzer.test.js +68 -0
- package/dist/src/__tests__/browse-analyzer.test.js.map +1 -0
- package/dist/src/__tests__/browse-builtin-handlers.test.d.ts +2 -0
- package/dist/src/__tests__/browse-builtin-handlers.test.d.ts.map +1 -0
- package/dist/src/__tests__/browse-builtin-handlers.test.js +139 -0
- package/dist/src/__tests__/browse-builtin-handlers.test.js.map +1 -0
- package/dist/src/__tests__/browse-cdp.test.d.ts +2 -0
- package/dist/src/__tests__/browse-cdp.test.d.ts.map +1 -0
- package/dist/src/__tests__/browse-cdp.test.js +169 -0
- package/dist/src/__tests__/browse-cdp.test.js.map +1 -0
- package/dist/src/__tests__/browse-dashboard.test.d.ts +2 -0
- package/dist/src/__tests__/browse-dashboard.test.d.ts.map +1 -0
- package/dist/src/__tests__/browse-dashboard.test.js +179 -0
- package/dist/src/__tests__/browse-dashboard.test.js.map +1 -0
- package/dist/src/__tests__/browse-engine.test.d.ts +2 -0
- package/dist/src/__tests__/browse-engine.test.d.ts.map +1 -0
- package/dist/src/__tests__/browse-engine.test.js +122 -0
- package/dist/src/__tests__/browse-engine.test.js.map +1 -0
- package/dist/src/__tests__/browse-expression.test.d.ts +2 -0
- package/dist/src/__tests__/browse-expression.test.d.ts.map +1 -0
- package/dist/src/__tests__/browse-expression.test.js +54 -0
- package/dist/src/__tests__/browse-expression.test.js.map +1 -0
- package/dist/src/__tests__/browse-store.test.d.ts +2 -0
- package/dist/src/__tests__/browse-store.test.d.ts.map +1 -0
- package/dist/src/__tests__/browse-store.test.js +99 -0
- package/dist/src/__tests__/browse-store.test.js.map +1 -0
- package/dist/src/__tests__/browse-workflow-types.test.d.ts +2 -0
- package/dist/src/__tests__/browse-workflow-types.test.d.ts.map +1 -0
- package/dist/src/__tests__/browse-workflow-types.test.js +33 -0
- package/dist/src/__tests__/browse-workflow-types.test.js.map +1 -0
- package/dist/src/browser/action-builder/analyzer.d.ts +11 -0
- package/dist/src/browser/action-builder/analyzer.d.ts.map +1 -0
- package/dist/src/browser/action-builder/analyzer.js +71 -0
- package/dist/src/browser/action-builder/analyzer.js.map +1 -0
- package/dist/src/browser/action-builder/types.d.ts +47 -0
- package/dist/src/browser/action-builder/types.d.ts.map +1 -0
- package/dist/src/browser/action-builder/types.js +2 -0
- package/dist/src/browser/action-builder/types.js.map +1 -0
- package/dist/src/browser/adapters/gemini.d.ts +3 -0
- package/dist/src/browser/adapters/gemini.d.ts.map +1 -0
- package/dist/src/browser/adapters/gemini.js +16 -0
- package/dist/src/browser/adapters/gemini.js.map +1 -0
- package/dist/src/browser/adapters/google.d.ts +3 -0
- package/dist/src/browser/adapters/google.d.ts.map +1 -0
- package/dist/src/browser/adapters/google.js +17 -0
- package/dist/src/browser/adapters/google.js.map +1 -0
- package/dist/src/browser/adapters/index.d.ts +19 -0
- package/dist/src/browser/adapters/index.d.ts.map +1 -0
- package/dist/src/browser/adapters/index.js +23 -0
- package/dist/src/browser/adapters/index.js.map +1 -0
- package/dist/src/browser/adapters/instagram.d.ts +3 -0
- package/dist/src/browser/adapters/instagram.d.ts.map +1 -0
- package/dist/src/browser/adapters/instagram.js +17 -0
- package/dist/src/browser/adapters/instagram.js.map +1 -0
- package/dist/src/browser/adapters/linkedin.d.ts +3 -0
- package/dist/src/browser/adapters/linkedin.d.ts.map +1 -0
- package/dist/src/browser/adapters/linkedin.js +19 -0
- package/dist/src/browser/adapters/linkedin.js.map +1 -0
- package/dist/src/browser/adapters/microsoft.d.ts +3 -0
- package/dist/src/browser/adapters/microsoft.d.ts.map +1 -0
- package/dist/src/browser/adapters/microsoft.js +16 -0
- package/dist/src/browser/adapters/microsoft.js.map +1 -0
- package/dist/src/browser/adapters/x.d.ts +3 -0
- package/dist/src/browser/adapters/x.d.ts.map +1 -0
- package/dist/src/browser/adapters/x.js +19 -0
- package/dist/src/browser/adapters/x.js.map +1 -0
- package/dist/src/browser/dashboard/api-types.d.ts +50 -0
- package/dist/src/browser/dashboard/api-types.d.ts.map +1 -0
- package/dist/src/browser/dashboard/api-types.js +14 -0
- package/dist/src/browser/dashboard/api-types.js.map +1 -0
- package/dist/src/browser/dashboard/server.d.ts +9 -0
- package/dist/src/browser/dashboard/server.d.ts.map +1 -0
- package/dist/src/browser/dashboard/server.js +62 -0
- package/dist/src/browser/dashboard/server.js.map +1 -0
- package/dist/src/browser/dashboard/ui.html +1811 -0
- package/dist/src/browser/workflow/builtin-handlers.d.ts +3 -0
- package/dist/src/browser/workflow/builtin-handlers.d.ts.map +1 -0
- package/dist/src/browser/workflow/builtin-handlers.js +343 -0
- package/dist/src/browser/workflow/builtin-handlers.js.map +1 -0
- package/dist/src/browser/workflow/engine.d.ts +15 -0
- package/dist/src/browser/workflow/engine.d.ts.map +1 -0
- package/dist/src/browser/workflow/engine.js +127 -0
- package/dist/src/browser/workflow/engine.js.map +1 -0
- package/dist/src/browser/workflow/expression.d.ts +4 -0
- package/dist/src/browser/workflow/expression.d.ts.map +1 -0
- package/dist/src/browser/workflow/expression.js +64 -0
- package/dist/src/browser/workflow/expression.js.map +1 -0
- package/dist/src/browser/workflow/store.d.ts +24 -0
- package/dist/src/browser/workflow/store.d.ts.map +1 -0
- package/dist/src/browser/workflow/store.js +145 -0
- package/dist/src/browser/workflow/store.js.map +1 -0
- package/dist/src/browser/workflow/types.d.ts +48 -0
- package/dist/src/browser/workflow/types.d.ts.map +1 -0
- package/dist/src/browser/workflow/types.js +2 -0
- package/dist/src/browser/workflow/types.js.map +1 -0
- package/dist/src/commands/browse-action.d.ts +4 -0
- package/dist/src/commands/browse-action.d.ts.map +1 -0
- package/dist/src/commands/browse-action.js +151 -0
- package/dist/src/commands/browse-action.js.map +1 -0
- package/dist/src/commands/browse-platform.d.ts +4 -0
- package/dist/src/commands/browse-platform.d.ts.map +1 -0
- package/dist/src/commands/browse-platform.js +117 -0
- package/dist/src/commands/browse-platform.js.map +1 -0
- package/dist/src/commands/browse-workflow.d.ts +4 -0
- package/dist/src/commands/browse-workflow.d.ts.map +1 -0
- package/dist/src/commands/browse-workflow.js +153 -0
- package/dist/src/commands/browse-workflow.js.map +1 -0
- package/dist/src/commands/browse.d.ts +10 -6
- package/dist/src/commands/browse.d.ts.map +1 -1
- package/dist/src/commands/browse.js +11 -2154
- package/dist/src/commands/browse.js.map +1 -1
- package/dist/src/commands/design-detect.d.ts +21 -0
- package/dist/src/commands/design-detect.d.ts.map +1 -0
- package/dist/src/commands/design-detect.js +127 -0
- package/dist/src/commands/design-detect.js.map +1 -0
- package/dist/src/commands/design-palette.d.ts +22 -0
- package/dist/src/commands/design-palette.d.ts.map +1 -0
- package/dist/src/commands/design-palette.js +539 -0
- package/dist/src/commands/design-palette.js.map +1 -0
- package/dist/src/commands/hooks-core-commands.d.ts +10 -0
- package/dist/src/commands/hooks-core-commands.d.ts.map +1 -0
- package/dist/src/commands/hooks-core-commands.js +377 -0
- package/dist/src/commands/hooks-core-commands.js.map +1 -0
- package/dist/src/commands/hooks-coverage-commands.d.ts +12 -0
- package/dist/src/commands/hooks-coverage-commands.d.ts.map +1 -0
- package/dist/src/commands/hooks-coverage-commands.js +1217 -0
- package/dist/src/commands/hooks-coverage-commands.js.map +1 -0
- package/dist/src/commands/hooks-coverage-utils.d.ts +42 -0
- package/dist/src/commands/hooks-coverage-utils.d.ts.map +1 -0
- package/dist/src/commands/hooks-coverage-utils.js +220 -0
- package/dist/src/commands/hooks-coverage-utils.js.map +1 -0
- package/dist/src/commands/hooks-extended-commands.d.ts +14 -0
- package/dist/src/commands/hooks-extended-commands.d.ts.map +1 -0
- package/dist/src/commands/hooks-extended-commands.js +579 -0
- package/dist/src/commands/hooks-extended-commands.js.map +1 -0
- package/dist/src/commands/hooks-formatting.d.ts +13 -0
- package/dist/src/commands/hooks-formatting.d.ts.map +1 -0
- package/dist/src/commands/hooks-formatting.js +42 -0
- package/dist/src/commands/hooks-formatting.js.map +1 -0
- package/dist/src/commands/hooks-routing-commands.d.ts +15 -0
- package/dist/src/commands/hooks-routing-commands.d.ts.map +1 -0
- package/dist/src/commands/hooks-routing-commands.js +723 -0
- package/dist/src/commands/hooks-routing-commands.js.map +1 -0
- package/dist/src/commands/hooks-workers.d.ts +9 -0
- package/dist/src/commands/hooks-workers.d.ts.map +1 -0
- package/dist/src/commands/hooks-workers.js +782 -0
- package/dist/src/commands/hooks-workers.js.map +1 -0
- package/dist/src/commands/hooks.d.ts +8 -0
- package/dist/src/commands/hooks.d.ts.map +1 -1
- package/dist/src/commands/hooks.js +179 -4103
- package/dist/src/commands/hooks.js.map +1 -1
- package/dist/src/commands/index.d.ts +1 -0
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +6 -0
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/org.d.ts.map +1 -1
- package/dist/src/commands/org.js +14 -15
- package/dist/src/commands/org.js.map +1 -1
- package/dist/src/commands/tokens.d.ts.map +1 -1
- package/dist/src/commands/tokens.js +77 -1
- package/dist/src/commands/tokens.js.map +1 -1
- package/dist/src/init/executor.d.ts.map +1 -1
- package/dist/src/init/executor.js +18 -8
- package/dist/src/init/executor.js.map +1 -1
- package/dist/src/init/settings-generator.d.ts.map +1 -1
- package/dist/src/init/settings-generator.js +39 -5
- package/dist/src/init/settings-generator.js.map +1 -1
- package/dist/src/init/statusline-generator.d.ts.map +1 -1
- package/dist/src/init/statusline-generator.js +25 -5
- package/dist/src/init/statusline-generator.js.map +1 -1
- package/dist/src/mcp-tools/browser-tools.d.ts +3 -5
- package/dist/src/mcp-tools/browser-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/browser-tools.js +619 -326
- package/dist/src/mcp-tools/browser-tools.js.map +1 -1
- package/dist/src/mcp-tools/hooks-embedding.d.ts +161 -0
- package/dist/src/mcp-tools/hooks-embedding.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-embedding.js +506 -0
- package/dist/src/mcp-tools/hooks-embedding.js.map +1 -0
- package/dist/src/mcp-tools/hooks-intelligence.d.ts +26 -0
- package/dist/src/mcp-tools/hooks-intelligence.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-intelligence.js +1328 -0
- package/dist/src/mcp-tools/hooks-intelligence.js.map +1 -0
- package/dist/src/mcp-tools/hooks-routing.d.ts +27 -0
- package/dist/src/mcp-tools/hooks-routing.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-routing.js +1591 -0
- package/dist/src/mcp-tools/hooks-routing.js.map +1 -0
- package/dist/src/mcp-tools/hooks-tools.d.ts +3 -38
- package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-tools.js +5 -3393
- package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
- package/dist/src/mcp-tools/monograph-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/monograph-tools.js +24 -14
- package/dist/src/mcp-tools/monograph-tools.js.map +1 -1
- package/dist/src/mcp-tools/workflow-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/workflow-tools.js +54 -1
- package/dist/src/mcp-tools/workflow-tools.js.map +1 -1
- package/dist/src/memory/embedding-operations.d.ts +58 -0
- package/dist/src/memory/embedding-operations.d.ts.map +1 -0
- package/dist/src/memory/embedding-operations.js +299 -0
- package/dist/src/memory/embedding-operations.js.map +1 -0
- package/dist/src/memory/ewc-consolidation.d.ts.map +1 -1
- package/dist/src/memory/ewc-consolidation.js +37 -3
- package/dist/src/memory/ewc-consolidation.js.map +1 -1
- package/dist/src/memory/hnsw-operations.d.ts +130 -0
- package/dist/src/memory/hnsw-operations.d.ts.map +1 -0
- package/dist/src/memory/hnsw-operations.js +400 -0
- package/dist/src/memory/hnsw-operations.js.map +1 -0
- package/dist/src/memory/intelligence.d.ts.map +1 -1
- package/dist/src/memory/intelligence.js +42 -23
- package/dist/src/memory/intelligence.js.map +1 -1
- package/dist/src/memory/memory-bridge.d.ts.map +1 -1
- package/dist/src/memory/memory-bridge.js +52 -8
- package/dist/src/memory/memory-bridge.js.map +1 -1
- package/dist/src/memory/memory-crud.d.ts +67 -0
- package/dist/src/memory/memory-crud.d.ts.map +1 -0
- package/dist/src/memory/memory-crud.js +415 -0
- package/dist/src/memory/memory-crud.js.map +1 -0
- package/dist/src/memory/memory-initializer.d.ts +9 -322
- package/dist/src/memory/memory-initializer.d.ts.map +1 -1
- package/dist/src/memory/memory-initializer.js +17 -1794
- package/dist/src/memory/memory-initializer.js.map +1 -1
- package/dist/src/memory/memory-migrations.d.ts +30 -0
- package/dist/src/memory/memory-migrations.d.ts.map +1 -0
- package/dist/src/memory/memory-migrations.js +134 -0
- package/dist/src/memory/memory-migrations.js.map +1 -0
- package/dist/src/memory/memory-read.d.ts +78 -0
- package/dist/src/memory/memory-read.d.ts.map +1 -0
- package/dist/src/memory/memory-read.js +331 -0
- package/dist/src/memory/memory-read.js.map +1 -0
- package/dist/src/memory/memory-schema.d.ts +13 -0
- package/dist/src/memory/memory-schema.d.ts.map +1 -0
- package/dist/src/memory/memory-schema.js +167 -0
- package/dist/src/memory/memory-schema.js.map +1 -0
- package/dist/src/memory/sona-optimizer.d.ts.map +1 -1
- package/dist/src/memory/sona-optimizer.js +37 -4
- package/dist/src/memory/sona-optimizer.js.map +1 -1
- package/dist/src/monovector/route-outcomes.d.ts.map +1 -1
- package/dist/src/monovector/route-outcomes.js +16 -6
- package/dist/src/monovector/route-outcomes.js.map +1 -1
- package/dist/src/pricing/model-pricing.d.ts +41 -0
- package/dist/src/pricing/model-pricing.d.ts.map +1 -0
- package/dist/src/pricing/model-pricing.js +61 -0
- package/dist/src/pricing/model-pricing.js.map +1 -0
- package/dist/src/ui/.monomind/capture/active-run.json +1 -0
- package/dist/src/ui/.monomind/orgs/system-trial-qa/runs/real-events-1782290897.convs.jsonl +3 -0
- package/dist/src/ui/.monomind/orgs/system-trial-qa/runs/real-events-1782290897.jsonl +11 -0
- package/dist/src/ui/.monomind/orgs/system-trial-qa/runs/rigid-qa-restart-1782288201.jsonl +540 -0
- package/dist/src/ui/.monomind/orgs/system-trial-qa-threads.jsonl +3 -0
- package/dist/src/ui/.monomind/orgs/test-event-fix/runs/rigid-qa-restart-1782288201.jsonl +2 -0
- package/dist/src/ui/MODULARIZATION_PLAN.md +79 -0
- package/dist/src/ui/collector.mjs +23 -13
- package/dist/src/ui/dashboard.html +1652 -13
- package/dist/src/ui/data/known-projects.json +1 -0
- package/dist/src/ui/data/mastermind-events.jsonl +553 -0
- package/dist/src/ui/data/sessions/_index.json +1 -0
- package/dist/src/ui/data/sessions/final-sess-001.jsonl +542 -0
- package/dist/src/ui/data/unknown-events.jsonl +1 -0
- package/dist/src/ui/orgs.html +154 -10
- package/dist/src/ui/server.mjs +1131 -168
- package/dist/src/ui/sse-manager.mjs +119 -0
- package/dist/src/update/checker.js +1 -1
- package/dist/src/update/checker.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/workflow/builtin-handlers.js +321 -0
- package/dist/workflow/engine.js +253 -0
- package/dist/workflow/expression.js +98 -0
- package/dist/workflow/types.js +2 -0
- package/package.json +8 -5
|
@@ -0,0 +1,1217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks Coverage Commands
|
|
3
|
+
* Coverage-aware routing, progress tracking, and statusline generation.
|
|
4
|
+
* Extracted from hooks.ts to reduce file size.
|
|
5
|
+
*/
|
|
6
|
+
import { output } from '../output.js';
|
|
7
|
+
import { callMCPTool, MCPClientError } from '../mcp-client.js';
|
|
8
|
+
import { readCoverageFromDisk, classifyCoverageGap, suggestAgentsForFile, } from './hooks-coverage-utils.js';
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Coverage-Aware Routing Commands
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Coverage route subcommand
|
|
13
|
+
export const coverageRouteCommand = {
|
|
14
|
+
name: 'coverage-route',
|
|
15
|
+
description: 'Route task to agents based on test coverage gaps (monovector integration)',
|
|
16
|
+
options: [
|
|
17
|
+
{
|
|
18
|
+
name: 'task',
|
|
19
|
+
short: 't',
|
|
20
|
+
description: 'Task description to route',
|
|
21
|
+
type: 'string',
|
|
22
|
+
required: true
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'threshold',
|
|
26
|
+
description: 'Coverage threshold percentage (default: 80)',
|
|
27
|
+
type: 'number',
|
|
28
|
+
default: 80
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'no-monovector',
|
|
32
|
+
description: 'Disable monovector integration',
|
|
33
|
+
type: 'boolean',
|
|
34
|
+
default: false
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
examples: [
|
|
38
|
+
{ command: 'monomind hooks coverage-route -t "fix bug in auth"', description: 'Route with coverage awareness' },
|
|
39
|
+
{ command: 'monomind hooks coverage-route -t "add tests" --threshold 90', description: 'Route with custom threshold' }
|
|
40
|
+
],
|
|
41
|
+
action: async (ctx) => {
|
|
42
|
+
const task = ctx.args[0] || ctx.flags.task;
|
|
43
|
+
const threshold = ctx.flags.threshold || 80;
|
|
44
|
+
const useMonovector = !ctx.flags['no-monovector'];
|
|
45
|
+
if (!task) {
|
|
46
|
+
output.printError('Task description is required. Use --task or -t flag.');
|
|
47
|
+
return { success: false, exitCode: 1 };
|
|
48
|
+
}
|
|
49
|
+
const spinner = output.createSpinner({ text: 'Analyzing coverage and routing task...' });
|
|
50
|
+
spinner.start();
|
|
51
|
+
// Try reading coverage from disk first
|
|
52
|
+
const diskCoverage = readCoverageFromDisk();
|
|
53
|
+
if (diskCoverage.found) {
|
|
54
|
+
spinner.succeed(`Coverage data loaded from ${diskCoverage.source}`);
|
|
55
|
+
// Find files with lowest coverage that may relate to the task
|
|
56
|
+
const taskLower = task.toLowerCase();
|
|
57
|
+
const taskWords = taskLower.split(/\s+/).filter(w => w.length > 2);
|
|
58
|
+
// Score each file by relevance to the task and how low its coverage is
|
|
59
|
+
const scoredFiles = diskCoverage.entries
|
|
60
|
+
.filter(e => e.lines < threshold)
|
|
61
|
+
.map(e => {
|
|
62
|
+
const fileNameLower = e.filePath.toLowerCase();
|
|
63
|
+
let relevance = 0;
|
|
64
|
+
for (const word of taskWords) {
|
|
65
|
+
if (fileNameLower.includes(word))
|
|
66
|
+
relevance += 2;
|
|
67
|
+
}
|
|
68
|
+
// Penalize high coverage (we care about low coverage)
|
|
69
|
+
const coveragePenalty = e.lines / 100;
|
|
70
|
+
return { ...e, relevance, score: relevance + (1 - coveragePenalty) };
|
|
71
|
+
})
|
|
72
|
+
.sort((a, b) => b.score - a.score);
|
|
73
|
+
const gaps = scoredFiles.slice(0, 8).map(e => {
|
|
74
|
+
const { gapType, priority } = classifyCoverageGap(e.lines, threshold);
|
|
75
|
+
return {
|
|
76
|
+
filePath: e.filePath,
|
|
77
|
+
coveragePercent: e.lines,
|
|
78
|
+
gapType,
|
|
79
|
+
priority,
|
|
80
|
+
suggestedAgents: suggestAgentsForFile(e.filePath),
|
|
81
|
+
reason: `${e.lines.toFixed(1)}% coverage, below ${threshold}%`,
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
const criticalGaps = gaps.filter(g => g.gapType === 'critical').length;
|
|
85
|
+
const primaryAgent = taskLower.includes('test') ? 'tester' :
|
|
86
|
+
taskLower.includes('security') || taskLower.includes('auth') ? 'security-auditor' :
|
|
87
|
+
taskLower.includes('fix') || taskLower.includes('bug') ? 'coder' : 'tester';
|
|
88
|
+
const suggestions = [];
|
|
89
|
+
if (criticalGaps > 0)
|
|
90
|
+
suggestions.push(`${criticalGaps} critical coverage gaps need immediate attention`);
|
|
91
|
+
if (diskCoverage.summary.overallLineCoverage < threshold) {
|
|
92
|
+
suggestions.push(`Overall line coverage (${diskCoverage.summary.overallLineCoverage.toFixed(1)}%) is below ${threshold}% threshold`);
|
|
93
|
+
}
|
|
94
|
+
if (scoredFiles.length > 8)
|
|
95
|
+
suggestions.push(`${scoredFiles.length - 8} additional files with low coverage`);
|
|
96
|
+
const result = {
|
|
97
|
+
success: true,
|
|
98
|
+
task,
|
|
99
|
+
coverageAware: true,
|
|
100
|
+
gaps,
|
|
101
|
+
routing: {
|
|
102
|
+
primaryAgent,
|
|
103
|
+
confidence: gaps.length > 0 ? 0.85 : 0.6,
|
|
104
|
+
reason: gaps.length > 0
|
|
105
|
+
? `Routing to ${primaryAgent} based on ${gaps.length} coverage gaps related to task`
|
|
106
|
+
: `No coverage gaps found related to task, routing to ${primaryAgent}`,
|
|
107
|
+
coverageImpact: gaps.length > 0 ? 'high' : 'low',
|
|
108
|
+
},
|
|
109
|
+
suggestions,
|
|
110
|
+
metrics: {
|
|
111
|
+
filesAnalyzed: diskCoverage.summary.totalFiles,
|
|
112
|
+
totalGaps: scoredFiles.length,
|
|
113
|
+
criticalGaps,
|
|
114
|
+
avgCoverage: diskCoverage.summary.overallLineCoverage,
|
|
115
|
+
},
|
|
116
|
+
source: diskCoverage.source,
|
|
117
|
+
};
|
|
118
|
+
if (ctx.flags.format === 'json') {
|
|
119
|
+
output.printJson(result);
|
|
120
|
+
return { success: true, data: result };
|
|
121
|
+
}
|
|
122
|
+
output.writeln();
|
|
123
|
+
output.printBox([
|
|
124
|
+
`Agent: ${output.highlight(result.routing.primaryAgent)}`,
|
|
125
|
+
`Confidence: ${(result.routing.confidence * 100).toFixed(1)}%`,
|
|
126
|
+
`Coverage-Aware: ${output.success('Yes')} (from ${diskCoverage.source})`,
|
|
127
|
+
`Reason: ${result.routing.reason}`
|
|
128
|
+
].join('\n'), 'Coverage-Aware Routing');
|
|
129
|
+
if (gaps.length > 0) {
|
|
130
|
+
output.writeln();
|
|
131
|
+
output.writeln(output.bold('Priority Coverage Gaps'));
|
|
132
|
+
output.printTable({
|
|
133
|
+
columns: [
|
|
134
|
+
{ key: 'filePath', header: 'File', width: 35, format: (v) => {
|
|
135
|
+
const s = String(v);
|
|
136
|
+
return s.length > 32 ? '...' + s.slice(-32) : s;
|
|
137
|
+
} },
|
|
138
|
+
{ key: 'coveragePercent', header: 'Coverage', width: 10, align: 'right', format: (v) => `${Number(v).toFixed(1)}%` },
|
|
139
|
+
{ key: 'gapType', header: 'Type', width: 10 },
|
|
140
|
+
{ key: 'suggestedAgents', header: 'Agent', width: 15, format: (v) => Array.isArray(v) ? v[0] || '' : String(v) }
|
|
141
|
+
],
|
|
142
|
+
data: gaps.slice(0, 8)
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
if (result.metrics.filesAnalyzed > 0) {
|
|
146
|
+
output.writeln();
|
|
147
|
+
output.writeln(output.bold('Coverage Metrics'));
|
|
148
|
+
output.printList([
|
|
149
|
+
`Files Analyzed: ${result.metrics.filesAnalyzed}`,
|
|
150
|
+
`Total Gaps: ${result.metrics.totalGaps}`,
|
|
151
|
+
`Critical Gaps: ${result.metrics.criticalGaps}`,
|
|
152
|
+
`Average Coverage: ${result.metrics.avgCoverage.toFixed(1)}%`
|
|
153
|
+
]);
|
|
154
|
+
}
|
|
155
|
+
if (suggestions.length > 0) {
|
|
156
|
+
output.writeln();
|
|
157
|
+
output.writeln(output.bold('Suggestions'));
|
|
158
|
+
output.printList(suggestions.map(s => output.dim(s)));
|
|
159
|
+
}
|
|
160
|
+
return { success: true, data: result };
|
|
161
|
+
}
|
|
162
|
+
// No disk coverage - fall back to MCP tool
|
|
163
|
+
try {
|
|
164
|
+
const result = await callMCPTool('hooks_coverage-route', {
|
|
165
|
+
task,
|
|
166
|
+
threshold,
|
|
167
|
+
useMonovector,
|
|
168
|
+
});
|
|
169
|
+
spinner.stop();
|
|
170
|
+
if (ctx.flags.format === 'json') {
|
|
171
|
+
output.printJson(result);
|
|
172
|
+
return { success: true, data: result };
|
|
173
|
+
}
|
|
174
|
+
output.writeln();
|
|
175
|
+
output.printBox([
|
|
176
|
+
`Agent: ${output.highlight(result.routing.primaryAgent)}`,
|
|
177
|
+
`Confidence: ${(result.routing.confidence * 100).toFixed(1)}%`,
|
|
178
|
+
`Coverage-Aware: ${result.coverageAware ? output.success('Yes') : output.dim('No coverage data')}`,
|
|
179
|
+
`Reason: ${result.routing.reason}`
|
|
180
|
+
].join('\n'), 'Coverage-Aware Routing');
|
|
181
|
+
if (result.gaps.length > 0) {
|
|
182
|
+
output.writeln();
|
|
183
|
+
output.writeln(output.bold('Priority Coverage Gaps'));
|
|
184
|
+
output.printTable({
|
|
185
|
+
columns: [
|
|
186
|
+
{ key: 'filePath', header: 'File', width: 35, format: (v) => {
|
|
187
|
+
const s = String(v);
|
|
188
|
+
return s.length > 32 ? '...' + s.slice(-32) : s;
|
|
189
|
+
} },
|
|
190
|
+
{ key: 'coveragePercent', header: 'Coverage', width: 10, align: 'right', format: (v) => `${Number(v).toFixed(1)}%` },
|
|
191
|
+
{ key: 'gapType', header: 'Type', width: 10 },
|
|
192
|
+
{ key: 'suggestedAgents', header: 'Agent', width: 15, format: (v) => Array.isArray(v) ? v[0] || '' : String(v) }
|
|
193
|
+
],
|
|
194
|
+
data: result.gaps.slice(0, 8)
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
if (result.metrics.filesAnalyzed > 0) {
|
|
198
|
+
output.writeln();
|
|
199
|
+
output.writeln(output.bold('Coverage Metrics'));
|
|
200
|
+
output.printList([
|
|
201
|
+
`Files Analyzed: ${result.metrics.filesAnalyzed}`,
|
|
202
|
+
`Total Gaps: ${result.metrics.totalGaps}`,
|
|
203
|
+
`Critical Gaps: ${result.metrics.criticalGaps}`,
|
|
204
|
+
`Average Coverage: ${result.metrics.avgCoverage.toFixed(1)}%`
|
|
205
|
+
]);
|
|
206
|
+
}
|
|
207
|
+
if (result.suggestions.length > 0) {
|
|
208
|
+
output.writeln();
|
|
209
|
+
output.writeln(output.bold('Suggestions'));
|
|
210
|
+
output.printList(result.suggestions.map(s => output.dim(s)));
|
|
211
|
+
}
|
|
212
|
+
return { success: true, data: result };
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
spinner.fail('No coverage data found');
|
|
216
|
+
output.writeln();
|
|
217
|
+
output.printWarning('No coverage data found. Run your test suite with coverage first.');
|
|
218
|
+
output.writeln();
|
|
219
|
+
output.printList([
|
|
220
|
+
'Jest: npx jest --coverage',
|
|
221
|
+
'Vitest: npx vitest --coverage',
|
|
222
|
+
'nyc: npx nyc npm test',
|
|
223
|
+
'c8: npx c8 npm test',
|
|
224
|
+
]);
|
|
225
|
+
output.writeln();
|
|
226
|
+
output.writeln(output.dim('Expected files: coverage/coverage-summary.json, coverage/lcov.info, or .nyc_output/out.json'));
|
|
227
|
+
return { success: false, exitCode: 1 };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
// Coverage suggest subcommand
|
|
232
|
+
export const coverageSuggestCommand = {
|
|
233
|
+
name: 'coverage-suggest',
|
|
234
|
+
description: 'Suggest coverage improvements for a path (monovector integration)',
|
|
235
|
+
options: [
|
|
236
|
+
{
|
|
237
|
+
name: 'path',
|
|
238
|
+
short: 'p',
|
|
239
|
+
description: 'Path to analyze for coverage suggestions',
|
|
240
|
+
type: 'string',
|
|
241
|
+
required: true
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: 'threshold',
|
|
245
|
+
description: 'Coverage threshold percentage (default: 80)',
|
|
246
|
+
type: 'number',
|
|
247
|
+
default: 80
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: 'limit',
|
|
251
|
+
short: 'l',
|
|
252
|
+
description: 'Maximum number of suggestions (default: 20)',
|
|
253
|
+
type: 'number',
|
|
254
|
+
default: 20
|
|
255
|
+
}
|
|
256
|
+
],
|
|
257
|
+
examples: [
|
|
258
|
+
{ command: 'monomind hooks coverage-suggest -p src/', description: 'Suggest improvements for src/' },
|
|
259
|
+
{ command: 'monomind hooks coverage-suggest -p src/services --threshold 90', description: 'Stricter threshold' }
|
|
260
|
+
],
|
|
261
|
+
action: async (ctx) => {
|
|
262
|
+
const targetPath = ctx.args[0] || ctx.flags.path;
|
|
263
|
+
const threshold = ctx.flags.threshold || 80;
|
|
264
|
+
const limit = ctx.flags.limit || 20;
|
|
265
|
+
if (!targetPath) {
|
|
266
|
+
output.printError('Path is required. Use --path or -p flag.');
|
|
267
|
+
return { success: false, exitCode: 1 };
|
|
268
|
+
}
|
|
269
|
+
const spinner = output.createSpinner({ text: `Analyzing coverage for ${targetPath}...` });
|
|
270
|
+
spinner.start();
|
|
271
|
+
// Try reading coverage from disk first
|
|
272
|
+
const diskCoverage = readCoverageFromDisk();
|
|
273
|
+
if (diskCoverage.found) {
|
|
274
|
+
spinner.succeed(`Coverage data loaded from ${diskCoverage.source}`);
|
|
275
|
+
// Filter entries to those matching the target path
|
|
276
|
+
const pathLower = targetPath.toLowerCase().replace(/\\/g, '/');
|
|
277
|
+
const matchingEntries = diskCoverage.entries.filter(e => {
|
|
278
|
+
const fileLower = e.filePath.toLowerCase().replace(/\\/g, '/');
|
|
279
|
+
return fileLower.includes(pathLower);
|
|
280
|
+
});
|
|
281
|
+
const belowThreshold = matchingEntries.filter(e => e.lines < threshold);
|
|
282
|
+
const suggestions = belowThreshold.slice(0, limit).map(e => {
|
|
283
|
+
const { gapType, priority } = classifyCoverageGap(e.lines, threshold);
|
|
284
|
+
return {
|
|
285
|
+
filePath: e.filePath,
|
|
286
|
+
coveragePercent: e.lines,
|
|
287
|
+
gapType,
|
|
288
|
+
priority,
|
|
289
|
+
suggestedAgents: suggestAgentsForFile(e.filePath),
|
|
290
|
+
reason: e.lines === 0 ? 'No coverage at all' :
|
|
291
|
+
e.lines < 20 ? 'Very low coverage, needs tests' :
|
|
292
|
+
e.lines < 50 ? 'Below 50%, add more tests' :
|
|
293
|
+
`Below ${threshold}% threshold`,
|
|
294
|
+
};
|
|
295
|
+
});
|
|
296
|
+
const totalLinesCov = matchingEntries.length > 0
|
|
297
|
+
? matchingEntries.reduce((acc, e) => acc + e.lines, 0) / matchingEntries.length
|
|
298
|
+
: 0;
|
|
299
|
+
const totalBranchesCov = matchingEntries.length > 0
|
|
300
|
+
? matchingEntries.reduce((acc, e) => acc + e.branches, 0) / matchingEntries.length
|
|
301
|
+
: 0;
|
|
302
|
+
const prioritizedFiles = belowThreshold.slice(0, 5).map(e => e.filePath);
|
|
303
|
+
const result = {
|
|
304
|
+
success: true,
|
|
305
|
+
path: targetPath,
|
|
306
|
+
suggestions,
|
|
307
|
+
summary: {
|
|
308
|
+
totalFiles: matchingEntries.length,
|
|
309
|
+
overallLineCoverage: totalLinesCov,
|
|
310
|
+
overallBranchCoverage: totalBranchesCov,
|
|
311
|
+
filesBelowThreshold: belowThreshold.length,
|
|
312
|
+
},
|
|
313
|
+
prioritizedFiles,
|
|
314
|
+
monovectorAvailable: false,
|
|
315
|
+
source: diskCoverage.source,
|
|
316
|
+
};
|
|
317
|
+
if (ctx.flags.format === 'json') {
|
|
318
|
+
output.printJson(result);
|
|
319
|
+
return { success: true, data: result };
|
|
320
|
+
}
|
|
321
|
+
output.writeln();
|
|
322
|
+
output.printBox([
|
|
323
|
+
`Path: ${output.highlight(targetPath)}`,
|
|
324
|
+
`Files Analyzed: ${result.summary.totalFiles}`,
|
|
325
|
+
`Line Coverage: ${result.summary.overallLineCoverage.toFixed(1)}%`,
|
|
326
|
+
`Branch Coverage: ${result.summary.overallBranchCoverage.toFixed(1)}%`,
|
|
327
|
+
`Below Threshold: ${result.summary.filesBelowThreshold} files`,
|
|
328
|
+
`Source: ${output.highlight(diskCoverage.source)}`
|
|
329
|
+
].join('\n'), 'Coverage Summary');
|
|
330
|
+
if (suggestions.length > 0) {
|
|
331
|
+
output.writeln();
|
|
332
|
+
output.writeln(output.bold('Coverage Improvement Suggestions'));
|
|
333
|
+
output.printTable({
|
|
334
|
+
columns: [
|
|
335
|
+
{ key: 'filePath', header: 'File', width: 40, format: (v) => {
|
|
336
|
+
const s = String(v);
|
|
337
|
+
return s.length > 37 ? '...' + s.slice(-37) : s;
|
|
338
|
+
} },
|
|
339
|
+
{ key: 'coveragePercent', header: 'Coverage', width: 10, align: 'right', format: (v) => `${Number(v).toFixed(1)}%` },
|
|
340
|
+
{ key: 'gapType', header: 'Priority', width: 10 },
|
|
341
|
+
{ key: 'reason', header: 'Reason', width: 25 }
|
|
342
|
+
],
|
|
343
|
+
data: suggestions.slice(0, 15)
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
output.writeln();
|
|
348
|
+
output.printSuccess('All files meet coverage threshold!');
|
|
349
|
+
}
|
|
350
|
+
if (prioritizedFiles.length > 0) {
|
|
351
|
+
output.writeln();
|
|
352
|
+
output.writeln(output.bold('Priority Files (Top 5)'));
|
|
353
|
+
output.printList(prioritizedFiles.slice(0, 5).map(f => output.highlight(f)));
|
|
354
|
+
}
|
|
355
|
+
return { success: true, data: result };
|
|
356
|
+
}
|
|
357
|
+
// No disk coverage - fall back to MCP tool
|
|
358
|
+
try {
|
|
359
|
+
const result = await callMCPTool('hooks_coverage-suggest', {
|
|
360
|
+
path: targetPath,
|
|
361
|
+
threshold,
|
|
362
|
+
limit,
|
|
363
|
+
});
|
|
364
|
+
spinner.stop();
|
|
365
|
+
if (ctx.flags.format === 'json') {
|
|
366
|
+
output.printJson(result);
|
|
367
|
+
return { success: true, data: result };
|
|
368
|
+
}
|
|
369
|
+
output.writeln();
|
|
370
|
+
output.printBox([
|
|
371
|
+
`Path: ${output.highlight(result.path)}`,
|
|
372
|
+
`Files Analyzed: ${result.summary.totalFiles}`,
|
|
373
|
+
`Line Coverage: ${result.summary.overallLineCoverage.toFixed(1)}%`,
|
|
374
|
+
`Branch Coverage: ${result.summary.overallBranchCoverage.toFixed(1)}%`,
|
|
375
|
+
`Below Threshold: ${result.summary.filesBelowThreshold} files`,
|
|
376
|
+
`Keyword routing: ${result.monovectorAvailable ? output.success('Available') : output.dim('Unavailable')}`
|
|
377
|
+
].join('\n'), 'Coverage Summary');
|
|
378
|
+
if (result.suggestions.length > 0) {
|
|
379
|
+
output.writeln();
|
|
380
|
+
output.writeln(output.bold('Coverage Improvement Suggestions'));
|
|
381
|
+
output.printTable({
|
|
382
|
+
columns: [
|
|
383
|
+
{ key: 'filePath', header: 'File', width: 40, format: (v) => {
|
|
384
|
+
const s = String(v);
|
|
385
|
+
return s.length > 37 ? '...' + s.slice(-37) : s;
|
|
386
|
+
} },
|
|
387
|
+
{ key: 'coveragePercent', header: 'Coverage', width: 10, align: 'right', format: (v) => `${Number(v).toFixed(1)}%` },
|
|
388
|
+
{ key: 'gapType', header: 'Priority', width: 10 },
|
|
389
|
+
{ key: 'reason', header: 'Reason', width: 25 }
|
|
390
|
+
],
|
|
391
|
+
data: result.suggestions.slice(0, 15)
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
output.writeln();
|
|
396
|
+
output.printSuccess('All files meet coverage threshold!');
|
|
397
|
+
}
|
|
398
|
+
if (result.prioritizedFiles.length > 0) {
|
|
399
|
+
output.writeln();
|
|
400
|
+
output.writeln(output.bold('Priority Files (Top 5)'));
|
|
401
|
+
output.printList(result.prioritizedFiles.slice(0, 5).map(f => output.highlight(f)));
|
|
402
|
+
}
|
|
403
|
+
return { success: true, data: result };
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
spinner.fail('No coverage data found');
|
|
407
|
+
output.writeln();
|
|
408
|
+
output.printWarning('No coverage data found. Run your test suite with coverage first.');
|
|
409
|
+
output.writeln();
|
|
410
|
+
output.printList([
|
|
411
|
+
'Jest: npx jest --coverage',
|
|
412
|
+
'Vitest: npx vitest --coverage',
|
|
413
|
+
'nyc: npx nyc npm test',
|
|
414
|
+
'c8: npx c8 npm test',
|
|
415
|
+
]);
|
|
416
|
+
output.writeln();
|
|
417
|
+
output.writeln(output.dim('Expected files: coverage/coverage-summary.json, coverage/lcov.info, or .nyc_output/out.json'));
|
|
418
|
+
return { success: false, exitCode: 1 };
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
// Coverage gaps subcommand
|
|
423
|
+
export const coverageGapsCommand = {
|
|
424
|
+
name: 'coverage-gaps',
|
|
425
|
+
description: 'List all coverage gaps with priority scoring and agent assignments',
|
|
426
|
+
options: [
|
|
427
|
+
{
|
|
428
|
+
name: 'threshold',
|
|
429
|
+
description: 'Coverage threshold percentage (default: 80)',
|
|
430
|
+
type: 'number',
|
|
431
|
+
default: 80
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
name: 'group-by-agent',
|
|
435
|
+
description: 'Group gaps by suggested agent (default: true)',
|
|
436
|
+
type: 'boolean',
|
|
437
|
+
default: true
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
name: 'critical-only',
|
|
441
|
+
description: 'Show only critical gaps',
|
|
442
|
+
type: 'boolean',
|
|
443
|
+
default: false
|
|
444
|
+
}
|
|
445
|
+
],
|
|
446
|
+
examples: [
|
|
447
|
+
{ command: 'monomind hooks coverage-gaps', description: 'List all coverage gaps' },
|
|
448
|
+
{ command: 'monomind hooks coverage-gaps --critical-only', description: 'Only critical gaps' },
|
|
449
|
+
{ command: 'monomind hooks coverage-gaps --threshold 90', description: 'Stricter threshold' }
|
|
450
|
+
],
|
|
451
|
+
action: async (ctx) => {
|
|
452
|
+
const threshold = ctx.flags.threshold || 80;
|
|
453
|
+
const groupByAgent = ctx.flags['group-by-agent'] !== false;
|
|
454
|
+
const criticalOnly = ctx.flags['critical-only'] || false;
|
|
455
|
+
const spinner = output.createSpinner({ text: 'Analyzing project coverage gaps...' });
|
|
456
|
+
spinner.start();
|
|
457
|
+
// Try reading coverage from disk first
|
|
458
|
+
const diskCoverage = readCoverageFromDisk();
|
|
459
|
+
if (diskCoverage.found) {
|
|
460
|
+
spinner.succeed(`Coverage data loaded from ${diskCoverage.source}`);
|
|
461
|
+
// Build gaps from disk data
|
|
462
|
+
const allGaps = diskCoverage.entries
|
|
463
|
+
.filter(e => e.lines < threshold)
|
|
464
|
+
.map(e => {
|
|
465
|
+
const { gapType, priority } = classifyCoverageGap(e.lines, threshold);
|
|
466
|
+
return {
|
|
467
|
+
filePath: e.filePath,
|
|
468
|
+
coveragePercent: e.lines,
|
|
469
|
+
gapType,
|
|
470
|
+
complexity: Math.round((100 - e.lines) / 10),
|
|
471
|
+
priority,
|
|
472
|
+
suggestedAgents: suggestAgentsForFile(e.filePath),
|
|
473
|
+
reason: `Line coverage ${e.lines.toFixed(1)}% below ${threshold}% threshold`,
|
|
474
|
+
};
|
|
475
|
+
});
|
|
476
|
+
const gaps = criticalOnly
|
|
477
|
+
? allGaps.filter(g => g.gapType === 'critical')
|
|
478
|
+
: allGaps;
|
|
479
|
+
// Build agent assignments
|
|
480
|
+
const agentAssignments = {};
|
|
481
|
+
if (groupByAgent) {
|
|
482
|
+
for (const gap of gaps) {
|
|
483
|
+
const agent = gap.suggestedAgents[0] || 'tester';
|
|
484
|
+
if (!agentAssignments[agent])
|
|
485
|
+
agentAssignments[agent] = [];
|
|
486
|
+
agentAssignments[agent].push(gap.filePath);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
const result = {
|
|
490
|
+
success: true,
|
|
491
|
+
gaps,
|
|
492
|
+
summary: {
|
|
493
|
+
totalFiles: diskCoverage.summary.totalFiles,
|
|
494
|
+
overallLineCoverage: diskCoverage.summary.overallLineCoverage,
|
|
495
|
+
overallBranchCoverage: diskCoverage.summary.overallBranchCoverage,
|
|
496
|
+
filesBelowThreshold: gaps.length,
|
|
497
|
+
coverageThreshold: threshold,
|
|
498
|
+
},
|
|
499
|
+
agentAssignments,
|
|
500
|
+
monovectorAvailable: false,
|
|
501
|
+
source: diskCoverage.source,
|
|
502
|
+
};
|
|
503
|
+
if (ctx.flags.format === 'json') {
|
|
504
|
+
output.printJson(result);
|
|
505
|
+
return { success: true, data: result };
|
|
506
|
+
}
|
|
507
|
+
output.writeln();
|
|
508
|
+
output.printBox([
|
|
509
|
+
`Total Files: ${result.summary.totalFiles}`,
|
|
510
|
+
`Line Coverage: ${result.summary.overallLineCoverage.toFixed(1)}%`,
|
|
511
|
+
`Branch Coverage: ${result.summary.overallBranchCoverage.toFixed(1)}%`,
|
|
512
|
+
`Below ${threshold}%: ${result.summary.filesBelowThreshold} files`,
|
|
513
|
+
`Source: ${output.highlight(diskCoverage.source)}`
|
|
514
|
+
].join('\n'), 'Coverage Gap Analysis');
|
|
515
|
+
if (gaps.length > 0) {
|
|
516
|
+
output.writeln();
|
|
517
|
+
output.writeln(output.bold(`Coverage Gaps (${gaps.length} files)`));
|
|
518
|
+
output.printTable({
|
|
519
|
+
columns: [
|
|
520
|
+
{ key: 'filePath', header: 'File', width: 35, format: (v) => {
|
|
521
|
+
const s = String(v);
|
|
522
|
+
return s.length > 32 ? '...' + s.slice(-32) : s;
|
|
523
|
+
} },
|
|
524
|
+
{ key: 'coveragePercent', header: 'Coverage', width: 10, align: 'right', format: (v) => `${Number(v).toFixed(1)}%` },
|
|
525
|
+
{ key: 'gapType', header: 'Type', width: 10, format: (v) => {
|
|
526
|
+
const t = String(v);
|
|
527
|
+
if (t === 'critical')
|
|
528
|
+
return output.error(t);
|
|
529
|
+
if (t === 'high')
|
|
530
|
+
return output.warning(t);
|
|
531
|
+
return t;
|
|
532
|
+
} },
|
|
533
|
+
{ key: 'priority', header: 'Priority', width: 8, align: 'right' },
|
|
534
|
+
{ key: 'suggestedAgents', header: 'Agent', width: 12, format: (v) => Array.isArray(v) ? v[0] || '' : String(v) }
|
|
535
|
+
],
|
|
536
|
+
data: gaps.slice(0, 20)
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
output.writeln();
|
|
541
|
+
output.printSuccess('No coverage gaps found! All files meet threshold.');
|
|
542
|
+
}
|
|
543
|
+
if (groupByAgent && Object.keys(agentAssignments).length > 0) {
|
|
544
|
+
output.writeln();
|
|
545
|
+
output.writeln(output.bold('Agent Assignments'));
|
|
546
|
+
for (const [agent, files] of Object.entries(agentAssignments)) {
|
|
547
|
+
output.writeln();
|
|
548
|
+
output.writeln(` ${output.highlight(agent)} (${files.length} files)`);
|
|
549
|
+
files.slice(0, 3).forEach(f => {
|
|
550
|
+
output.writeln(` - ${output.dim(f)}`);
|
|
551
|
+
});
|
|
552
|
+
if (files.length > 3) {
|
|
553
|
+
output.writeln(` ... and ${files.length - 3} more`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
return { success: true, data: result };
|
|
558
|
+
}
|
|
559
|
+
// No coverage files on disk - try MCP tool as fallback
|
|
560
|
+
try {
|
|
561
|
+
const result = await callMCPTool('hooks_coverage-gaps', {
|
|
562
|
+
threshold,
|
|
563
|
+
groupByAgent,
|
|
564
|
+
});
|
|
565
|
+
spinner.stop();
|
|
566
|
+
const gaps = criticalOnly
|
|
567
|
+
? result.gaps.filter(g => g.gapType === 'critical')
|
|
568
|
+
: result.gaps;
|
|
569
|
+
if (ctx.flags.format === 'json') {
|
|
570
|
+
output.printJson({ ...result, gaps });
|
|
571
|
+
return { success: true, data: result };
|
|
572
|
+
}
|
|
573
|
+
output.writeln();
|
|
574
|
+
output.printBox([
|
|
575
|
+
`Total Files: ${result.summary.totalFiles}`,
|
|
576
|
+
`Line Coverage: ${result.summary.overallLineCoverage.toFixed(1)}%`,
|
|
577
|
+
`Branch Coverage: ${result.summary.overallBranchCoverage.toFixed(1)}%`,
|
|
578
|
+
`Below ${result.summary.coverageThreshold}%: ${result.summary.filesBelowThreshold} files`,
|
|
579
|
+
`Keyword routing: ${result.monovectorAvailable ? output.success('Available') : output.dim('Unavailable')}`
|
|
580
|
+
].join('\n'), 'Coverage Gap Analysis');
|
|
581
|
+
if (gaps.length > 0) {
|
|
582
|
+
output.writeln();
|
|
583
|
+
output.writeln(output.bold(`Coverage Gaps (${gaps.length} files)`));
|
|
584
|
+
output.printTable({
|
|
585
|
+
columns: [
|
|
586
|
+
{ key: 'filePath', header: 'File', width: 35, format: (v) => {
|
|
587
|
+
const s = String(v);
|
|
588
|
+
return s.length > 32 ? '...' + s.slice(-32) : s;
|
|
589
|
+
} },
|
|
590
|
+
{ key: 'coveragePercent', header: 'Coverage', width: 10, align: 'right', format: (v) => `${Number(v).toFixed(1)}%` },
|
|
591
|
+
{ key: 'gapType', header: 'Type', width: 10, format: (v) => {
|
|
592
|
+
const t = String(v);
|
|
593
|
+
if (t === 'critical')
|
|
594
|
+
return output.error(t);
|
|
595
|
+
if (t === 'high')
|
|
596
|
+
return output.warning(t);
|
|
597
|
+
return t;
|
|
598
|
+
} },
|
|
599
|
+
{ key: 'priority', header: 'Priority', width: 8, align: 'right' },
|
|
600
|
+
{ key: 'suggestedAgents', header: 'Agent', width: 12, format: (v) => Array.isArray(v) ? v[0] || '' : String(v) }
|
|
601
|
+
],
|
|
602
|
+
data: gaps.slice(0, 20)
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
output.writeln();
|
|
607
|
+
output.printSuccess('No coverage gaps found! All files meet threshold.');
|
|
608
|
+
}
|
|
609
|
+
if (groupByAgent && Object.keys(result.agentAssignments).length > 0) {
|
|
610
|
+
output.writeln();
|
|
611
|
+
output.writeln(output.bold('Agent Assignments'));
|
|
612
|
+
for (const [agent, files] of Object.entries(result.agentAssignments)) {
|
|
613
|
+
output.writeln();
|
|
614
|
+
output.writeln(` ${output.highlight(agent)} (${files.length} files)`);
|
|
615
|
+
files.slice(0, 3).forEach(f => {
|
|
616
|
+
output.writeln(` - ${output.dim(f)}`);
|
|
617
|
+
});
|
|
618
|
+
if (files.length > 3) {
|
|
619
|
+
output.writeln(` ... and ${files.length - 3} more`);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
return { success: true, data: result };
|
|
624
|
+
}
|
|
625
|
+
catch (error) {
|
|
626
|
+
spinner.fail('No coverage data found');
|
|
627
|
+
output.writeln();
|
|
628
|
+
output.printWarning('No coverage data found. Run your test suite with coverage first.');
|
|
629
|
+
output.writeln();
|
|
630
|
+
output.printList([
|
|
631
|
+
'Jest: npx jest --coverage',
|
|
632
|
+
'Vitest: npx vitest --coverage',
|
|
633
|
+
'nyc: npx nyc npm test',
|
|
634
|
+
'c8: npx c8 npm test',
|
|
635
|
+
]);
|
|
636
|
+
output.writeln();
|
|
637
|
+
output.writeln(output.dim('Expected files: coverage/coverage-summary.json, coverage/lcov.info, or .nyc_output/out.json'));
|
|
638
|
+
return { success: false, exitCode: 1 };
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
// Progress hook command
|
|
643
|
+
export const progressHookCommand = {
|
|
644
|
+
name: 'progress',
|
|
645
|
+
description: 'Check implementation progress via hooks',
|
|
646
|
+
options: [
|
|
647
|
+
{
|
|
648
|
+
name: 'detailed',
|
|
649
|
+
short: 'd',
|
|
650
|
+
description: 'Show detailed breakdown by category',
|
|
651
|
+
type: 'boolean',
|
|
652
|
+
default: false
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
name: 'sync',
|
|
656
|
+
short: 's',
|
|
657
|
+
description: 'Sync and persist progress to file',
|
|
658
|
+
type: 'boolean',
|
|
659
|
+
default: false
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
name: 'summary',
|
|
663
|
+
description: 'Show human-readable summary',
|
|
664
|
+
type: 'boolean',
|
|
665
|
+
default: false
|
|
666
|
+
}
|
|
667
|
+
],
|
|
668
|
+
examples: [
|
|
669
|
+
{ command: 'monomind hooks progress', description: 'Check current progress' },
|
|
670
|
+
{ command: 'monomind hooks progress -d', description: 'Detailed breakdown' },
|
|
671
|
+
{ command: 'monomind hooks progress --sync', description: 'Sync progress to file' },
|
|
672
|
+
{ command: 'monomind hooks progress --summary', description: 'Human-readable summary' }
|
|
673
|
+
],
|
|
674
|
+
action: async (ctx) => {
|
|
675
|
+
const detailed = ctx.flags.detailed;
|
|
676
|
+
const sync = ctx.flags.sync;
|
|
677
|
+
const summary = ctx.flags.summary;
|
|
678
|
+
try {
|
|
679
|
+
if (summary) {
|
|
680
|
+
const spinner = output.createSpinner({ text: 'Getting progress summary...' });
|
|
681
|
+
spinner.start();
|
|
682
|
+
const result = await callMCPTool('progress_summary', {});
|
|
683
|
+
spinner.stop();
|
|
684
|
+
if (ctx.flags.format === 'json') {
|
|
685
|
+
output.printJson(result);
|
|
686
|
+
return { success: true, data: result };
|
|
687
|
+
}
|
|
688
|
+
output.writeln();
|
|
689
|
+
output.writeln(result.summary);
|
|
690
|
+
return { success: true, data: result };
|
|
691
|
+
}
|
|
692
|
+
if (sync) {
|
|
693
|
+
const spinner = output.createSpinner({ text: 'Syncing progress...' });
|
|
694
|
+
spinner.start();
|
|
695
|
+
const result = await callMCPTool('progress_sync', {});
|
|
696
|
+
spinner.stop();
|
|
697
|
+
if (ctx.flags.format === 'json') {
|
|
698
|
+
output.printJson(result);
|
|
699
|
+
return { success: true, data: result };
|
|
700
|
+
}
|
|
701
|
+
output.writeln();
|
|
702
|
+
output.printSuccess(`Progress synced: ${result.progress}%`);
|
|
703
|
+
output.writeln(output.dim(` Persisted to .monomind/metrics/v1-progress.json`));
|
|
704
|
+
output.writeln(output.dim(` Last updated: ${result.lastUpdated}`));
|
|
705
|
+
return { success: true, data: result };
|
|
706
|
+
}
|
|
707
|
+
// Default: check progress
|
|
708
|
+
const spinner = output.createSpinner({ text: 'Checking v1 progress...' });
|
|
709
|
+
spinner.start();
|
|
710
|
+
const result = await callMCPTool('progress_check', { detailed });
|
|
711
|
+
spinner.stop();
|
|
712
|
+
if (ctx.flags.format === 'json') {
|
|
713
|
+
output.printJson(result);
|
|
714
|
+
return { success: true, data: result };
|
|
715
|
+
}
|
|
716
|
+
output.writeln();
|
|
717
|
+
const progressValue = result.overall ?? result.progress ?? 0;
|
|
718
|
+
// Create progress bar
|
|
719
|
+
const barWidth = 30;
|
|
720
|
+
const filled = Math.round((progressValue / 100) * barWidth);
|
|
721
|
+
const empty = barWidth - filled;
|
|
722
|
+
const bar = output.success('█'.repeat(filled)) + output.dim('░'.repeat(empty));
|
|
723
|
+
output.writeln(output.bold('v1 Implementation Progress'));
|
|
724
|
+
output.writeln();
|
|
725
|
+
output.writeln(`[${bar}] ${progressValue}%`);
|
|
726
|
+
output.writeln();
|
|
727
|
+
if (detailed && result.cli) {
|
|
728
|
+
output.writeln(output.highlight('CLI Commands:') + ` ${result.cli.progress}% (${result.cli.commands}/${result.cli.target})`);
|
|
729
|
+
output.writeln(output.highlight('MCP Tools:') + ` ${result.mcp?.progress ?? 0}% (${result.mcp?.tools ?? 0}/${result.mcp?.target ?? 0})`);
|
|
730
|
+
output.writeln(output.highlight('Hooks:') + ` ${result.hooks?.progress ?? 0}% (${result.hooks?.subcommands ?? 0}/${result.hooks?.target ?? 0})`);
|
|
731
|
+
output.writeln(output.highlight('Packages:') + ` ${result.packages?.progress ?? 0}% (${result.packages?.total ?? 0}/${result.packages?.target ?? 0})`);
|
|
732
|
+
output.writeln(output.highlight('DDD Structure:') + ` ${result.ddd?.progress ?? 0}% (${result.packages?.withDDD ?? 0}/${result.packages?.total ?? 0})`);
|
|
733
|
+
output.writeln();
|
|
734
|
+
if (result.codebase) {
|
|
735
|
+
output.writeln(output.dim(`Codebase: ${result.codebase.totalFiles} files, ${result.codebase.totalLines.toLocaleString()} lines`));
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
else if (result.breakdown) {
|
|
739
|
+
output.writeln('Breakdown:');
|
|
740
|
+
for (const [category, value] of Object.entries(result.breakdown)) {
|
|
741
|
+
output.writeln(` ${output.highlight(category)}: ${value}`);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
if (result.lastUpdated) {
|
|
745
|
+
output.writeln(output.dim(`Last updated: ${result.lastUpdated}`));
|
|
746
|
+
}
|
|
747
|
+
return { success: true, data: result };
|
|
748
|
+
}
|
|
749
|
+
catch (error) {
|
|
750
|
+
if (error instanceof MCPClientError) {
|
|
751
|
+
output.printError(`Progress check failed: ${error.message}`);
|
|
752
|
+
}
|
|
753
|
+
else {
|
|
754
|
+
output.printError(`Progress check failed: ${String(error)}`);
|
|
755
|
+
}
|
|
756
|
+
return { success: false, exitCode: 1 };
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
// Statusline subcommand - generates dynamic status display
|
|
761
|
+
export const statuslineCommand = {
|
|
762
|
+
name: 'statusline',
|
|
763
|
+
description: 'Generate dynamic statusline with v1 progress and system status',
|
|
764
|
+
options: [
|
|
765
|
+
{
|
|
766
|
+
name: 'json',
|
|
767
|
+
description: 'Output as JSON',
|
|
768
|
+
type: 'boolean',
|
|
769
|
+
default: false
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
name: 'compact',
|
|
773
|
+
description: 'Compact single-line output',
|
|
774
|
+
type: 'boolean',
|
|
775
|
+
default: false
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
name: 'no-color',
|
|
779
|
+
description: 'Disable ANSI colors',
|
|
780
|
+
type: 'boolean',
|
|
781
|
+
default: false
|
|
782
|
+
}
|
|
783
|
+
],
|
|
784
|
+
examples: [
|
|
785
|
+
{ command: 'monomind hooks statusline', description: 'Display full statusline' },
|
|
786
|
+
{ command: 'monomind hooks statusline --json', description: 'JSON output for hooks' },
|
|
787
|
+
{ command: 'monomind hooks statusline --compact', description: 'Single-line status' }
|
|
788
|
+
],
|
|
789
|
+
action: async (ctx) => {
|
|
790
|
+
const fs = await import('fs');
|
|
791
|
+
const path = await import('path');
|
|
792
|
+
const { execSync } = await import('child_process');
|
|
793
|
+
// Get learning stats from memory database
|
|
794
|
+
function getLearningStats() {
|
|
795
|
+
const memoryPaths = [
|
|
796
|
+
path.join(process.cwd(), '.swarm', 'memory.db'),
|
|
797
|
+
path.join(process.cwd(), '.claude', 'memory.db'),
|
|
798
|
+
];
|
|
799
|
+
let patterns = 0;
|
|
800
|
+
let sessions = 0;
|
|
801
|
+
let trajectories = 0;
|
|
802
|
+
for (const dbPath of memoryPaths) {
|
|
803
|
+
if (fs.existsSync(dbPath)) {
|
|
804
|
+
try {
|
|
805
|
+
const stats = fs.statSync(dbPath);
|
|
806
|
+
const sizeKB = stats.size / 1024;
|
|
807
|
+
patterns = Math.floor(sizeKB / 2);
|
|
808
|
+
sessions = Math.max(1, Math.floor(patterns / 10));
|
|
809
|
+
trajectories = Math.floor(patterns / 5);
|
|
810
|
+
break;
|
|
811
|
+
}
|
|
812
|
+
catch {
|
|
813
|
+
// Ignore
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
const sessionsPath = path.join(process.cwd(), '.claude', 'sessions');
|
|
818
|
+
if (fs.existsSync(sessionsPath)) {
|
|
819
|
+
try {
|
|
820
|
+
const sessionFiles = fs.readdirSync(sessionsPath).filter((f) => f.endsWith('.json'));
|
|
821
|
+
sessions = Math.max(sessions, sessionFiles.length);
|
|
822
|
+
}
|
|
823
|
+
catch {
|
|
824
|
+
// Ignore
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return { patterns, sessions, trajectories };
|
|
828
|
+
}
|
|
829
|
+
// Get v1 progress
|
|
830
|
+
function getv1Progress() {
|
|
831
|
+
const learning = getLearningStats();
|
|
832
|
+
let domainsCompleted = 0;
|
|
833
|
+
if (learning.patterns >= 500)
|
|
834
|
+
domainsCompleted = 5;
|
|
835
|
+
else if (learning.patterns >= 200)
|
|
836
|
+
domainsCompleted = 4;
|
|
837
|
+
else if (learning.patterns >= 100)
|
|
838
|
+
domainsCompleted = 3;
|
|
839
|
+
else if (learning.patterns >= 50)
|
|
840
|
+
domainsCompleted = 2;
|
|
841
|
+
else if (learning.patterns >= 10)
|
|
842
|
+
domainsCompleted = 1;
|
|
843
|
+
const totalDomains = 5;
|
|
844
|
+
const dddProgress = Math.min(100, Math.floor((domainsCompleted / totalDomains) * 100));
|
|
845
|
+
return { domainsCompleted, totalDomains, dddProgress, patternsLearned: learning.patterns, sessionsCompleted: learning.sessions };
|
|
846
|
+
}
|
|
847
|
+
// Get security status
|
|
848
|
+
function getSecurityStatus() {
|
|
849
|
+
const scanResultsPath = path.join(process.cwd(), '.claude', 'security-scans');
|
|
850
|
+
let cvesFixed = 0;
|
|
851
|
+
const totalCves = 3;
|
|
852
|
+
if (fs.existsSync(scanResultsPath)) {
|
|
853
|
+
try {
|
|
854
|
+
const scans = fs.readdirSync(scanResultsPath).filter((f) => f.endsWith('.json'));
|
|
855
|
+
cvesFixed = Math.min(totalCves, scans.length);
|
|
856
|
+
}
|
|
857
|
+
catch {
|
|
858
|
+
// Ignore
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
const auditPath = path.join(process.cwd(), '.swarm', 'security');
|
|
862
|
+
if (fs.existsSync(auditPath)) {
|
|
863
|
+
try {
|
|
864
|
+
const audits = fs.readdirSync(auditPath).filter((f) => f.includes('audit'));
|
|
865
|
+
cvesFixed = Math.min(totalCves, Math.max(cvesFixed, audits.length));
|
|
866
|
+
}
|
|
867
|
+
catch {
|
|
868
|
+
// Ignore
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
const status = cvesFixed >= totalCves ? 'CLEAN' : cvesFixed > 0 ? 'IN_PROGRESS' : 'PENDING';
|
|
872
|
+
return { status, cvesFixed, totalCves };
|
|
873
|
+
}
|
|
874
|
+
// Get swarm status
|
|
875
|
+
function getSwarmStatus() {
|
|
876
|
+
let activeAgents = 0;
|
|
877
|
+
let coordinationActive = false;
|
|
878
|
+
const maxAgents = 15;
|
|
879
|
+
const isWindows = process.platform === 'win32';
|
|
880
|
+
try {
|
|
881
|
+
const psCmd = isWindows
|
|
882
|
+
? 'tasklist /FI "IMAGENAME eq node.exe" 2>NUL | findstr /I /C:"node" >NUL && echo 1 || echo 0'
|
|
883
|
+
: 'ps aux 2>/dev/null | grep -c agentic-flow || echo "0"';
|
|
884
|
+
const ps = execSync(psCmd, { encoding: 'utf-8' });
|
|
885
|
+
const raw = parseInt(ps.trim());
|
|
886
|
+
activeAgents = Math.max(0, isWindows ? raw : raw - 1);
|
|
887
|
+
coordinationActive = activeAgents > 0;
|
|
888
|
+
}
|
|
889
|
+
catch {
|
|
890
|
+
// Ignore
|
|
891
|
+
}
|
|
892
|
+
return { activeAgents, maxAgents, coordinationActive };
|
|
893
|
+
}
|
|
894
|
+
// Get system metrics
|
|
895
|
+
function getSystemMetrics() {
|
|
896
|
+
let memoryMB = 0;
|
|
897
|
+
let subAgents = 0;
|
|
898
|
+
const learning = getLearningStats();
|
|
899
|
+
try {
|
|
900
|
+
memoryMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
|
|
901
|
+
}
|
|
902
|
+
catch {
|
|
903
|
+
// Ignore
|
|
904
|
+
}
|
|
905
|
+
// Calculate intelligence from multiple sources (matching statusline-generator.ts)
|
|
906
|
+
let intelligencePct = 0;
|
|
907
|
+
// 1. Check learning.json for REAL intelligence metrics first
|
|
908
|
+
const learningJsonPaths = [
|
|
909
|
+
path.join(process.cwd(), '.monomind', 'learning.json'),
|
|
910
|
+
path.join(process.cwd(), '.claude', '.monomind', 'learning.json'),
|
|
911
|
+
path.join(process.cwd(), '.swarm', 'learning.json'),
|
|
912
|
+
];
|
|
913
|
+
for (const lPath of learningJsonPaths) {
|
|
914
|
+
if (fs.existsSync(lPath)) {
|
|
915
|
+
try {
|
|
916
|
+
if (fs.statSync(lPath).size <= 524_288) {
|
|
917
|
+
const data = JSON.parse(fs.readFileSync(lPath, 'utf-8'));
|
|
918
|
+
if (data.intelligence?.score !== undefined) {
|
|
919
|
+
intelligencePct = Math.min(100, Math.floor(data.intelligence.score));
|
|
920
|
+
break;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
catch { /* ignore */ }
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
// 2. Fallback: calculate from patterns and vectors
|
|
928
|
+
if (intelligencePct === 0) {
|
|
929
|
+
const fromPatterns = learning.patterns > 0 ? Math.min(100, Math.floor(learning.patterns / 10)) : 0;
|
|
930
|
+
// Will be updated later with vector count
|
|
931
|
+
intelligencePct = fromPatterns;
|
|
932
|
+
}
|
|
933
|
+
// 3. Fallback: calculate maturity score from project indicators
|
|
934
|
+
if (intelligencePct === 0) {
|
|
935
|
+
let maturityScore = 0;
|
|
936
|
+
// Check for key project files/dirs
|
|
937
|
+
if (fs.existsSync(path.join(process.cwd(), '.claude')))
|
|
938
|
+
maturityScore += 15;
|
|
939
|
+
if (fs.existsSync(path.join(process.cwd(), '.monomind')))
|
|
940
|
+
maturityScore += 15;
|
|
941
|
+
if (fs.existsSync(path.join(process.cwd(), 'CLAUDE.md')))
|
|
942
|
+
maturityScore += 10;
|
|
943
|
+
if (fs.existsSync(path.join(process.cwd(), 'monomind.config.json')))
|
|
944
|
+
maturityScore += 10;
|
|
945
|
+
if (fs.existsSync(path.join(process.cwd(), '.swarm')))
|
|
946
|
+
maturityScore += 10;
|
|
947
|
+
// Check for test files
|
|
948
|
+
const testDirs = ['tests', '__tests__', 'test', 'v1/__tests__'];
|
|
949
|
+
for (const dir of testDirs) {
|
|
950
|
+
if (fs.existsSync(path.join(process.cwd(), dir))) {
|
|
951
|
+
maturityScore += 10;
|
|
952
|
+
break;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
// Check for hooks config
|
|
956
|
+
if (fs.existsSync(path.join(process.cwd(), '.claude', 'settings.json')))
|
|
957
|
+
maturityScore += 10;
|
|
958
|
+
intelligencePct = Math.min(100, maturityScore);
|
|
959
|
+
}
|
|
960
|
+
const contextPct = Math.min(100, Math.floor(learning.sessions * 5));
|
|
961
|
+
return { memoryMB, contextPct, intelligencePct, subAgents };
|
|
962
|
+
}
|
|
963
|
+
// Get user info
|
|
964
|
+
function getUserInfo() {
|
|
965
|
+
let name = 'user';
|
|
966
|
+
let gitBranch = '';
|
|
967
|
+
const modelName = 'Opus 4.6 (1M context)';
|
|
968
|
+
const isWindows = process.platform === 'win32';
|
|
969
|
+
try {
|
|
970
|
+
const nameCmd = isWindows
|
|
971
|
+
? 'git config user.name 2>NUL || echo user'
|
|
972
|
+
: 'git config user.name 2>/dev/null || echo "user"';
|
|
973
|
+
const branchCmd = isWindows
|
|
974
|
+
? 'git branch --show-current 2>NUL || echo.'
|
|
975
|
+
: 'git branch --show-current 2>/dev/null || echo ""';
|
|
976
|
+
name = execSync(nameCmd, { encoding: 'utf-8' }).trim();
|
|
977
|
+
gitBranch = execSync(branchCmd, { encoding: 'utf-8' }).trim();
|
|
978
|
+
if (gitBranch === '.')
|
|
979
|
+
gitBranch = '';
|
|
980
|
+
}
|
|
981
|
+
catch {
|
|
982
|
+
// Ignore
|
|
983
|
+
}
|
|
984
|
+
return { name, gitBranch, modelName };
|
|
985
|
+
}
|
|
986
|
+
// Collect all status
|
|
987
|
+
const progress = getv1Progress();
|
|
988
|
+
const security = getSecurityStatus();
|
|
989
|
+
const swarm = getSwarmStatus();
|
|
990
|
+
const system = getSystemMetrics();
|
|
991
|
+
const user = getUserInfo();
|
|
992
|
+
const statusData = {
|
|
993
|
+
user,
|
|
994
|
+
v1Progress: progress,
|
|
995
|
+
security,
|
|
996
|
+
swarm,
|
|
997
|
+
system,
|
|
998
|
+
timestamp: new Date().toISOString()
|
|
999
|
+
};
|
|
1000
|
+
// JSON output
|
|
1001
|
+
if (ctx.flags.json || ctx.flags.format === 'json') {
|
|
1002
|
+
output.printJson(statusData);
|
|
1003
|
+
return { success: true, data: statusData };
|
|
1004
|
+
}
|
|
1005
|
+
// Compact output
|
|
1006
|
+
if (ctx.flags.compact) {
|
|
1007
|
+
const line = `DDD:${progress.domainsCompleted}/${progress.totalDomains} CVE:${security.cvesFixed}/${security.totalCves} Swarm:${swarm.activeAgents}/${swarm.maxAgents} Ctx:${system.contextPct}% Int:${system.intelligencePct}%`;
|
|
1008
|
+
output.writeln(line);
|
|
1009
|
+
return { success: true, data: statusData };
|
|
1010
|
+
}
|
|
1011
|
+
// Full colored output
|
|
1012
|
+
const noColor = ctx.flags['no-color'] || ctx.flags.noColor;
|
|
1013
|
+
const c = noColor ? {
|
|
1014
|
+
reset: '', bold: '', dim: '', red: '', green: '', yellow: '', blue: '',
|
|
1015
|
+
purple: '', cyan: '', brightRed: '', brightGreen: '', brightYellow: '',
|
|
1016
|
+
brightBlue: '', brightPurple: '', brightCyan: '', brightWhite: ''
|
|
1017
|
+
} : {
|
|
1018
|
+
reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m', red: '\x1b[0;31m',
|
|
1019
|
+
green: '\x1b[0;32m', yellow: '\x1b[0;33m', blue: '\x1b[0;34m',
|
|
1020
|
+
purple: '\x1b[0;35m', cyan: '\x1b[0;36m', brightRed: '\x1b[1;31m',
|
|
1021
|
+
brightGreen: '\x1b[1;32m', brightYellow: '\x1b[1;33m', brightBlue: '\x1b[1;34m',
|
|
1022
|
+
brightPurple: '\x1b[1;35m', brightCyan: '\x1b[1;36m', brightWhite: '\x1b[1;37m'
|
|
1023
|
+
};
|
|
1024
|
+
// Progress bar helper
|
|
1025
|
+
const progressBar = (current, total) => {
|
|
1026
|
+
const filled = Math.round((current / total) * 5);
|
|
1027
|
+
const empty = 5 - filled;
|
|
1028
|
+
return '[' + '●'.repeat(filled) + '○'.repeat(empty) + ']';
|
|
1029
|
+
};
|
|
1030
|
+
// Generate lines
|
|
1031
|
+
let header = `${c.bold}${c.brightPurple}▊ Monomind ${c.reset}`;
|
|
1032
|
+
header += `${swarm.coordinationActive ? c.brightCyan : c.dim}● ${c.brightCyan}${user.name}${c.reset}`;
|
|
1033
|
+
if (user.gitBranch) {
|
|
1034
|
+
header += ` ${c.dim}│${c.reset} ${c.brightBlue}⎇ ${user.gitBranch}${c.reset}`;
|
|
1035
|
+
}
|
|
1036
|
+
header += ` ${c.dim}│${c.reset} ${c.purple}${user.modelName}${c.reset}`;
|
|
1037
|
+
const separator = `${c.dim}─────────────────────────────────────────────────────${c.reset}`;
|
|
1038
|
+
// Get hooks stats
|
|
1039
|
+
const hooksStats = { enabled: 0, total: 17 };
|
|
1040
|
+
const settingsPath = path.join(process.cwd(), '.claude', 'settings.json');
|
|
1041
|
+
if (fs.existsSync(settingsPath)) {
|
|
1042
|
+
try {
|
|
1043
|
+
if (fs.statSync(settingsPath).size <= 524_288) {
|
|
1044
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
1045
|
+
if (settings.hooks) {
|
|
1046
|
+
hooksStats.enabled = Object.values(settings.hooks).filter((h) => h && typeof h === 'object').length;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
catch { /* ignore */ }
|
|
1051
|
+
}
|
|
1052
|
+
// Get AgentDB stats (matching statusline-generator.ts paths)
|
|
1053
|
+
const agentdbStats = { vectorCount: 0, dbSizeKB: 0, hasHnsw: false };
|
|
1054
|
+
// Check for direct database files first
|
|
1055
|
+
const dbPaths = [
|
|
1056
|
+
path.join(process.cwd(), '.swarm', 'memory.db'),
|
|
1057
|
+
path.join(process.cwd(), '.monomind', 'memory.db'),
|
|
1058
|
+
path.join(process.cwd(), '.claude', 'memory.db'),
|
|
1059
|
+
path.join(process.cwd(), 'data', 'memory.db'),
|
|
1060
|
+
path.join(process.cwd(), 'memory.db'),
|
|
1061
|
+
path.join(process.cwd(), '.agentdb', 'memory.db'),
|
|
1062
|
+
path.join(process.cwd(), '.monomind', 'memory', 'agentdb.db'),
|
|
1063
|
+
];
|
|
1064
|
+
for (const dbPath of dbPaths) {
|
|
1065
|
+
if (fs.existsSync(dbPath)) {
|
|
1066
|
+
try {
|
|
1067
|
+
const stats = fs.statSync(dbPath);
|
|
1068
|
+
agentdbStats.dbSizeKB = Math.round(stats.size / 1024);
|
|
1069
|
+
agentdbStats.vectorCount = Math.floor(agentdbStats.dbSizeKB / 2);
|
|
1070
|
+
agentdbStats.hasHnsw = agentdbStats.vectorCount > 100;
|
|
1071
|
+
break;
|
|
1072
|
+
}
|
|
1073
|
+
catch { /* ignore */ }
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
// Check for AgentDB directories if no direct db found
|
|
1077
|
+
if (agentdbStats.vectorCount === 0) {
|
|
1078
|
+
const agentdbDirs = [
|
|
1079
|
+
path.join(process.cwd(), '.monomind', 'agentdb'),
|
|
1080
|
+
path.join(process.cwd(), '.swarm', 'agentdb'),
|
|
1081
|
+
path.join(process.cwd(), 'data', 'agentdb'),
|
|
1082
|
+
path.join(process.cwd(), '.agentdb'),
|
|
1083
|
+
];
|
|
1084
|
+
for (const dir of agentdbDirs) {
|
|
1085
|
+
if (fs.existsSync(dir)) {
|
|
1086
|
+
try {
|
|
1087
|
+
const files = fs.readdirSync(dir);
|
|
1088
|
+
for (const f of files) {
|
|
1089
|
+
if (f.endsWith('.db') || f.endsWith('.sqlite')) {
|
|
1090
|
+
const filePath = path.join(dir, f);
|
|
1091
|
+
const fileStat = fs.statSync(filePath);
|
|
1092
|
+
agentdbStats.dbSizeKB += Math.round(fileStat.size / 1024);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
agentdbStats.vectorCount = Math.floor(agentdbStats.dbSizeKB / 2);
|
|
1096
|
+
agentdbStats.hasHnsw = agentdbStats.vectorCount > 100;
|
|
1097
|
+
if (agentdbStats.vectorCount > 0)
|
|
1098
|
+
break;
|
|
1099
|
+
}
|
|
1100
|
+
catch { /* ignore */ }
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
// Check for HNSW index files
|
|
1105
|
+
const hnswPaths = [
|
|
1106
|
+
path.join(process.cwd(), '.monomind', 'hnsw'),
|
|
1107
|
+
path.join(process.cwd(), '.swarm', 'hnsw'),
|
|
1108
|
+
path.join(process.cwd(), 'data', 'hnsw'),
|
|
1109
|
+
];
|
|
1110
|
+
for (const hnswPath of hnswPaths) {
|
|
1111
|
+
if (fs.existsSync(hnswPath)) {
|
|
1112
|
+
agentdbStats.hasHnsw = true;
|
|
1113
|
+
try {
|
|
1114
|
+
const hnswFiles = fs.readdirSync(hnswPath);
|
|
1115
|
+
const indexFile = hnswFiles.find(f => f.endsWith('.index'));
|
|
1116
|
+
if (indexFile) {
|
|
1117
|
+
const indexStat = fs.statSync(path.join(hnswPath, indexFile));
|
|
1118
|
+
const hnswVectors = Math.floor(indexStat.size / 512);
|
|
1119
|
+
agentdbStats.vectorCount = Math.max(agentdbStats.vectorCount, hnswVectors);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
catch { /* ignore */ }
|
|
1123
|
+
break;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
// Check for vectors.json file
|
|
1127
|
+
const vectorsPath = path.join(process.cwd(), '.monomind', 'vectors.json');
|
|
1128
|
+
if (fs.existsSync(vectorsPath) && agentdbStats.vectorCount === 0) {
|
|
1129
|
+
try {
|
|
1130
|
+
if (fs.statSync(vectorsPath).size <= 8_388_608) {
|
|
1131
|
+
const data = JSON.parse(fs.readFileSync(vectorsPath, 'utf-8'));
|
|
1132
|
+
if (Array.isArray(data)) {
|
|
1133
|
+
agentdbStats.vectorCount = data.length;
|
|
1134
|
+
}
|
|
1135
|
+
else if (data.vectors) {
|
|
1136
|
+
agentdbStats.vectorCount = Object.keys(data.vectors).length;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
catch { /* ignore */ }
|
|
1141
|
+
}
|
|
1142
|
+
// Get test stats
|
|
1143
|
+
const testStats = { testFiles: 0, testCases: 0 };
|
|
1144
|
+
const testPaths = ['tests', '__tests__', 'test', 'spec'];
|
|
1145
|
+
for (const testPath of testPaths) {
|
|
1146
|
+
const fullPath = path.join(process.cwd(), testPath);
|
|
1147
|
+
if (fs.existsSync(fullPath)) {
|
|
1148
|
+
try {
|
|
1149
|
+
const files = fs.readdirSync(fullPath, { recursive: true });
|
|
1150
|
+
testStats.testFiles = files.filter((f) => /\.(test|spec)\.(ts|js|tsx|jsx)$/.test(f)).length;
|
|
1151
|
+
testStats.testCases = testStats.testFiles * 28; // Estimate
|
|
1152
|
+
}
|
|
1153
|
+
catch { /* ignore */ }
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
// Get MCP stats
|
|
1157
|
+
const mcpStats = { enabled: 0, total: 0 };
|
|
1158
|
+
const mcpPath = path.join(process.cwd(), '.mcp.json');
|
|
1159
|
+
if (fs.existsSync(mcpPath)) {
|
|
1160
|
+
try {
|
|
1161
|
+
const mcp = JSON.parse(fs.readFileSync(mcpPath, 'utf-8'));
|
|
1162
|
+
if (mcp.mcpServers) {
|
|
1163
|
+
mcpStats.total = Object.keys(mcp.mcpServers).length;
|
|
1164
|
+
mcpStats.enabled = mcpStats.total;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
catch { /* ignore */ }
|
|
1168
|
+
}
|
|
1169
|
+
const domainsColor = progress.domainsCompleted >= 3 ? c.brightGreen : progress.domainsCompleted > 0 ? c.yellow : c.red;
|
|
1170
|
+
// Dynamic perf indicator based on patterns/HNSW
|
|
1171
|
+
let perfIndicator = `${c.dim}⚡ HNSW: idle${c.reset}`;
|
|
1172
|
+
if (agentdbStats.hasHnsw && agentdbStats.vectorCount > 0) {
|
|
1173
|
+
perfIndicator = `${c.brightGreen}⚡ HNSW ${agentdbStats.vectorCount.toLocaleString()} vec${c.reset}`;
|
|
1174
|
+
}
|
|
1175
|
+
else if (progress.patternsLearned > 0) {
|
|
1176
|
+
const patternsK = progress.patternsLearned >= 1000 ? `${(progress.patternsLearned / 1000).toFixed(1)}k` : String(progress.patternsLearned);
|
|
1177
|
+
perfIndicator = `${c.brightYellow}📚 ${patternsK} patterns${c.reset}`;
|
|
1178
|
+
}
|
|
1179
|
+
const line1 = `${c.brightCyan}🏗️ DDD Domains${c.reset} ${progressBar(progress.domainsCompleted, progress.totalDomains)} ` +
|
|
1180
|
+
`${domainsColor}${progress.domainsCompleted}${c.reset}/${c.brightWhite}${progress.totalDomains}${c.reset} ` +
|
|
1181
|
+
perfIndicator;
|
|
1182
|
+
const swarmIndicator = swarm.coordinationActive ? `${c.brightGreen}◉${c.reset}` : `${c.dim}○${c.reset}`;
|
|
1183
|
+
const agentsColor = swarm.activeAgents > 0 ? c.brightGreen : c.red;
|
|
1184
|
+
const securityIcon = security.status === 'CLEAN' ? '🟢' : security.status === 'IN_PROGRESS' ? '🟡' : '🔴';
|
|
1185
|
+
const securityColor = security.status === 'CLEAN' ? c.brightGreen : security.status === 'IN_PROGRESS' ? c.brightYellow : c.brightRed;
|
|
1186
|
+
const hooksColor = hooksStats.enabled > 0 ? c.brightGreen : c.dim;
|
|
1187
|
+
const line2 = `${c.brightYellow}🤖 Swarm${c.reset} ${swarmIndicator} [${agentsColor}${String(swarm.activeAgents).padStart(2)}${c.reset}/${c.brightWhite}${swarm.maxAgents}${c.reset}] ` +
|
|
1188
|
+
`${c.brightPurple}👥 ${system.subAgents}${c.reset} ` +
|
|
1189
|
+
`${c.brightBlue}🪝 ${hooksColor}${hooksStats.enabled}${c.reset}/${c.brightWhite}${hooksStats.total}${c.reset} ` +
|
|
1190
|
+
`${securityIcon} ${securityColor}CVE ${security.cvesFixed}${c.reset}/${c.brightWhite}${security.totalCves}${c.reset} ` +
|
|
1191
|
+
`${c.brightCyan}💾 ${system.memoryMB}MB${c.reset} ` +
|
|
1192
|
+
`${c.brightPurple}🧠 ${String(system.intelligencePct).padStart(3)}%${c.reset}`;
|
|
1193
|
+
const dddColor = progress.dddProgress >= 50 ? c.brightGreen : progress.dddProgress > 0 ? c.yellow : c.red;
|
|
1194
|
+
const line3 = `${c.brightPurple}🔧 Architecture${c.reset} ` +
|
|
1195
|
+
`${c.cyan}ADRs${c.reset} ${c.dim}●0/0${c.reset} ${c.dim}│${c.reset} ` +
|
|
1196
|
+
`${c.cyan}DDD${c.reset} ${dddColor}●${String(progress.dddProgress).padStart(3)}%${c.reset} ${c.dim}│${c.reset} ` +
|
|
1197
|
+
`${c.cyan}Security${c.reset} ${securityColor}●${security.status}${c.reset}`;
|
|
1198
|
+
const vectorColor = agentdbStats.vectorCount > 0 ? c.brightGreen : c.dim;
|
|
1199
|
+
const testColor = testStats.testFiles > 0 ? c.brightGreen : c.dim;
|
|
1200
|
+
const mcpColor = mcpStats.enabled > 0 ? c.brightGreen : c.dim;
|
|
1201
|
+
const sizeDisplay = agentdbStats.dbSizeKB >= 1024 ? `${(agentdbStats.dbSizeKB / 1024).toFixed(1)}MB` : `${agentdbStats.dbSizeKB}KB`;
|
|
1202
|
+
const hnswIndicator = agentdbStats.hasHnsw ? `${c.brightGreen}⚡${c.reset}` : '';
|
|
1203
|
+
const line4 = `${c.brightCyan}📊 AgentDB${c.reset} ` +
|
|
1204
|
+
`${c.cyan}Vectors${c.reset} ${vectorColor}●${agentdbStats.vectorCount}${hnswIndicator}${c.reset} ${c.dim}│${c.reset} ` +
|
|
1205
|
+
`${c.cyan}Size${c.reset} ${c.brightWhite}${sizeDisplay}${c.reset} ${c.dim}│${c.reset} ` +
|
|
1206
|
+
`${c.cyan}Tests${c.reset} ${testColor}●${testStats.testFiles}${c.reset} ${c.dim}(${testStats.testCases} cases)${c.reset} ${c.dim}│${c.reset} ` +
|
|
1207
|
+
`${c.cyan}MCP${c.reset} ${mcpColor}●${mcpStats.enabled}/${mcpStats.total}${c.reset}`;
|
|
1208
|
+
output.writeln(header);
|
|
1209
|
+
output.writeln(separator);
|
|
1210
|
+
output.writeln(line1);
|
|
1211
|
+
output.writeln(line2);
|
|
1212
|
+
output.writeln(line3);
|
|
1213
|
+
output.writeln(line4);
|
|
1214
|
+
return { success: true, data: statusData };
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
//# sourceMappingURL=hooks-coverage-commands.js.map
|