@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,53 +1,53 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { withIpc } from '../ipc-helper.js';
|
|
3
|
-
import { c, icons, header, divider } from '../colors.js';
|
|
4
|
-
|
|
5
|
-
export function modulesCommand(): Command {
|
|
6
|
-
return new Command('modules')
|
|
7
|
-
.description('List registered code modules')
|
|
8
|
-
.option('--language <lang>', 'Filter by language')
|
|
9
|
-
.option('-l, --limit <n>', 'Maximum results')
|
|
10
|
-
.action(async (opts) => {
|
|
11
|
-
await withIpc(async (client) => {
|
|
12
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
-
const modules: any = await client.request('code.modules', {
|
|
14
|
-
language: opts.language,
|
|
15
|
-
limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
if (!modules?.length) {
|
|
19
|
-
console.log(`${icons.module} ${c.dim('No code modules registered.')}`);
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
console.log(header(`${modules.length} Code Modules`, icons.module));
|
|
24
|
-
|
|
25
|
-
// Group by language
|
|
26
|
-
const byLang: Record<string, number> = {};
|
|
27
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
-
for (const mod of modules as any[]) {
|
|
29
|
-
byLang[mod.language] = (byLang[mod.language] || 0) + 1;
|
|
30
|
-
}
|
|
31
|
-
const langSummary = Object.entries(byLang)
|
|
32
|
-
.sort((a, b) => b[1] - a[1])
|
|
33
|
-
.map(([lang, count]) => `${c.cyan(lang)} ${c.dim(`(${count})`)}`)
|
|
34
|
-
.join(' ');
|
|
35
|
-
console.log(` ${langSummary}\n`);
|
|
36
|
-
|
|
37
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
|
-
for (const mod of modules as any[]) {
|
|
39
|
-
const score = mod.reusabilityScore ?? mod.reusability_score ?? 0;
|
|
40
|
-
const scoreColor = score >= 0.7 ? c.green : score >= 0.4 ? c.orange : c.red;
|
|
41
|
-
const langTag = c.cyan(`[${mod.language}]`);
|
|
42
|
-
|
|
43
|
-
console.log(` ${c.dim(`#${mod.id}`)} ${langTag} ${c.value(mod.name)}`);
|
|
44
|
-
if (mod.description) {
|
|
45
|
-
console.log(` ${c.dim(mod.description.slice(0, 120))}`);
|
|
46
|
-
}
|
|
47
|
-
console.log(` ${c.label('File:')} ${c.dim(mod.filePath ?? mod.file_path)} ${c.label('Score:')} ${scoreColor(typeof score === 'number' ? score.toFixed(2) : score)}`);
|
|
48
|
-
console.log();
|
|
49
|
-
}
|
|
50
|
-
console.log(divider());
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
}
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { withIpc } from '../ipc-helper.js';
|
|
3
|
+
import { c, icons, header, divider } from '../colors.js';
|
|
4
|
+
|
|
5
|
+
export function modulesCommand(): Command {
|
|
6
|
+
return new Command('modules')
|
|
7
|
+
.description('List registered code modules')
|
|
8
|
+
.option('--language <lang>', 'Filter by language')
|
|
9
|
+
.option('-l, --limit <n>', 'Maximum results')
|
|
10
|
+
.action(async (opts) => {
|
|
11
|
+
await withIpc(async (client) => {
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
const modules: any = await client.request('code.modules', {
|
|
14
|
+
language: opts.language,
|
|
15
|
+
limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
if (!modules?.length) {
|
|
19
|
+
console.log(`${icons.module} ${c.dim('No code modules registered.')}`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log(header(`${modules.length} Code Modules`, icons.module));
|
|
24
|
+
|
|
25
|
+
// Group by language
|
|
26
|
+
const byLang: Record<string, number> = {};
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
+
for (const mod of modules as any[]) {
|
|
29
|
+
byLang[mod.language] = (byLang[mod.language] || 0) + 1;
|
|
30
|
+
}
|
|
31
|
+
const langSummary = Object.entries(byLang)
|
|
32
|
+
.sort((a, b) => b[1] - a[1])
|
|
33
|
+
.map(([lang, count]) => `${c.cyan(lang)} ${c.dim(`(${count})`)}`)
|
|
34
|
+
.join(' ');
|
|
35
|
+
console.log(` ${langSummary}\n`);
|
|
36
|
+
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
|
+
for (const mod of modules as any[]) {
|
|
39
|
+
const score = mod.reusabilityScore ?? mod.reusability_score ?? 0;
|
|
40
|
+
const scoreColor = score >= 0.7 ? c.green : score >= 0.4 ? c.orange : c.red;
|
|
41
|
+
const langTag = c.cyan(`[${mod.language}]`);
|
|
42
|
+
|
|
43
|
+
console.log(` ${c.dim(`#${mod.id}`)} ${langTag} ${c.value(mod.name)}`);
|
|
44
|
+
if (mod.description) {
|
|
45
|
+
console.log(` ${c.dim(mod.description.slice(0, 120))}`);
|
|
46
|
+
}
|
|
47
|
+
console.log(` ${c.label('File:')} ${c.dim(mod.filePath ?? mod.file_path)} ${c.label('Score:')} ${scoreColor(typeof score === 'number' ? score.toFixed(2) : score)}`);
|
|
48
|
+
console.log();
|
|
49
|
+
}
|
|
50
|
+
console.log(divider());
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { withIpc } from '../ipc-helper.js';
|
|
3
|
-
import { c, icons, header, keyValue, divider } from '../colors.js';
|
|
4
|
-
|
|
5
|
-
export function networkCommand(): Command {
|
|
6
|
-
return new Command('network')
|
|
7
|
-
.description('Explore the synapse network')
|
|
8
|
-
.option('--node <type:id>', 'Node to explore (e.g., error:42)')
|
|
9
|
-
.option('-l, --limit <n>', 'Max synapses to show', '20')
|
|
10
|
-
.action(async (opts) => {
|
|
11
|
-
await withIpc(async (client) => {
|
|
12
|
-
if (opts.node) {
|
|
13
|
-
const [nodeType, nodeIdStr] = opts.node.split(':');
|
|
14
|
-
const nodeId = parseInt(nodeIdStr, 10);
|
|
15
|
-
|
|
16
|
-
if (!nodeType || isNaN(nodeId)) {
|
|
17
|
-
console.error(c.error('Invalid node format. Use: --node error:42'));
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
-
const related: any = await client.request('synapse.related', {
|
|
23
|
-
nodeType,
|
|
24
|
-
nodeId,
|
|
25
|
-
maxDepth: 2,
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
if (!related?.length) {
|
|
29
|
-
console.log(`${c.dim('No connections found for')} ${c.cyan(`${nodeType}:${nodeId}`)}`);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
console.log(header(`Connections from ${nodeType}:${nodeId}`, icons.synapse));
|
|
34
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
-
for (const r of related as any[]) {
|
|
36
|
-
const weight = (r.activation ?? r.weight ?? 0);
|
|
37
|
-
const weightColor = weight >= 0.7 ? c.green : weight >= 0.3 ? c.orange : c.dim;
|
|
38
|
-
console.log(` ${c.cyan(icons.arrow)} ${c.value(`${r.nodeType}:${r.nodeId}`)} ${c.label('weight:')} ${weightColor(weight.toFixed(3))}`);
|
|
39
|
-
}
|
|
40
|
-
} else {
|
|
41
|
-
// Show general network stats
|
|
42
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
-
const stats: any = await client.request('synapse.stats', {});
|
|
44
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
-
const overview: any = await client.request('analytics.network', {
|
|
46
|
-
limit: parseInt(opts.limit, 10),
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
console.log(header('Synapse Network', icons.synapse));
|
|
50
|
-
console.log(keyValue('Total synapses', stats.totalSynapses ?? 0));
|
|
51
|
-
console.log(keyValue('Average weight', (stats.avgWeight ?? 0).toFixed(3)));
|
|
52
|
-
console.log();
|
|
53
|
-
|
|
54
|
-
if (overview?.strongestSynapses?.length) {
|
|
55
|
-
console.log(` ${c.purple.bold('Strongest connections:')}`);
|
|
56
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
|
-
for (const s of overview.strongestSynapses as any[]) {
|
|
58
|
-
const weight = (s.weight ?? 0);
|
|
59
|
-
const weightColor = weight >= 0.7 ? c.green : weight >= 0.3 ? c.orange : c.dim;
|
|
60
|
-
console.log(` ${c.dim(s.source)} ${c.cyan(icons.arrow)} ${c.dim(s.target)} ${c.label(`[${s.type}]`)} ${weightColor(weight.toFixed(3))}`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
console.log(`\n${divider()}`);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
}
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { withIpc } from '../ipc-helper.js';
|
|
3
|
+
import { c, icons, header, keyValue, divider } from '../colors.js';
|
|
4
|
+
|
|
5
|
+
export function networkCommand(): Command {
|
|
6
|
+
return new Command('network')
|
|
7
|
+
.description('Explore the synapse network')
|
|
8
|
+
.option('--node <type:id>', 'Node to explore (e.g., error:42)')
|
|
9
|
+
.option('-l, --limit <n>', 'Max synapses to show', '20')
|
|
10
|
+
.action(async (opts) => {
|
|
11
|
+
await withIpc(async (client) => {
|
|
12
|
+
if (opts.node) {
|
|
13
|
+
const [nodeType, nodeIdStr] = opts.node.split(':');
|
|
14
|
+
const nodeId = parseInt(nodeIdStr, 10);
|
|
15
|
+
|
|
16
|
+
if (!nodeType || isNaN(nodeId)) {
|
|
17
|
+
console.error(c.error('Invalid node format. Use: --node error:42'));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
+
const related: any = await client.request('synapse.related', {
|
|
23
|
+
nodeType,
|
|
24
|
+
nodeId,
|
|
25
|
+
maxDepth: 2,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (!related?.length) {
|
|
29
|
+
console.log(`${c.dim('No connections found for')} ${c.cyan(`${nodeType}:${nodeId}`)}`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console.log(header(`Connections from ${nodeType}:${nodeId}`, icons.synapse));
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
for (const r of related as any[]) {
|
|
36
|
+
const weight = (r.activation ?? r.weight ?? 0);
|
|
37
|
+
const weightColor = weight >= 0.7 ? c.green : weight >= 0.3 ? c.orange : c.dim;
|
|
38
|
+
console.log(` ${c.cyan(icons.arrow)} ${c.value(`${r.nodeType}:${r.nodeId}`)} ${c.label('weight:')} ${weightColor(weight.toFixed(3))}`);
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
// Show general network stats
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
+
const stats: any = await client.request('synapse.stats', {});
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
const overview: any = await client.request('analytics.network', {
|
|
46
|
+
limit: parseInt(opts.limit, 10),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
console.log(header('Synapse Network', icons.synapse));
|
|
50
|
+
console.log(keyValue('Total synapses', stats.totalSynapses ?? 0));
|
|
51
|
+
console.log(keyValue('Average weight', (stats.avgWeight ?? 0).toFixed(3)));
|
|
52
|
+
console.log();
|
|
53
|
+
|
|
54
|
+
if (overview?.strongestSynapses?.length) {
|
|
55
|
+
console.log(` ${c.purple.bold('Strongest connections:')}`);
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
|
+
for (const s of overview.strongestSynapses as any[]) {
|
|
58
|
+
const weight = (s.weight ?? 0);
|
|
59
|
+
const weightColor = weight >= 0.7 ? c.green : weight >= 0.3 ? c.orange : c.dim;
|
|
60
|
+
console.log(` ${c.dim(s.source)} ${c.cyan(icons.arrow)} ${c.dim(s.target)} ${c.label(`[${s.type}]`)} ${weightColor(weight.toFixed(3))}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
console.log(`\n${divider()}`);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { withIpc } from '../ipc-helper.js';
|
|
3
|
-
import { c, icons, header, divider, table } from '../colors.js';
|
|
4
|
-
|
|
5
|
-
export function projectsCommand(): Command {
|
|
6
|
-
return new Command('projects')
|
|
7
|
-
.description('List all imported projects with stats')
|
|
8
|
-
.action(async () => {
|
|
9
|
-
await withIpc(async (client) => {
|
|
10
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
-
const projects = await client.request('project.list', {}) as any[];
|
|
12
|
-
|
|
13
|
-
console.log(header('Projects', icons.module));
|
|
14
|
-
|
|
15
|
-
if (projects.length === 0) {
|
|
16
|
-
console.log(`\n ${c.dim('No projects imported yet.')} Use ${c.cyan('brain import <dir>')} to get started.`);
|
|
17
|
-
console.log(`\n${divider()}`);
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
console.log();
|
|
22
|
-
|
|
23
|
-
const rows: string[][] = [
|
|
24
|
-
[c.dim(' #'), c.dim('Name'), c.dim('Language'), c.dim('Modules'), c.dim('Path')],
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
for (const p of projects) {
|
|
28
|
-
rows.push([
|
|
29
|
-
c.dimmer(` ${p.id}`),
|
|
30
|
-
c.value(p.name),
|
|
31
|
-
p.language ? c.cyan(p.language) : c.dim('—'),
|
|
32
|
-
c.green(String(p.moduleCount)),
|
|
33
|
-
p.path ? c.dim(p.path) : c.dim('—'),
|
|
34
|
-
]);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
console.log(table(rows, [5, 24, 14, 9, 40]));
|
|
38
|
-
console.log(`\n ${c.label('Total:')} ${c.value(String(projects.length))} projects, ${c.green(String(projects.reduce((s: number, p: any) => s + p.moduleCount, 0)))} modules`);
|
|
39
|
-
console.log(`\n${divider()}`);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
}
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { withIpc } from '../ipc-helper.js';
|
|
3
|
+
import { c, icons, header, divider, table } from '../colors.js';
|
|
4
|
+
|
|
5
|
+
export function projectsCommand(): Command {
|
|
6
|
+
return new Command('projects')
|
|
7
|
+
.description('List all imported projects with stats')
|
|
8
|
+
.action(async () => {
|
|
9
|
+
await withIpc(async (client) => {
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
const projects = await client.request('project.list', {}) as any[];
|
|
12
|
+
|
|
13
|
+
console.log(header('Projects', icons.module));
|
|
14
|
+
|
|
15
|
+
if (projects.length === 0) {
|
|
16
|
+
console.log(`\n ${c.dim('No projects imported yet.')} Use ${c.cyan('brain import <dir>')} to get started.`);
|
|
17
|
+
console.log(`\n${divider()}`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
console.log();
|
|
22
|
+
|
|
23
|
+
const rows: string[][] = [
|
|
24
|
+
[c.dim(' #'), c.dim('Name'), c.dim('Language'), c.dim('Modules'), c.dim('Path')],
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
for (const p of projects) {
|
|
28
|
+
rows.push([
|
|
29
|
+
c.dimmer(` ${p.id}`),
|
|
30
|
+
c.value(p.name),
|
|
31
|
+
p.language ? c.cyan(p.language) : c.dim('—'),
|
|
32
|
+
c.green(String(p.moduleCount)),
|
|
33
|
+
p.path ? c.dim(p.path) : c.dim('—'),
|
|
34
|
+
]);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log(table(rows, [5, 24, 14, 9, 40]));
|
|
38
|
+
console.log(`\n ${c.label('Total:')} ${c.value(String(projects.length))} projects, ${c.green(String(projects.reduce((s: number, p: any) => s + p.moduleCount, 0)))} modules`);
|
|
39
|
+
console.log(`\n${divider()}`);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -1,120 +1,120 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { withIpc } from '../ipc-helper.js';
|
|
3
|
-
import { c, icons, header, statusBadge, divider } from '../colors.js';
|
|
4
|
-
|
|
5
|
-
export function queryCommand(): Command {
|
|
6
|
-
return new Command('query')
|
|
7
|
-
.description('Search errors, code modules, and insights')
|
|
8
|
-
.argument('<search>', 'Search term')
|
|
9
|
-
.option('-l, --limit <n>', 'Maximum results per category', '10')
|
|
10
|
-
.option('--errors-only', 'Only search errors')
|
|
11
|
-
.option('--modules-only', 'Only search code modules')
|
|
12
|
-
.option('--insights-only', 'Only search insights')
|
|
13
|
-
.option('--page <n>', 'Page number (starting from 1)', '1')
|
|
14
|
-
.action(async (search: string, opts) => {
|
|
15
|
-
await withIpc(async (client) => {
|
|
16
|
-
const limit = parseInt(opts.limit, 10);
|
|
17
|
-
const page = parseInt(opts.page, 10) || 1;
|
|
18
|
-
const offset = (page - 1) * limit;
|
|
19
|
-
const searchAll = !opts.errorsOnly && !opts.modulesOnly && !opts.insightsOnly;
|
|
20
|
-
let totalResults = 0;
|
|
21
|
-
|
|
22
|
-
// --- Errors ---
|
|
23
|
-
if (searchAll || opts.errorsOnly) {
|
|
24
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
|
-
const results: any = await client.request('error.query', {
|
|
26
|
-
search,
|
|
27
|
-
limit: limit + offset,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const errors = Array.isArray(results) ? results.slice(offset, offset + limit) : [];
|
|
31
|
-
if (errors.length > 0) {
|
|
32
|
-
totalResults += errors.length;
|
|
33
|
-
console.log(header(`Errors matching "${search}"`, icons.error));
|
|
34
|
-
|
|
35
|
-
for (const err of errors) {
|
|
36
|
-
const badge = statusBadge(err.resolved ? 'resolved' : 'open');
|
|
37
|
-
const typeTag = c.purple(err.errorType ?? 'unknown');
|
|
38
|
-
console.log(` ${c.dim(`#${err.id}`)} ${badge} ${typeTag}`);
|
|
39
|
-
console.log(` ${c.dim((err.message ?? '').slice(0, 120))}`);
|
|
40
|
-
|
|
41
|
-
// Get solutions for this error
|
|
42
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
-
const solutions: any = await client.request('solution.query', { error_id: err.id });
|
|
44
|
-
if (solutions?.length > 0) {
|
|
45
|
-
console.log(` ${c.green(`${icons.check} ${solutions.length} solution(s)`)}`);
|
|
46
|
-
for (const sol of solutions.slice(0, 3)) {
|
|
47
|
-
console.log(` ${c.dim(icons.corner)} ${c.dim((sol.description ?? '').slice(0, 100))}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
console.log();
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// --- Code Modules ---
|
|
56
|
-
if (searchAll || opts.modulesOnly) {
|
|
57
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
|
-
const modules: any = await client.request('code.find', {
|
|
59
|
-
query: search,
|
|
60
|
-
limit: limit + offset,
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
const mods = Array.isArray(modules) ? modules.slice(offset, offset + limit) : [];
|
|
64
|
-
if (mods.length > 0) {
|
|
65
|
-
totalResults += mods.length;
|
|
66
|
-
console.log(header(`Modules matching "${search}"`, icons.module));
|
|
67
|
-
|
|
68
|
-
for (const mod of mods) {
|
|
69
|
-
const score = mod.reusability_score ?? mod.reusabilityScore ?? 0;
|
|
70
|
-
const scoreColor = score >= 0.7 ? c.green : score >= 0.4 ? c.orange : c.red;
|
|
71
|
-
console.log(` ${c.dim(`#${mod.id}`)} ${c.cyan(`[${mod.language}]`)} ${c.value(mod.name)}`);
|
|
72
|
-
console.log(` ${c.label('File:')} ${c.dim(mod.file_path ?? mod.filePath)} ${c.label('Score:')} ${scoreColor(typeof score === 'number' ? score.toFixed(2) : score)}`);
|
|
73
|
-
if (mod.description) {
|
|
74
|
-
console.log(` ${c.dim(mod.description.slice(0, 120))}`);
|
|
75
|
-
}
|
|
76
|
-
console.log();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// --- Insights ---
|
|
82
|
-
if (searchAll || opts.insightsOnly) {
|
|
83
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
84
|
-
const insights: any = await client.request('research.insights', {
|
|
85
|
-
activeOnly: true,
|
|
86
|
-
limit: 100,
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// Client-side search since insights API may not support text search
|
|
90
|
-
const allInsights = Array.isArray(insights) ? insights : [];
|
|
91
|
-
const searchLower = search.toLowerCase();
|
|
92
|
-
const matched = allInsights.filter((i: { title?: string; description?: string }) =>
|
|
93
|
-
(i.title ?? '').toLowerCase().includes(searchLower) ||
|
|
94
|
-
(i.description ?? '').toLowerCase().includes(searchLower)
|
|
95
|
-
).slice(offset, offset + limit);
|
|
96
|
-
|
|
97
|
-
if (matched.length > 0) {
|
|
98
|
-
totalResults += matched.length;
|
|
99
|
-
console.log(header(`Insights matching "${search}"`, icons.insight));
|
|
100
|
-
|
|
101
|
-
for (const ins of matched) {
|
|
102
|
-
const typeTag = c.cyan(`[${ins.type}]`);
|
|
103
|
-
console.log(` ${typeTag} ${c.value(ins.title)}`);
|
|
104
|
-
if (ins.description) {
|
|
105
|
-
console.log(` ${c.dim(ins.description.slice(0, 150))}`);
|
|
106
|
-
}
|
|
107
|
-
console.log();
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (totalResults === 0) {
|
|
113
|
-
console.log(`\n${icons.search} ${c.dim(`No results found for "${search}".`)}`);
|
|
114
|
-
} else {
|
|
115
|
-
console.log(` ${c.dim(`Page ${page} — showing ${totalResults} result(s). Use --page ${page + 1} for more.`)}`);
|
|
116
|
-
console.log(divider());
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
}
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { withIpc } from '../ipc-helper.js';
|
|
3
|
+
import { c, icons, header, statusBadge, divider } from '../colors.js';
|
|
4
|
+
|
|
5
|
+
export function queryCommand(): Command {
|
|
6
|
+
return new Command('query')
|
|
7
|
+
.description('Search errors, code modules, and insights')
|
|
8
|
+
.argument('<search>', 'Search term')
|
|
9
|
+
.option('-l, --limit <n>', 'Maximum results per category', '10')
|
|
10
|
+
.option('--errors-only', 'Only search errors')
|
|
11
|
+
.option('--modules-only', 'Only search code modules')
|
|
12
|
+
.option('--insights-only', 'Only search insights')
|
|
13
|
+
.option('--page <n>', 'Page number (starting from 1)', '1')
|
|
14
|
+
.action(async (search: string, opts) => {
|
|
15
|
+
await withIpc(async (client) => {
|
|
16
|
+
const limit = parseInt(opts.limit, 10);
|
|
17
|
+
const page = parseInt(opts.page, 10) || 1;
|
|
18
|
+
const offset = (page - 1) * limit;
|
|
19
|
+
const searchAll = !opts.errorsOnly && !opts.modulesOnly && !opts.insightsOnly;
|
|
20
|
+
let totalResults = 0;
|
|
21
|
+
|
|
22
|
+
// --- Errors ---
|
|
23
|
+
if (searchAll || opts.errorsOnly) {
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
|
+
const results: any = await client.request('error.query', {
|
|
26
|
+
search,
|
|
27
|
+
limit: limit + offset,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const errors = Array.isArray(results) ? results.slice(offset, offset + limit) : [];
|
|
31
|
+
if (errors.length > 0) {
|
|
32
|
+
totalResults += errors.length;
|
|
33
|
+
console.log(header(`Errors matching "${search}"`, icons.error));
|
|
34
|
+
|
|
35
|
+
for (const err of errors) {
|
|
36
|
+
const badge = statusBadge(err.resolved ? 'resolved' : 'open');
|
|
37
|
+
const typeTag = c.purple(err.errorType ?? 'unknown');
|
|
38
|
+
console.log(` ${c.dim(`#${err.id}`)} ${badge} ${typeTag}`);
|
|
39
|
+
console.log(` ${c.dim((err.message ?? '').slice(0, 120))}`);
|
|
40
|
+
|
|
41
|
+
// Get solutions for this error
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
+
const solutions: any = await client.request('solution.query', { error_id: err.id });
|
|
44
|
+
if (solutions?.length > 0) {
|
|
45
|
+
console.log(` ${c.green(`${icons.check} ${solutions.length} solution(s)`)}`);
|
|
46
|
+
for (const sol of solutions.slice(0, 3)) {
|
|
47
|
+
console.log(` ${c.dim(icons.corner)} ${c.dim((sol.description ?? '').slice(0, 100))}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
console.log();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// --- Code Modules ---
|
|
56
|
+
if (searchAll || opts.modulesOnly) {
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
|
+
const modules: any = await client.request('code.find', {
|
|
59
|
+
query: search,
|
|
60
|
+
limit: limit + offset,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const mods = Array.isArray(modules) ? modules.slice(offset, offset + limit) : [];
|
|
64
|
+
if (mods.length > 0) {
|
|
65
|
+
totalResults += mods.length;
|
|
66
|
+
console.log(header(`Modules matching "${search}"`, icons.module));
|
|
67
|
+
|
|
68
|
+
for (const mod of mods) {
|
|
69
|
+
const score = mod.reusability_score ?? mod.reusabilityScore ?? 0;
|
|
70
|
+
const scoreColor = score >= 0.7 ? c.green : score >= 0.4 ? c.orange : c.red;
|
|
71
|
+
console.log(` ${c.dim(`#${mod.id}`)} ${c.cyan(`[${mod.language}]`)} ${c.value(mod.name)}`);
|
|
72
|
+
console.log(` ${c.label('File:')} ${c.dim(mod.file_path ?? mod.filePath)} ${c.label('Score:')} ${scoreColor(typeof score === 'number' ? score.toFixed(2) : score)}`);
|
|
73
|
+
if (mod.description) {
|
|
74
|
+
console.log(` ${c.dim(mod.description.slice(0, 120))}`);
|
|
75
|
+
}
|
|
76
|
+
console.log();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// --- Insights ---
|
|
82
|
+
if (searchAll || opts.insightsOnly) {
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
84
|
+
const insights: any = await client.request('research.insights', {
|
|
85
|
+
activeOnly: true,
|
|
86
|
+
limit: 100,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Client-side search since insights API may not support text search
|
|
90
|
+
const allInsights = Array.isArray(insights) ? insights : [];
|
|
91
|
+
const searchLower = search.toLowerCase();
|
|
92
|
+
const matched = allInsights.filter((i: { title?: string; description?: string }) =>
|
|
93
|
+
(i.title ?? '').toLowerCase().includes(searchLower) ||
|
|
94
|
+
(i.description ?? '').toLowerCase().includes(searchLower)
|
|
95
|
+
).slice(offset, offset + limit);
|
|
96
|
+
|
|
97
|
+
if (matched.length > 0) {
|
|
98
|
+
totalResults += matched.length;
|
|
99
|
+
console.log(header(`Insights matching "${search}"`, icons.insight));
|
|
100
|
+
|
|
101
|
+
for (const ins of matched) {
|
|
102
|
+
const typeTag = c.cyan(`[${ins.type}]`);
|
|
103
|
+
console.log(` ${typeTag} ${c.value(ins.title)}`);
|
|
104
|
+
if (ins.description) {
|
|
105
|
+
console.log(` ${c.dim(ins.description.slice(0, 150))}`);
|
|
106
|
+
}
|
|
107
|
+
console.log();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (totalResults === 0) {
|
|
113
|
+
console.log(`\n${icons.search} ${c.dim(`No results found for "${search}".`)}`);
|
|
114
|
+
} else {
|
|
115
|
+
console.log(` ${c.dim(`Page ${page} — showing ${totalResults} result(s). Use --page ${page + 1} for more.`)}`);
|
|
116
|
+
console.log(divider());
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|