@timmeck/brain 1.8.1 → 1.8.3
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/cli/commands/dashboard.js +595 -595
- package/dist/cli/commands/doctor.js +6 -1
- package/dist/cli/commands/doctor.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/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/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 -395
- package/src/brain.ts +266 -266
- package/src/cli/colors.ts +116 -116
- package/src/cli/commands/config.ts +169 -169
- package/src/cli/commands/dashboard.ts +755 -755
- package/src/cli/commands/doctor.ts +124 -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 -238
- 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 -140
- 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 -271
- 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,62 +1,62 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { spawn } from 'node:child_process';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { getDataDir } from '../../utils/paths.js';
|
|
6
|
-
import { c, icons } from '../colors.js';
|
|
7
|
-
import { checkForUpdate } from '../update-check.js';
|
|
8
|
-
|
|
9
|
-
export function startCommand(): Command {
|
|
10
|
-
return new Command('start')
|
|
11
|
-
.description('Start the Brain daemon')
|
|
12
|
-
.option('-f, --foreground', 'Run in foreground (no detach)')
|
|
13
|
-
.option('-c, --config <path>', 'Config file path')
|
|
14
|
-
.action((opts) => {
|
|
15
|
-
const pidPath = path.join(getDataDir(), 'brain.pid');
|
|
16
|
-
|
|
17
|
-
// Check if already running
|
|
18
|
-
if (fs.existsSync(pidPath)) {
|
|
19
|
-
const pid = parseInt(fs.readFileSync(pidPath, 'utf8').trim(), 10);
|
|
20
|
-
try {
|
|
21
|
-
process.kill(pid, 0); // Check if process exists
|
|
22
|
-
console.log(`${icons.brain} Brain daemon is ${c.green('already running')} ${c.dim(`(PID: ${pid})`)}`);
|
|
23
|
-
return;
|
|
24
|
-
} catch {
|
|
25
|
-
// PID file stale, remove it
|
|
26
|
-
fs.unlinkSync(pidPath);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (opts.foreground) {
|
|
31
|
-
// Run in foreground — import dynamically to avoid loading everything at CLI parse time
|
|
32
|
-
import('../../brain.js').then(({ BrainCore }) => {
|
|
33
|
-
const core = new BrainCore();
|
|
34
|
-
core.start(opts.config);
|
|
35
|
-
});
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Spawn detached daemon
|
|
40
|
-
const args = ['daemon'];
|
|
41
|
-
if (opts.config) args.push('-c', opts.config);
|
|
42
|
-
|
|
43
|
-
const entryPoint = path.resolve(import.meta.dirname, '../../index.js');
|
|
44
|
-
const child = spawn(process.execPath, [entryPoint, ...args], {
|
|
45
|
-
detached: true,
|
|
46
|
-
stdio: 'ignore',
|
|
47
|
-
});
|
|
48
|
-
child.unref();
|
|
49
|
-
|
|
50
|
-
console.log(`${icons.brain} ${c.info('Brain daemon starting')} ${c.dim(`(PID: ${child.pid})`)}`);
|
|
51
|
-
|
|
52
|
-
// Wait briefly for PID file to appear
|
|
53
|
-
setTimeout(async () => {
|
|
54
|
-
if (fs.existsSync(pidPath)) {
|
|
55
|
-
console.log(`${icons.ok} ${c.success('Brain daemon started successfully.')}`);
|
|
56
|
-
} else {
|
|
57
|
-
console.log(`${icons.clock} ${c.warn('Brain daemon may still be starting.')} Check: ${c.cyan('brain status')}`);
|
|
58
|
-
}
|
|
59
|
-
await checkForUpdate();
|
|
60
|
-
}, 1000);
|
|
61
|
-
});
|
|
62
|
-
}
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { getDataDir } from '../../utils/paths.js';
|
|
6
|
+
import { c, icons } from '../colors.js';
|
|
7
|
+
import { checkForUpdate } from '../update-check.js';
|
|
8
|
+
|
|
9
|
+
export function startCommand(): Command {
|
|
10
|
+
return new Command('start')
|
|
11
|
+
.description('Start the Brain daemon')
|
|
12
|
+
.option('-f, --foreground', 'Run in foreground (no detach)')
|
|
13
|
+
.option('-c, --config <path>', 'Config file path')
|
|
14
|
+
.action((opts) => {
|
|
15
|
+
const pidPath = path.join(getDataDir(), 'brain.pid');
|
|
16
|
+
|
|
17
|
+
// Check if already running
|
|
18
|
+
if (fs.existsSync(pidPath)) {
|
|
19
|
+
const pid = parseInt(fs.readFileSync(pidPath, 'utf8').trim(), 10);
|
|
20
|
+
try {
|
|
21
|
+
process.kill(pid, 0); // Check if process exists
|
|
22
|
+
console.log(`${icons.brain} Brain daemon is ${c.green('already running')} ${c.dim(`(PID: ${pid})`)}`);
|
|
23
|
+
return;
|
|
24
|
+
} catch {
|
|
25
|
+
// PID file stale, remove it
|
|
26
|
+
fs.unlinkSync(pidPath);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (opts.foreground) {
|
|
31
|
+
// Run in foreground — import dynamically to avoid loading everything at CLI parse time
|
|
32
|
+
import('../../brain.js').then(({ BrainCore }) => {
|
|
33
|
+
const core = new BrainCore();
|
|
34
|
+
core.start(opts.config);
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Spawn detached daemon
|
|
40
|
+
const args = ['daemon'];
|
|
41
|
+
if (opts.config) args.push('-c', opts.config);
|
|
42
|
+
|
|
43
|
+
const entryPoint = path.resolve(import.meta.dirname, '../../index.js');
|
|
44
|
+
const child = spawn(process.execPath, [entryPoint, ...args], {
|
|
45
|
+
detached: true,
|
|
46
|
+
stdio: 'ignore',
|
|
47
|
+
});
|
|
48
|
+
child.unref();
|
|
49
|
+
|
|
50
|
+
console.log(`${icons.brain} ${c.info('Brain daemon starting')} ${c.dim(`(PID: ${child.pid})`)}`);
|
|
51
|
+
|
|
52
|
+
// Wait briefly for PID file to appear
|
|
53
|
+
setTimeout(async () => {
|
|
54
|
+
if (fs.existsSync(pidPath)) {
|
|
55
|
+
console.log(`${icons.ok} ${c.success('Brain daemon started successfully.')}`);
|
|
56
|
+
} else {
|
|
57
|
+
console.log(`${icons.clock} ${c.warn('Brain daemon may still be starting.')} Check: ${c.cyan('brain status')}`);
|
|
58
|
+
}
|
|
59
|
+
await checkForUpdate();
|
|
60
|
+
}, 1000);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { getDataDir } from '../../utils/paths.js';
|
|
5
|
-
import { withIpc } from '../ipc-helper.js';
|
|
6
|
-
import { c, icons, header, keyValue, divider } from '../colors.js';
|
|
7
|
-
import { checkForUpdate, getCurrentVersion } from '../update-check.js';
|
|
8
|
-
|
|
9
|
-
export function statusCommand(): Command {
|
|
10
|
-
return new Command('status')
|
|
11
|
-
.description('Show Brain daemon status')
|
|
12
|
-
.action(async () => {
|
|
13
|
-
const pidPath = path.join(getDataDir(), 'brain.pid');
|
|
14
|
-
|
|
15
|
-
if (!fs.existsSync(pidPath)) {
|
|
16
|
-
console.log(`${icons.brain} Brain Daemon: ${c.red.bold('NOT RUNNING')}`);
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const pid = parseInt(fs.readFileSync(pidPath, 'utf8').trim(), 10);
|
|
21
|
-
let running = false;
|
|
22
|
-
try {
|
|
23
|
-
process.kill(pid, 0);
|
|
24
|
-
running = true;
|
|
25
|
-
} catch { /* not running */ }
|
|
26
|
-
|
|
27
|
-
if (!running) {
|
|
28
|
-
console.log(`${icons.brain} Brain Daemon: ${c.red.bold('NOT RUNNING')} ${c.dim('(stale PID file)')}`);
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
console.log(header(`Brain Status v${getCurrentVersion()}`, icons.brain));
|
|
33
|
-
console.log(` ${c.green(`${icons.dot} RUNNING`)} ${c.dim(`(PID ${pid})`)}`);
|
|
34
|
-
|
|
35
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
|
-
await withIpc(async (client) => {
|
|
37
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
|
-
const summary: any = await client.request('analytics.summary', {});
|
|
39
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
|
-
const network: any = await client.request('synapse.stats', {});
|
|
41
|
-
|
|
42
|
-
const dbPath = path.join(getDataDir(), 'brain.db');
|
|
43
|
-
let dbSize = '?';
|
|
44
|
-
try {
|
|
45
|
-
const stat = fs.statSync(dbPath);
|
|
46
|
-
dbSize = `${(stat.size / 1024 / 1024).toFixed(1)} MB`;
|
|
47
|
-
} catch { /* ignore */ }
|
|
48
|
-
|
|
49
|
-
console.log(keyValue('Database', `${dbPath} (${dbSize})`));
|
|
50
|
-
console.log();
|
|
51
|
-
|
|
52
|
-
console.log(` ${icons.error} ${c.purple.bold('Error Brain')}`);
|
|
53
|
-
console.log(` ${c.label('Errors:')} ${c.value(summary.errors?.total ?? 0)} total, ${c.red(summary.errors?.unresolved ?? 0)} unresolved, ${c.dim(`${summary.errors?.last7d ?? 0} last 7d`)}`);
|
|
54
|
-
console.log(` ${c.label('Solutions:')} ${c.value(summary.solutions?.total ?? 0)}`);
|
|
55
|
-
console.log(` ${c.label('Rules:')} ${c.green(summary.rules?.active ?? 0)} active`);
|
|
56
|
-
console.log(` ${c.label('Anti-Pat.:')} ${c.value(summary.antipatterns?.total ?? 0)}`);
|
|
57
|
-
console.log();
|
|
58
|
-
|
|
59
|
-
console.log(` ${icons.module} ${c.blue.bold('Code Brain')}`);
|
|
60
|
-
console.log(` ${c.label('Modules:')} ${c.value(summary.modules?.total ?? 0)} registered`);
|
|
61
|
-
console.log();
|
|
62
|
-
|
|
63
|
-
console.log(` ${icons.synapse} ${c.cyan.bold('Synapse Network')}`);
|
|
64
|
-
console.log(` ${c.label('Synapses:')} ${c.value(network.totalSynapses ?? 0)}`);
|
|
65
|
-
console.log(` ${c.label('Avg weight:')} ${c.value((network.avgWeight ?? 0).toFixed(2))}`);
|
|
66
|
-
console.log();
|
|
67
|
-
|
|
68
|
-
console.log(` ${icons.insight} ${c.orange.bold('Research Brain')}`);
|
|
69
|
-
console.log(` ${c.label('Insights:')} ${c.value(summary.insights?.active ?? 0)} active`);
|
|
70
|
-
|
|
71
|
-
await checkForUpdate();
|
|
72
|
-
console.log(`\n${divider()}`);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
}
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { getDataDir } from '../../utils/paths.js';
|
|
5
|
+
import { withIpc } from '../ipc-helper.js';
|
|
6
|
+
import { c, icons, header, keyValue, divider } from '../colors.js';
|
|
7
|
+
import { checkForUpdate, getCurrentVersion } from '../update-check.js';
|
|
8
|
+
|
|
9
|
+
export function statusCommand(): Command {
|
|
10
|
+
return new Command('status')
|
|
11
|
+
.description('Show Brain daemon status')
|
|
12
|
+
.action(async () => {
|
|
13
|
+
const pidPath = path.join(getDataDir(), 'brain.pid');
|
|
14
|
+
|
|
15
|
+
if (!fs.existsSync(pidPath)) {
|
|
16
|
+
console.log(`${icons.brain} Brain Daemon: ${c.red.bold('NOT RUNNING')}`);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const pid = parseInt(fs.readFileSync(pidPath, 'utf8').trim(), 10);
|
|
21
|
+
let running = false;
|
|
22
|
+
try {
|
|
23
|
+
process.kill(pid, 0);
|
|
24
|
+
running = true;
|
|
25
|
+
} catch { /* not running */ }
|
|
26
|
+
|
|
27
|
+
if (!running) {
|
|
28
|
+
console.log(`${icons.brain} Brain Daemon: ${c.red.bold('NOT RUNNING')} ${c.dim('(stale PID file)')}`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
console.log(header(`Brain Status v${getCurrentVersion()}`, icons.brain));
|
|
33
|
+
console.log(` ${c.green(`${icons.dot} RUNNING`)} ${c.dim(`(PID ${pid})`)}`);
|
|
34
|
+
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
|
+
await withIpc(async (client) => {
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
|
+
const summary: any = await client.request('analytics.summary', {});
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
|
+
const network: any = await client.request('synapse.stats', {});
|
|
41
|
+
|
|
42
|
+
const dbPath = path.join(getDataDir(), 'brain.db');
|
|
43
|
+
let dbSize = '?';
|
|
44
|
+
try {
|
|
45
|
+
const stat = fs.statSync(dbPath);
|
|
46
|
+
dbSize = `${(stat.size / 1024 / 1024).toFixed(1)} MB`;
|
|
47
|
+
} catch { /* ignore */ }
|
|
48
|
+
|
|
49
|
+
console.log(keyValue('Database', `${dbPath} (${dbSize})`));
|
|
50
|
+
console.log();
|
|
51
|
+
|
|
52
|
+
console.log(` ${icons.error} ${c.purple.bold('Error Brain')}`);
|
|
53
|
+
console.log(` ${c.label('Errors:')} ${c.value(summary.errors?.total ?? 0)} total, ${c.red(summary.errors?.unresolved ?? 0)} unresolved, ${c.dim(`${summary.errors?.last7d ?? 0} last 7d`)}`);
|
|
54
|
+
console.log(` ${c.label('Solutions:')} ${c.value(summary.solutions?.total ?? 0)}`);
|
|
55
|
+
console.log(` ${c.label('Rules:')} ${c.green(summary.rules?.active ?? 0)} active`);
|
|
56
|
+
console.log(` ${c.label('Anti-Pat.:')} ${c.value(summary.antipatterns?.total ?? 0)}`);
|
|
57
|
+
console.log();
|
|
58
|
+
|
|
59
|
+
console.log(` ${icons.module} ${c.blue.bold('Code Brain')}`);
|
|
60
|
+
console.log(` ${c.label('Modules:')} ${c.value(summary.modules?.total ?? 0)} registered`);
|
|
61
|
+
console.log();
|
|
62
|
+
|
|
63
|
+
console.log(` ${icons.synapse} ${c.cyan.bold('Synapse Network')}`);
|
|
64
|
+
console.log(` ${c.label('Synapses:')} ${c.value(network.totalSynapses ?? 0)}`);
|
|
65
|
+
console.log(` ${c.label('Avg weight:')} ${c.value((network.avgWeight ?? 0).toFixed(2))}`);
|
|
66
|
+
console.log();
|
|
67
|
+
|
|
68
|
+
console.log(` ${icons.insight} ${c.orange.bold('Research Brain')}`);
|
|
69
|
+
console.log(` ${c.label('Insights:')} ${c.value(summary.insights?.active ?? 0)} active`);
|
|
70
|
+
|
|
71
|
+
await checkForUpdate();
|
|
72
|
+
console.log(`\n${divider()}`);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
package/src/cli/commands/stop.ts
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { getDataDir } from '../../utils/paths.js';
|
|
5
|
-
import { c, icons } from '../colors.js';
|
|
6
|
-
|
|
7
|
-
export function stopCommand(): Command {
|
|
8
|
-
return new Command('stop')
|
|
9
|
-
.description('Stop the Brain daemon')
|
|
10
|
-
.action(() => {
|
|
11
|
-
const pidPath = path.join(getDataDir(), 'brain.pid');
|
|
12
|
-
|
|
13
|
-
if (!fs.existsSync(pidPath)) {
|
|
14
|
-
console.log(`${icons.brain} ${c.dim('Brain daemon is not running (no PID file found).')}`);
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const pid = parseInt(fs.readFileSync(pidPath, 'utf8').trim(), 10);
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
process.kill(pid, 'SIGTERM');
|
|
22
|
-
console.log(`${icons.brain} ${c.success('Brain daemon stopped')} ${c.dim(`(PID: ${pid})`)}`);
|
|
23
|
-
} catch (err) {
|
|
24
|
-
if ((err as NodeJS.ErrnoException).code === 'ESRCH') {
|
|
25
|
-
console.log(`${icons.brain} ${c.dim('Brain daemon was not running (stale PID file removed).')}`);
|
|
26
|
-
} else {
|
|
27
|
-
console.error(`${icons.error} ${c.error(`Failed to stop daemon: ${err}`)}`);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Clean up PID file
|
|
32
|
-
try { fs.unlinkSync(pidPath); } catch { /* ignore */ }
|
|
33
|
-
});
|
|
34
|
-
}
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { getDataDir } from '../../utils/paths.js';
|
|
5
|
+
import { c, icons } from '../colors.js';
|
|
6
|
+
|
|
7
|
+
export function stopCommand(): Command {
|
|
8
|
+
return new Command('stop')
|
|
9
|
+
.description('Stop the Brain daemon')
|
|
10
|
+
.action(() => {
|
|
11
|
+
const pidPath = path.join(getDataDir(), 'brain.pid');
|
|
12
|
+
|
|
13
|
+
if (!fs.existsSync(pidPath)) {
|
|
14
|
+
console.log(`${icons.brain} ${c.dim('Brain daemon is not running (no PID file found).')}`);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const pid = parseInt(fs.readFileSync(pidPath, 'utf8').trim(), 10);
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
process.kill(pid, 'SIGTERM');
|
|
22
|
+
console.log(`${icons.brain} ${c.success('Brain daemon stopped')} ${c.dim(`(PID: ${pid})`)}`);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
if ((err as NodeJS.ErrnoException).code === 'ESRCH') {
|
|
25
|
+
console.log(`${icons.brain} ${c.dim('Brain daemon was not running (stale PID file removed).')}`);
|
|
26
|
+
} else {
|
|
27
|
+
console.error(`${icons.error} ${c.error(`Failed to stop daemon: ${err}`)}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Clean up PID file
|
|
32
|
+
try { fs.unlinkSync(pidPath); } catch { /* ignore */ }
|
|
33
|
+
});
|
|
34
|
+
}
|
package/src/cli/ipc-helper.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { IpcClient } from '../ipc/client.js';
|
|
2
|
-
import { getPipeName } from '../utils/paths.js';
|
|
3
|
-
import { c, icons } from './colors.js';
|
|
4
|
-
|
|
5
|
-
export async function withIpc<T>(fn: (client: IpcClient) => Promise<T>): Promise<T> {
|
|
6
|
-
const client = new IpcClient(getPipeName(), 5000);
|
|
7
|
-
try {
|
|
8
|
-
await client.connect();
|
|
9
|
-
return await fn(client);
|
|
10
|
-
} catch (err) {
|
|
11
|
-
if (err instanceof Error && err.message.includes('ENOENT')) {
|
|
12
|
-
console.error(`${icons.error} ${c.error('Brain daemon is not running.')} Start it with: ${c.cyan('brain start')}`);
|
|
13
|
-
} else if (err instanceof Error && err.message.includes('ECONNREFUSED')) {
|
|
14
|
-
console.error(`${icons.error} ${c.error('Brain daemon is not responding.')} Try: ${c.cyan('brain stop && brain start')}`);
|
|
15
|
-
} else {
|
|
16
|
-
console.error(`${icons.error} ${c.error(err instanceof Error ? err.message : String(err))}`);
|
|
17
|
-
}
|
|
18
|
-
process.exit(1);
|
|
19
|
-
} finally {
|
|
20
|
-
client.disconnect();
|
|
21
|
-
}
|
|
22
|
-
}
|
|
1
|
+
import { IpcClient } from '../ipc/client.js';
|
|
2
|
+
import { getPipeName } from '../utils/paths.js';
|
|
3
|
+
import { c, icons } from './colors.js';
|
|
4
|
+
|
|
5
|
+
export async function withIpc<T>(fn: (client: IpcClient) => Promise<T>): Promise<T> {
|
|
6
|
+
const client = new IpcClient(getPipeName(), 5000);
|
|
7
|
+
try {
|
|
8
|
+
await client.connect();
|
|
9
|
+
return await fn(client);
|
|
10
|
+
} catch (err) {
|
|
11
|
+
if (err instanceof Error && err.message.includes('ENOENT')) {
|
|
12
|
+
console.error(`${icons.error} ${c.error('Brain daemon is not running.')} Start it with: ${c.cyan('brain start')}`);
|
|
13
|
+
} else if (err instanceof Error && err.message.includes('ECONNREFUSED')) {
|
|
14
|
+
console.error(`${icons.error} ${c.error('Brain daemon is not responding.')} Try: ${c.cyan('brain stop && brain start')}`);
|
|
15
|
+
} else {
|
|
16
|
+
console.error(`${icons.error} ${c.error(err instanceof Error ? err.message : String(err))}`);
|
|
17
|
+
}
|
|
18
|
+
process.exit(1);
|
|
19
|
+
} finally {
|
|
20
|
+
client.disconnect();
|
|
21
|
+
}
|
|
22
|
+
}
|
package/src/cli/update-check.ts
CHANGED
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
import https from 'node:https';
|
|
2
|
-
import { c, icons } from './colors.js';
|
|
3
|
-
|
|
4
|
-
// Read current version from package.json at build time
|
|
5
|
-
import { createRequire } from 'node:module';
|
|
6
|
-
const require = createRequire(import.meta.url);
|
|
7
|
-
const pkg = require('../../package.json');
|
|
8
|
-
const CURRENT_VERSION: string = pkg.version;
|
|
9
|
-
|
|
10
|
-
export function getCurrentVersion(): string {
|
|
11
|
-
return CURRENT_VERSION;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function fetchLatestVersion(): Promise<string | null> {
|
|
15
|
-
return new Promise((resolve) => {
|
|
16
|
-
const timeout = setTimeout(() => resolve(null), 3000);
|
|
17
|
-
|
|
18
|
-
const req = https.get(
|
|
19
|
-
'https://registry.npmjs.org/@timmeck/brain/latest',
|
|
20
|
-
{ headers: { Accept: 'application/json' } },
|
|
21
|
-
(res) => {
|
|
22
|
-
let data = '';
|
|
23
|
-
res.on('data', (chunk) => { data += chunk; });
|
|
24
|
-
res.on('end', () => {
|
|
25
|
-
clearTimeout(timeout);
|
|
26
|
-
try {
|
|
27
|
-
const json = JSON.parse(data);
|
|
28
|
-
resolve(json.version ?? null);
|
|
29
|
-
} catch {
|
|
30
|
-
resolve(null);
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
},
|
|
34
|
-
);
|
|
35
|
-
req.on('error', () => {
|
|
36
|
-
clearTimeout(timeout);
|
|
37
|
-
resolve(null);
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function isNewer(latest: string, current: string): boolean {
|
|
43
|
-
const l = latest.split('.').map(Number);
|
|
44
|
-
const c = current.split('.').map(Number);
|
|
45
|
-
for (let i = 0; i < 3; i++) {
|
|
46
|
-
if ((l[i] ?? 0) > (c[i] ?? 0)) return true;
|
|
47
|
-
if ((l[i] ?? 0) < (c[i] ?? 0)) return false;
|
|
48
|
-
}
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export async function checkForUpdate(): Promise<void> {
|
|
53
|
-
try {
|
|
54
|
-
const latest = await fetchLatestVersion();
|
|
55
|
-
if (latest && isNewer(latest, CURRENT_VERSION)) {
|
|
56
|
-
console.log();
|
|
57
|
-
console.log(` ${icons.star} ${c.orange.bold(`Update available: v${CURRENT_VERSION} → v${latest}`)}`);
|
|
58
|
-
console.log(` Run: ${c.cyan('npm update -g @timmeck/brain')}`);
|
|
59
|
-
}
|
|
60
|
-
} catch {
|
|
61
|
-
// silently ignore — update check is best-effort
|
|
62
|
-
}
|
|
63
|
-
}
|
|
1
|
+
import https from 'node:https';
|
|
2
|
+
import { c, icons } from './colors.js';
|
|
3
|
+
|
|
4
|
+
// Read current version from package.json at build time
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const pkg = require('../../package.json');
|
|
8
|
+
const CURRENT_VERSION: string = pkg.version;
|
|
9
|
+
|
|
10
|
+
export function getCurrentVersion(): string {
|
|
11
|
+
return CURRENT_VERSION;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function fetchLatestVersion(): Promise<string | null> {
|
|
15
|
+
return new Promise((resolve) => {
|
|
16
|
+
const timeout = setTimeout(() => resolve(null), 3000);
|
|
17
|
+
|
|
18
|
+
const req = https.get(
|
|
19
|
+
'https://registry.npmjs.org/@timmeck/brain/latest',
|
|
20
|
+
{ headers: { Accept: 'application/json' } },
|
|
21
|
+
(res) => {
|
|
22
|
+
let data = '';
|
|
23
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
24
|
+
res.on('end', () => {
|
|
25
|
+
clearTimeout(timeout);
|
|
26
|
+
try {
|
|
27
|
+
const json = JSON.parse(data);
|
|
28
|
+
resolve(json.version ?? null);
|
|
29
|
+
} catch {
|
|
30
|
+
resolve(null);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
req.on('error', () => {
|
|
36
|
+
clearTimeout(timeout);
|
|
37
|
+
resolve(null);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function isNewer(latest: string, current: string): boolean {
|
|
43
|
+
const l = latest.split('.').map(Number);
|
|
44
|
+
const c = current.split('.').map(Number);
|
|
45
|
+
for (let i = 0; i < 3; i++) {
|
|
46
|
+
if ((l[i] ?? 0) > (c[i] ?? 0)) return true;
|
|
47
|
+
if ((l[i] ?? 0) < (c[i] ?? 0)) return false;
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function checkForUpdate(): Promise<void> {
|
|
53
|
+
try {
|
|
54
|
+
const latest = await fetchLatestVersion();
|
|
55
|
+
if (latest && isNewer(latest, CURRENT_VERSION)) {
|
|
56
|
+
console.log();
|
|
57
|
+
console.log(` ${icons.star} ${c.orange.bold(`Update available: v${CURRENT_VERSION} → v${latest}`)}`);
|
|
58
|
+
console.log(` Run: ${c.cyan('npm update -g @timmeck/brain')}`);
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
// silently ignore — update check is best-effort
|
|
62
|
+
}
|
|
63
|
+
}
|