@timmeck/brain 1.8.0 → 1.8.2
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/BRAIN_PLAN.md +3324 -3324
- package/LICENSE +21 -21
- package/dist/api/server.d.ts +4 -0
- package/dist/api/server.js +73 -0
- package/dist/api/server.js.map +1 -1
- package/dist/brain.js +2 -1
- package/dist/brain.js.map +1 -1
- package/dist/cli/commands/dashboard.js +606 -572
- package/dist/cli/commands/dashboard.js.map +1 -1
- package/dist/dashboard/server.js +25 -25
- package/dist/db/migrations/001_core_schema.js +115 -115
- package/dist/db/migrations/002_learning_schema.js +33 -33
- package/dist/db/migrations/003_code_schema.js +48 -48
- package/dist/db/migrations/004_synapses_schema.js +52 -52
- package/dist/db/migrations/005_fts_indexes.js +73 -73
- package/dist/db/migrations/007_feedback.js +8 -8
- package/dist/db/migrations/008_git_integration.js +33 -33
- package/dist/db/migrations/009_embeddings.js +3 -3
- package/dist/db/repositories/antipattern.repository.js +3 -3
- package/dist/db/repositories/code-module.repository.js +32 -32
- package/dist/db/repositories/notification.repository.js +3 -3
- package/dist/db/repositories/project.repository.js +21 -21
- package/dist/db/repositories/rule.repository.js +24 -24
- package/dist/db/repositories/solution.repository.js +50 -50
- package/dist/db/repositories/synapse.repository.js +18 -18
- package/dist/db/repositories/terminal.repository.js +24 -24
- package/dist/embeddings/engine.d.ts +2 -2
- package/dist/embeddings/engine.js +17 -4
- package/dist/embeddings/engine.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/ipc/server.d.ts +8 -0
- package/dist/ipc/server.js +67 -1
- package/dist/ipc/server.js.map +1 -1
- package/dist/matching/error-matcher.js +5 -5
- package/dist/matching/fingerprint.js +6 -1
- package/dist/matching/fingerprint.js.map +1 -1
- package/dist/mcp/http-server.js +8 -2
- package/dist/mcp/http-server.js.map +1 -1
- package/dist/services/code.service.d.ts +3 -0
- package/dist/services/code.service.js +33 -4
- package/dist/services/code.service.js.map +1 -1
- package/dist/services/error.service.js +4 -3
- package/dist/services/error.service.js.map +1 -1
- package/dist/services/git.service.js +14 -14
- package/package.json +49 -49
- package/src/api/server.ts +395 -321
- package/src/brain.ts +266 -265
- package/src/cli/colors.ts +116 -116
- package/src/cli/commands/config.ts +169 -169
- package/src/cli/commands/dashboard.ts +755 -720
- package/src/cli/commands/doctor.ts +118 -118
- package/src/cli/commands/explain.ts +83 -83
- package/src/cli/commands/export.ts +31 -31
- package/src/cli/commands/import.ts +199 -199
- package/src/cli/commands/insights.ts +65 -65
- package/src/cli/commands/learn.ts +24 -24
- package/src/cli/commands/modules.ts +53 -53
- package/src/cli/commands/network.ts +67 -67
- package/src/cli/commands/projects.ts +42 -42
- package/src/cli/commands/query.ts +120 -120
- package/src/cli/commands/start.ts +62 -62
- package/src/cli/commands/status.ts +75 -75
- package/src/cli/commands/stop.ts +34 -34
- package/src/cli/ipc-helper.ts +22 -22
- package/src/cli/update-check.ts +63 -63
- package/src/code/fingerprint.ts +87 -87
- package/src/code/parsers/generic.ts +29 -29
- package/src/code/parsers/python.ts +54 -54
- package/src/code/parsers/typescript.ts +65 -65
- package/src/code/registry.ts +60 -60
- package/src/dashboard/server.ts +142 -142
- package/src/db/connection.ts +22 -22
- package/src/db/migrations/001_core_schema.ts +120 -120
- package/src/db/migrations/002_learning_schema.ts +38 -38
- package/src/db/migrations/003_code_schema.ts +53 -53
- package/src/db/migrations/004_synapses_schema.ts +57 -57
- package/src/db/migrations/005_fts_indexes.ts +78 -78
- package/src/db/migrations/006_synapses_phase3.ts +17 -17
- package/src/db/migrations/007_feedback.ts +13 -13
- package/src/db/migrations/008_git_integration.ts +38 -38
- package/src/db/migrations/009_embeddings.ts +8 -8
- package/src/db/repositories/antipattern.repository.ts +66 -66
- package/src/db/repositories/code-module.repository.ts +142 -142
- package/src/db/repositories/notification.repository.ts +66 -66
- package/src/db/repositories/project.repository.ts +93 -93
- package/src/db/repositories/rule.repository.ts +108 -108
- package/src/db/repositories/solution.repository.ts +154 -154
- package/src/db/repositories/synapse.repository.ts +153 -153
- package/src/db/repositories/terminal.repository.ts +101 -101
- package/src/embeddings/engine.ts +238 -217
- package/src/index.ts +63 -63
- package/src/ipc/client.ts +118 -118
- package/src/ipc/protocol.ts +35 -35
- package/src/ipc/router.ts +133 -133
- package/src/ipc/server.ts +176 -110
- package/src/learning/decay.ts +46 -46
- package/src/learning/pattern-extractor.ts +90 -90
- package/src/learning/rule-generator.ts +74 -74
- package/src/matching/error-matcher.ts +5 -5
- package/src/matching/fingerprint.ts +34 -29
- package/src/matching/similarity.ts +61 -61
- package/src/matching/tfidf.ts +74 -74
- package/src/matching/tokenizer.ts +41 -41
- package/src/mcp/auto-detect.ts +93 -93
- package/src/mcp/http-server.ts +140 -137
- package/src/mcp/server.ts +73 -73
- package/src/parsing/error-parser.ts +28 -28
- package/src/parsing/parsers/compiler.ts +93 -93
- package/src/parsing/parsers/generic.ts +28 -28
- package/src/parsing/parsers/go.ts +97 -97
- package/src/parsing/parsers/node.ts +69 -69
- package/src/parsing/parsers/python.ts +62 -62
- package/src/parsing/parsers/rust.ts +50 -50
- package/src/parsing/parsers/shell.ts +42 -42
- package/src/parsing/types.ts +47 -47
- package/src/research/gap-analyzer.ts +135 -135
- package/src/research/insight-generator.ts +123 -123
- package/src/research/research-engine.ts +116 -116
- package/src/research/synergy-detector.ts +126 -126
- package/src/research/template-extractor.ts +130 -130
- package/src/research/trend-analyzer.ts +127 -127
- package/src/services/code.service.ts +271 -238
- package/src/services/error.service.ts +4 -3
- package/src/services/git.service.ts +132 -132
- package/src/services/notification.service.ts +41 -41
- package/src/services/synapse.service.ts +59 -59
- package/src/services/terminal.service.ts +81 -81
- package/src/synapses/activation.ts +80 -80
- package/src/synapses/decay.ts +38 -38
- package/src/synapses/hebbian.ts +69 -69
- package/src/synapses/pathfinder.ts +81 -81
- package/src/synapses/synapse-manager.ts +109 -109
- package/src/types/code.types.ts +52 -52
- package/src/types/error.types.ts +67 -67
- package/src/types/ipc.types.ts +8 -8
- package/src/types/mcp.types.ts +53 -53
- package/src/types/research.types.ts +28 -28
- package/src/types/solution.types.ts +30 -30
- package/src/utils/events.ts +45 -45
- package/src/utils/hash.ts +5 -5
- package/src/utils/logger.ts +48 -48
- package/src/utils/paths.ts +19 -19
- package/tests/e2e/test_code_intelligence.py +1015 -0
- package/tests/e2e/test_error_memory.py +451 -0
- package/tests/e2e/test_full_integration.py +534 -0
- package/tests/fixtures/code-modules/modules.ts +83 -83
- package/tests/fixtures/errors/go.ts +9 -9
- package/tests/fixtures/errors/node.ts +24 -24
- package/tests/fixtures/errors/python.ts +21 -21
- package/tests/fixtures/errors/rust.ts +25 -25
- package/tests/fixtures/errors/shell.ts +15 -15
- package/tests/fixtures/solutions/solutions.ts +27 -27
- package/tests/helpers/setup-db.ts +52 -52
- package/tests/integration/code-flow.test.ts +86 -86
- package/tests/integration/error-flow.test.ts +83 -83
- package/tests/integration/ipc-flow.test.ts +166 -166
- package/tests/integration/learning-cycle.test.ts +82 -82
- package/tests/integration/synapse-flow.test.ts +117 -117
- package/tests/unit/code/analyzer.test.ts +58 -58
- package/tests/unit/code/fingerprint.test.ts +51 -51
- package/tests/unit/code/scorer.test.ts +55 -55
- package/tests/unit/learning/confidence-scorer.test.ts +60 -60
- package/tests/unit/learning/decay.test.ts +45 -45
- package/tests/unit/learning/pattern-extractor.test.ts +50 -50
- package/tests/unit/matching/error-matcher.test.ts +69 -69
- package/tests/unit/matching/fingerprint.test.ts +47 -47
- package/tests/unit/matching/similarity.test.ts +65 -65
- package/tests/unit/matching/tfidf.test.ts +71 -71
- package/tests/unit/matching/tokenizer.test.ts +83 -83
- package/tests/unit/parsing/parsers.test.ts +113 -113
- package/tests/unit/research/gap-analyzer.test.ts +45 -45
- package/tests/unit/research/trend-analyzer.test.ts +45 -45
- package/tests/unit/synapses/activation.test.ts +80 -80
- package/tests/unit/synapses/decay.test.ts +27 -27
- package/tests/unit/synapses/hebbian.test.ts +96 -96
- package/tests/unit/synapses/pathfinder.test.ts +72 -72
- package/tsconfig.json +18 -18
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import { getDataDir } from '../../utils/paths.js';
|
|
6
|
-
import { IpcClient } from '../../ipc/client.js';
|
|
7
|
-
import { getPipeName } from '../../utils/paths.js';
|
|
8
|
-
import { c, icons, header, divider } from '../colors.js';
|
|
9
|
-
|
|
10
|
-
function pass(label: string, detail?: string): void {
|
|
11
|
-
const extra = detail ? ` ${c.dim(detail)}` : '';
|
|
12
|
-
console.log(` ${c.green(icons.check)} ${label}${extra}`);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function fail(label: string, detail?: string): void {
|
|
16
|
-
const extra = detail ? ` ${c.dim(detail)}` : '';
|
|
17
|
-
console.log(` ${c.red(icons.cross)} ${label}${extra}`);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function doctorCommand(): Command {
|
|
21
|
-
return new Command('doctor')
|
|
22
|
-
.description('Check Brain health: daemon, DB, MCP, hooks')
|
|
23
|
-
.action(async () => {
|
|
24
|
-
console.log(header('Brain Doctor', icons.brain));
|
|
25
|
-
console.log();
|
|
26
|
-
|
|
27
|
-
let allGood = true;
|
|
28
|
-
|
|
29
|
-
// 1. Daemon running?
|
|
30
|
-
const pidPath = path.join(getDataDir(), 'brain.pid');
|
|
31
|
-
let daemonRunning = false;
|
|
32
|
-
if (fs.existsSync(pidPath)) {
|
|
33
|
-
const pid = parseInt(fs.readFileSync(pidPath, 'utf8').trim(), 10);
|
|
34
|
-
try {
|
|
35
|
-
process.kill(pid, 0);
|
|
36
|
-
daemonRunning = true;
|
|
37
|
-
pass('Daemon running', `PID ${pid}`);
|
|
38
|
-
} catch {
|
|
39
|
-
fail('Daemon not running', 'stale PID file');
|
|
40
|
-
allGood = false;
|
|
41
|
-
}
|
|
42
|
-
} else {
|
|
43
|
-
fail('Daemon not running', 'no PID file');
|
|
44
|
-
allGood = false;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// 2. DB reachable? (only if daemon running)
|
|
48
|
-
if (daemonRunning) {
|
|
49
|
-
const client = new IpcClient(getPipeName(), 3000);
|
|
50
|
-
try {
|
|
51
|
-
await client.connect();
|
|
52
|
-
await client.request('analytics.summary', {});
|
|
53
|
-
pass('Database reachable');
|
|
54
|
-
} catch {
|
|
55
|
-
fail('Database not reachable');
|
|
56
|
-
allGood = false;
|
|
57
|
-
} finally {
|
|
58
|
-
client.disconnect();
|
|
59
|
-
}
|
|
60
|
-
} else {
|
|
61
|
-
fail('Database not reachable', 'daemon not running');
|
|
62
|
-
allGood = false;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// 3. MCP configured?
|
|
66
|
-
const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
|
|
67
|
-
let mcpConfigured = false;
|
|
68
|
-
try {
|
|
69
|
-
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
70
|
-
if (settings.mcpServers?.brain || settings.mcpServers?.['brain-mcp']) {
|
|
71
|
-
mcpConfigured = true;
|
|
72
|
-
pass('MCP server configured');
|
|
73
|
-
} else {
|
|
74
|
-
fail('MCP server not configured', `edit ${settingsPath}`);
|
|
75
|
-
allGood = false;
|
|
76
|
-
}
|
|
77
|
-
} catch {
|
|
78
|
-
fail('MCP server not configured', 'settings.json not found');
|
|
79
|
-
allGood = false;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// 4. Hook active?
|
|
83
|
-
try {
|
|
84
|
-
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
85
|
-
const hooks = settings.hooks;
|
|
86
|
-
const hasPostToolUse = hooks?.PostToolUse?.some(
|
|
87
|
-
(h: { command?: string }) => h.command?.includes('brain') || h.command?.includes('post-tool-use'),
|
|
88
|
-
);
|
|
89
|
-
if (hasPostToolUse) {
|
|
90
|
-
pass('Auto-detect hook active');
|
|
91
|
-
} else {
|
|
92
|
-
fail('Auto-detect hook not configured', 'errors won\'t be tracked automatically');
|
|
93
|
-
allGood = false;
|
|
94
|
-
}
|
|
95
|
-
} catch {
|
|
96
|
-
fail('Auto-detect hook not configured');
|
|
97
|
-
allGood = false;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// 5. DB file size
|
|
101
|
-
const dbPath = path.join(getDataDir(), 'brain.db');
|
|
102
|
-
try {
|
|
103
|
-
const stat = fs.statSync(dbPath);
|
|
104
|
-
pass('Database file', `${(stat.size / 1024 / 1024).toFixed(1)} MB at ${dbPath}`);
|
|
105
|
-
} catch {
|
|
106
|
-
fail('Database file not found');
|
|
107
|
-
allGood = false;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
console.log();
|
|
111
|
-
if (allGood) {
|
|
112
|
-
console.log(` ${icons.ok} ${c.success('All checks passed!')}`);
|
|
113
|
-
} else {
|
|
114
|
-
console.log(` ${icons.warn} ${c.warn('Some checks failed.')} Run ${c.cyan('brain start')} and check your MCP config.`);
|
|
115
|
-
}
|
|
116
|
-
console.log(`\n${divider()}`);
|
|
117
|
-
});
|
|
118
|
-
}
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import { getDataDir } from '../../utils/paths.js';
|
|
6
|
+
import { IpcClient } from '../../ipc/client.js';
|
|
7
|
+
import { getPipeName } from '../../utils/paths.js';
|
|
8
|
+
import { c, icons, header, divider } from '../colors.js';
|
|
9
|
+
|
|
10
|
+
function pass(label: string, detail?: string): void {
|
|
11
|
+
const extra = detail ? ` ${c.dim(detail)}` : '';
|
|
12
|
+
console.log(` ${c.green(icons.check)} ${label}${extra}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function fail(label: string, detail?: string): void {
|
|
16
|
+
const extra = detail ? ` ${c.dim(detail)}` : '';
|
|
17
|
+
console.log(` ${c.red(icons.cross)} ${label}${extra}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function doctorCommand(): Command {
|
|
21
|
+
return new Command('doctor')
|
|
22
|
+
.description('Check Brain health: daemon, DB, MCP, hooks')
|
|
23
|
+
.action(async () => {
|
|
24
|
+
console.log(header('Brain Doctor', icons.brain));
|
|
25
|
+
console.log();
|
|
26
|
+
|
|
27
|
+
let allGood = true;
|
|
28
|
+
|
|
29
|
+
// 1. Daemon running?
|
|
30
|
+
const pidPath = path.join(getDataDir(), 'brain.pid');
|
|
31
|
+
let daemonRunning = false;
|
|
32
|
+
if (fs.existsSync(pidPath)) {
|
|
33
|
+
const pid = parseInt(fs.readFileSync(pidPath, 'utf8').trim(), 10);
|
|
34
|
+
try {
|
|
35
|
+
process.kill(pid, 0);
|
|
36
|
+
daemonRunning = true;
|
|
37
|
+
pass('Daemon running', `PID ${pid}`);
|
|
38
|
+
} catch {
|
|
39
|
+
fail('Daemon not running', 'stale PID file');
|
|
40
|
+
allGood = false;
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
fail('Daemon not running', 'no PID file');
|
|
44
|
+
allGood = false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 2. DB reachable? (only if daemon running)
|
|
48
|
+
if (daemonRunning) {
|
|
49
|
+
const client = new IpcClient(getPipeName(), 3000);
|
|
50
|
+
try {
|
|
51
|
+
await client.connect();
|
|
52
|
+
await client.request('analytics.summary', {});
|
|
53
|
+
pass('Database reachable');
|
|
54
|
+
} catch {
|
|
55
|
+
fail('Database not reachable');
|
|
56
|
+
allGood = false;
|
|
57
|
+
} finally {
|
|
58
|
+
client.disconnect();
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
fail('Database not reachable', 'daemon not running');
|
|
62
|
+
allGood = false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 3. MCP configured?
|
|
66
|
+
const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
|
|
67
|
+
let mcpConfigured = false;
|
|
68
|
+
try {
|
|
69
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
70
|
+
if (settings.mcpServers?.brain || settings.mcpServers?.['brain-mcp']) {
|
|
71
|
+
mcpConfigured = true;
|
|
72
|
+
pass('MCP server configured');
|
|
73
|
+
} else {
|
|
74
|
+
fail('MCP server not configured', `edit ${settingsPath}`);
|
|
75
|
+
allGood = false;
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
fail('MCP server not configured', 'settings.json not found');
|
|
79
|
+
allGood = false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 4. Hook active?
|
|
83
|
+
try {
|
|
84
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
85
|
+
const hooks = settings.hooks;
|
|
86
|
+
const hasPostToolUse = hooks?.PostToolUse?.some(
|
|
87
|
+
(h: { command?: string }) => h.command?.includes('brain') || h.command?.includes('post-tool-use'),
|
|
88
|
+
);
|
|
89
|
+
if (hasPostToolUse) {
|
|
90
|
+
pass('Auto-detect hook active');
|
|
91
|
+
} else {
|
|
92
|
+
fail('Auto-detect hook not configured', 'errors won\'t be tracked automatically');
|
|
93
|
+
allGood = false;
|
|
94
|
+
}
|
|
95
|
+
} catch {
|
|
96
|
+
fail('Auto-detect hook not configured');
|
|
97
|
+
allGood = false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 5. DB file size
|
|
101
|
+
const dbPath = path.join(getDataDir(), 'brain.db');
|
|
102
|
+
try {
|
|
103
|
+
const stat = fs.statSync(dbPath);
|
|
104
|
+
pass('Database file', `${(stat.size / 1024 / 1024).toFixed(1)} MB at ${dbPath}`);
|
|
105
|
+
} catch {
|
|
106
|
+
fail('Database file not found');
|
|
107
|
+
allGood = false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
console.log();
|
|
111
|
+
if (allGood) {
|
|
112
|
+
console.log(` ${icons.ok} ${c.success('All checks passed!')}`);
|
|
113
|
+
} else {
|
|
114
|
+
console.log(` ${icons.warn} ${c.warn('Some checks failed.')} Run ${c.cyan('brain start')} and check your MCP config.`);
|
|
115
|
+
}
|
|
116
|
+
console.log(`\n${divider()}`);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { withIpc } from '../ipc-helper.js';
|
|
3
|
-
import { c, icons } from '../colors.js';
|
|
4
|
-
|
|
5
|
-
export function explainCommand(): Command {
|
|
6
|
-
return new Command('explain')
|
|
7
|
-
.description('Show everything Brain knows about an error')
|
|
8
|
-
.argument('<errorId>', 'Error ID to explain')
|
|
9
|
-
.action(async (errorId) => {
|
|
10
|
-
const id = parseInt(errorId, 10);
|
|
11
|
-
if (isNaN(id)) {
|
|
12
|
-
console.error(`${icons.error} Invalid error ID: ${errorId}`);
|
|
13
|
-
process.exit(1);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
await withIpc(async (client) => {
|
|
17
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
-
const result: any = await client.request('analytics.explain', { errorId: id });
|
|
19
|
-
|
|
20
|
-
if (!result.error) {
|
|
21
|
-
console.error(`${icons.error} Error #${id} not found.`);
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const err = result.error;
|
|
26
|
-
console.log();
|
|
27
|
-
console.log(`${icons.brain} ${c.heading(`Error #${err.id} — ${err.type}`)}`);
|
|
28
|
-
console.log(`${c.dim('─'.repeat(60))}`);
|
|
29
|
-
console.log(` ${c.label('Message:')} ${err.message}`);
|
|
30
|
-
console.log(` ${c.label('File:')} ${err.file_path ?? 'unknown'}`);
|
|
31
|
-
console.log(` ${c.label('Context:')} ${err.context ?? 'none'}`);
|
|
32
|
-
console.log(` ${c.label('Seen:')} ${err.occurrence_count}x (first: ${err.first_seen}, last: ${err.last_seen})`);
|
|
33
|
-
console.log(` ${c.label('Resolved:')} ${err.resolved ? c.success('Yes') : c.error('No')}`);
|
|
34
|
-
console.log(` ${c.label('Synapses:')} ${result.synapseConnections} connections`);
|
|
35
|
-
|
|
36
|
-
// Error Chain
|
|
37
|
-
if (result.chain.parents.length > 0 || result.chain.children.length > 0) {
|
|
38
|
-
console.log();
|
|
39
|
-
console.log(` ${c.heading('Error Chain:')}`);
|
|
40
|
-
for (const p of result.chain.parents) {
|
|
41
|
-
console.log(` ${c.dim('↑')} Caused by: #${p.id} ${p.type}: ${p.message.slice(0, 60)}`);
|
|
42
|
-
}
|
|
43
|
-
console.log(` ${c.info('→')} #${err.id} ${err.type}`);
|
|
44
|
-
for (const ch of result.chain.children) {
|
|
45
|
-
console.log(` ${c.dim('↓')} Led to: #${ch.id} ${ch.type}: ${ch.message.slice(0, 60)}`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Solutions
|
|
50
|
-
if (result.solutions.length > 0) {
|
|
51
|
-
console.log();
|
|
52
|
-
console.log(` ${c.heading('Solutions:')}`);
|
|
53
|
-
for (const s of result.solutions) {
|
|
54
|
-
const rate = `${Math.round(s.successRate * 100)}%`;
|
|
55
|
-
console.log(` ${icons.ok} #${s.id}: ${s.description.slice(0, 80)} (success: ${rate}, confidence: ${s.confidence.toFixed(2)})`);
|
|
56
|
-
}
|
|
57
|
-
} else {
|
|
58
|
-
console.log();
|
|
59
|
-
console.log(` ${c.dim('No solutions found.')}`);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Related Errors
|
|
63
|
-
if (result.relatedErrors.length > 0) {
|
|
64
|
-
console.log();
|
|
65
|
-
console.log(` ${c.heading('Related Errors:')}`);
|
|
66
|
-
for (const r of result.relatedErrors.slice(0, 5)) {
|
|
67
|
-
console.log(` ${c.dim('~')} #${r.id} ${r.type}: ${r.message.slice(0, 60)} (${Math.round(r.similarity * 100)}%)`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Rules
|
|
72
|
-
if (result.rules.length > 0) {
|
|
73
|
-
console.log();
|
|
74
|
-
console.log(` ${c.heading('Applicable Rules:')}`);
|
|
75
|
-
for (const r of result.rules) {
|
|
76
|
-
console.log(` ${icons.gear} #${r.id}: ${r.action} (confidence: ${r.confidence.toFixed(2)})`);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
console.log();
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
}
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { withIpc } from '../ipc-helper.js';
|
|
3
|
+
import { c, icons } from '../colors.js';
|
|
4
|
+
|
|
5
|
+
export function explainCommand(): Command {
|
|
6
|
+
return new Command('explain')
|
|
7
|
+
.description('Show everything Brain knows about an error')
|
|
8
|
+
.argument('<errorId>', 'Error ID to explain')
|
|
9
|
+
.action(async (errorId) => {
|
|
10
|
+
const id = parseInt(errorId, 10);
|
|
11
|
+
if (isNaN(id)) {
|
|
12
|
+
console.error(`${icons.error} Invalid error ID: ${errorId}`);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
await withIpc(async (client) => {
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
const result: any = await client.request('analytics.explain', { errorId: id });
|
|
19
|
+
|
|
20
|
+
if (!result.error) {
|
|
21
|
+
console.error(`${icons.error} Error #${id} not found.`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const err = result.error;
|
|
26
|
+
console.log();
|
|
27
|
+
console.log(`${icons.brain} ${c.heading(`Error #${err.id} — ${err.type}`)}`);
|
|
28
|
+
console.log(`${c.dim('─'.repeat(60))}`);
|
|
29
|
+
console.log(` ${c.label('Message:')} ${err.message}`);
|
|
30
|
+
console.log(` ${c.label('File:')} ${err.file_path ?? 'unknown'}`);
|
|
31
|
+
console.log(` ${c.label('Context:')} ${err.context ?? 'none'}`);
|
|
32
|
+
console.log(` ${c.label('Seen:')} ${err.occurrence_count}x (first: ${err.first_seen}, last: ${err.last_seen})`);
|
|
33
|
+
console.log(` ${c.label('Resolved:')} ${err.resolved ? c.success('Yes') : c.error('No')}`);
|
|
34
|
+
console.log(` ${c.label('Synapses:')} ${result.synapseConnections} connections`);
|
|
35
|
+
|
|
36
|
+
// Error Chain
|
|
37
|
+
if (result.chain.parents.length > 0 || result.chain.children.length > 0) {
|
|
38
|
+
console.log();
|
|
39
|
+
console.log(` ${c.heading('Error Chain:')}`);
|
|
40
|
+
for (const p of result.chain.parents) {
|
|
41
|
+
console.log(` ${c.dim('↑')} Caused by: #${p.id} ${p.type}: ${p.message.slice(0, 60)}`);
|
|
42
|
+
}
|
|
43
|
+
console.log(` ${c.info('→')} #${err.id} ${err.type}`);
|
|
44
|
+
for (const ch of result.chain.children) {
|
|
45
|
+
console.log(` ${c.dim('↓')} Led to: #${ch.id} ${ch.type}: ${ch.message.slice(0, 60)}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Solutions
|
|
50
|
+
if (result.solutions.length > 0) {
|
|
51
|
+
console.log();
|
|
52
|
+
console.log(` ${c.heading('Solutions:')}`);
|
|
53
|
+
for (const s of result.solutions) {
|
|
54
|
+
const rate = `${Math.round(s.successRate * 100)}%`;
|
|
55
|
+
console.log(` ${icons.ok} #${s.id}: ${s.description.slice(0, 80)} (success: ${rate}, confidence: ${s.confidence.toFixed(2)})`);
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
console.log();
|
|
59
|
+
console.log(` ${c.dim('No solutions found.')}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Related Errors
|
|
63
|
+
if (result.relatedErrors.length > 0) {
|
|
64
|
+
console.log();
|
|
65
|
+
console.log(` ${c.heading('Related Errors:')}`);
|
|
66
|
+
for (const r of result.relatedErrors.slice(0, 5)) {
|
|
67
|
+
console.log(` ${c.dim('~')} #${r.id} ${r.type}: ${r.message.slice(0, 60)} (${Math.round(r.similarity * 100)}%)`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Rules
|
|
72
|
+
if (result.rules.length > 0) {
|
|
73
|
+
console.log();
|
|
74
|
+
console.log(` ${c.heading('Applicable Rules:')}`);
|
|
75
|
+
for (const r of result.rules) {
|
|
76
|
+
console.log(` ${icons.gear} #${r.id}: ${r.action} (confidence: ${r.confidence.toFixed(2)})`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { withIpc } from '../ipc-helper.js';
|
|
3
|
-
import { c, icons } from '../colors.js';
|
|
4
|
-
|
|
5
|
-
export function exportCommand(): Command {
|
|
6
|
-
return new Command('export')
|
|
7
|
-
.description('Export Brain data')
|
|
8
|
-
.option('--format <fmt>', 'Output format: json (default)', 'json')
|
|
9
|
-
.action(async () => {
|
|
10
|
-
await withIpc(async (client) => {
|
|
11
|
-
process.stderr.write(`${icons.gear} ${c.info('Exporting Brain data...')}\n`);
|
|
12
|
-
|
|
13
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
-
const summary: any = await client.request('analytics.summary', {});
|
|
15
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
|
-
const network: any = await client.request('analytics.network', { limit: 100 });
|
|
17
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
-
const insights: any = await client.request('research.insights', { activeOnly: true, limit: 100 });
|
|
19
|
-
|
|
20
|
-
const data = {
|
|
21
|
-
exportedAt: new Date().toISOString(),
|
|
22
|
-
summary,
|
|
23
|
-
network,
|
|
24
|
-
insights,
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
console.log(JSON.stringify(data, null, 2));
|
|
28
|
-
process.stderr.write(`${icons.ok} ${c.success('Export complete.')}\n`);
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
}
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { withIpc } from '../ipc-helper.js';
|
|
3
|
+
import { c, icons } from '../colors.js';
|
|
4
|
+
|
|
5
|
+
export function exportCommand(): Command {
|
|
6
|
+
return new Command('export')
|
|
7
|
+
.description('Export Brain data')
|
|
8
|
+
.option('--format <fmt>', 'Output format: json (default)', 'json')
|
|
9
|
+
.action(async () => {
|
|
10
|
+
await withIpc(async (client) => {
|
|
11
|
+
process.stderr.write(`${icons.gear} ${c.info('Exporting Brain data...')}\n`);
|
|
12
|
+
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
const summary: any = await client.request('analytics.summary', {});
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
|
+
const network: any = await client.request('analytics.network', { limit: 100 });
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
const insights: any = await client.request('research.insights', { activeOnly: true, limit: 100 });
|
|
19
|
+
|
|
20
|
+
const data = {
|
|
21
|
+
exportedAt: new Date().toISOString(),
|
|
22
|
+
summary,
|
|
23
|
+
network,
|
|
24
|
+
insights,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
console.log(JSON.stringify(data, null, 2));
|
|
28
|
+
process.stderr.write(`${icons.ok} ${c.success('Export complete.')}\n`);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|