@timmeck/brain 1.8.5 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/.dockerignore +11 -0
  2. package/Dockerfile +21 -0
  3. package/README.md +19 -0
  4. package/assets/brain-avatar-256.png +0 -0
  5. package/assets/brain-avatar-512.png +0 -0
  6. package/assets/brain-avatar.png +0 -0
  7. package/brain.log +1164 -0
  8. package/{src/cli/commands/dashboard.ts → dashboard.html} +688 -807
  9. package/dist/api/server.d.ts +4 -18
  10. package/dist/api/server.js +4 -173
  11. package/dist/api/server.js.map +1 -1
  12. package/dist/brain.d.ts +6 -0
  13. package/dist/brain.js +56 -4
  14. package/dist/brain.js.map +1 -1
  15. package/dist/cli/colors.d.ts +4 -25
  16. package/dist/cli/colors.js +3 -89
  17. package/dist/cli/colors.js.map +1 -1
  18. package/dist/cli/commands/peers.d.ts +2 -0
  19. package/dist/cli/commands/peers.js +38 -0
  20. package/dist/cli/commands/peers.js.map +1 -0
  21. package/dist/cli/commands/start.js +54 -17
  22. package/dist/cli/commands/start.js.map +1 -1
  23. package/dist/db/connection.d.ts +1 -2
  24. package/dist/db/connection.js +1 -18
  25. package/dist/db/connection.js.map +1 -1
  26. package/dist/index.js +3 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/ipc/__tests__/protocol.test.d.ts +1 -0
  29. package/dist/ipc/__tests__/protocol.test.js +117 -0
  30. package/dist/ipc/__tests__/protocol.test.js.map +1 -0
  31. package/dist/ipc/client.d.ts +1 -16
  32. package/dist/ipc/client.js +1 -100
  33. package/dist/ipc/client.js.map +1 -1
  34. package/dist/ipc/protocol.d.ts +1 -8
  35. package/dist/ipc/protocol.js +1 -28
  36. package/dist/ipc/protocol.js.map +1 -1
  37. package/dist/ipc/router.js +8 -0
  38. package/dist/ipc/router.js.map +1 -1
  39. package/dist/ipc/server.d.ts +1 -22
  40. package/dist/ipc/server.js +1 -163
  41. package/dist/ipc/server.js.map +1 -1
  42. package/dist/mcp/http-server.d.ts +1 -7
  43. package/dist/mcp/http-server.js +6 -117
  44. package/dist/mcp/http-server.js.map +1 -1
  45. package/dist/mcp/server.js +5 -61
  46. package/dist/mcp/server.js.map +1 -1
  47. package/dist/signals/__tests__/fingerprint.test.d.ts +1 -0
  48. package/dist/signals/__tests__/fingerprint.test.js +118 -0
  49. package/dist/signals/__tests__/fingerprint.test.js.map +1 -0
  50. package/dist/types/ipc.types.d.ts +1 -11
  51. package/dist/utils/__tests__/hash.test.d.ts +1 -0
  52. package/dist/utils/__tests__/hash.test.js +32 -0
  53. package/dist/utils/__tests__/hash.test.js.map +1 -0
  54. package/dist/utils/__tests__/paths.test.d.ts +1 -0
  55. package/dist/utils/__tests__/paths.test.js +75 -0
  56. package/dist/utils/__tests__/paths.test.js.map +1 -0
  57. package/dist/utils/events.d.ts +4 -8
  58. package/dist/utils/events.js +2 -14
  59. package/dist/utils/events.js.map +1 -1
  60. package/dist/utils/hash.d.ts +1 -1
  61. package/dist/utils/hash.js +1 -4
  62. package/dist/utils/hash.js.map +1 -1
  63. package/dist/utils/logger.d.ts +3 -2
  64. package/dist/utils/logger.js +8 -35
  65. package/dist/utils/logger.js.map +1 -1
  66. package/dist/utils/paths.d.ts +2 -1
  67. package/dist/utils/paths.js +4 -13
  68. package/dist/utils/paths.js.map +1 -1
  69. package/gen_avatar.py +142 -0
  70. package/package.json +2 -1
  71. package/BRAIN_PLAN.md +0 -3324
  72. package/src/api/server.ts +0 -395
  73. package/src/brain.ts +0 -266
  74. package/src/cli/colors.ts +0 -116
  75. package/src/cli/commands/config.ts +0 -169
  76. package/src/cli/commands/doctor.ts +0 -124
  77. package/src/cli/commands/explain.ts +0 -83
  78. package/src/cli/commands/export.ts +0 -31
  79. package/src/cli/commands/import.ts +0 -199
  80. package/src/cli/commands/insights.ts +0 -65
  81. package/src/cli/commands/learn.ts +0 -24
  82. package/src/cli/commands/modules.ts +0 -53
  83. package/src/cli/commands/network.ts +0 -67
  84. package/src/cli/commands/projects.ts +0 -42
  85. package/src/cli/commands/query.ts +0 -120
  86. package/src/cli/commands/start.ts +0 -62
  87. package/src/cli/commands/status.ts +0 -75
  88. package/src/cli/commands/stop.ts +0 -34
  89. package/src/cli/ipc-helper.ts +0 -22
  90. package/src/cli/update-check.ts +0 -63
  91. package/src/code/analyzer.ts +0 -117
  92. package/src/code/fingerprint.ts +0 -87
  93. package/src/code/matcher.ts +0 -129
  94. package/src/code/parsers/generic.ts +0 -29
  95. package/src/code/parsers/python.ts +0 -54
  96. package/src/code/parsers/typescript.ts +0 -65
  97. package/src/code/registry.ts +0 -60
  98. package/src/code/scorer.ts +0 -120
  99. package/src/config.ts +0 -135
  100. package/src/dashboard/server.ts +0 -142
  101. package/src/db/connection.ts +0 -22
  102. package/src/db/migrations/001_core_schema.ts +0 -120
  103. package/src/db/migrations/002_learning_schema.ts +0 -38
  104. package/src/db/migrations/003_code_schema.ts +0 -53
  105. package/src/db/migrations/004_synapses_schema.ts +0 -57
  106. package/src/db/migrations/005_fts_indexes.ts +0 -78
  107. package/src/db/migrations/006_synapses_phase3.ts +0 -17
  108. package/src/db/migrations/007_feedback.ts +0 -13
  109. package/src/db/migrations/008_git_integration.ts +0 -38
  110. package/src/db/migrations/009_embeddings.ts +0 -8
  111. package/src/db/migrations/index.ts +0 -70
  112. package/src/db/repositories/antipattern.repository.ts +0 -66
  113. package/src/db/repositories/code-module.repository.ts +0 -142
  114. package/src/db/repositories/error.repository.ts +0 -189
  115. package/src/db/repositories/insight.repository.ts +0 -99
  116. package/src/db/repositories/notification.repository.ts +0 -66
  117. package/src/db/repositories/project.repository.ts +0 -93
  118. package/src/db/repositories/rule.repository.ts +0 -108
  119. package/src/db/repositories/solution.repository.ts +0 -154
  120. package/src/db/repositories/synapse.repository.ts +0 -163
  121. package/src/db/repositories/terminal.repository.ts +0 -101
  122. package/src/embeddings/engine.ts +0 -238
  123. package/src/hooks/post-tool-use.ts +0 -92
  124. package/src/hooks/post-write.ts +0 -129
  125. package/src/index.ts +0 -63
  126. package/src/ipc/client.ts +0 -118
  127. package/src/ipc/protocol.ts +0 -35
  128. package/src/ipc/router.ts +0 -133
  129. package/src/ipc/server.ts +0 -176
  130. package/src/learning/confidence-scorer.ts +0 -80
  131. package/src/learning/decay.ts +0 -46
  132. package/src/learning/learning-engine.ts +0 -170
  133. package/src/learning/pattern-extractor.ts +0 -90
  134. package/src/learning/rule-generator.ts +0 -74
  135. package/src/main.rs:10:5 +0 -0
  136. package/src/matching/error-matcher.ts +0 -166
  137. package/src/matching/fingerprint.ts +0 -34
  138. package/src/matching/similarity.ts +0 -61
  139. package/src/matching/tfidf.ts +0 -74
  140. package/src/matching/tokenizer.ts +0 -41
  141. package/src/mcp/auto-detect.ts +0 -93
  142. package/src/mcp/http-server.ts +0 -140
  143. package/src/mcp/server.ts +0 -73
  144. package/src/mcp/tools.ts +0 -328
  145. package/src/parsing/error-parser.ts +0 -28
  146. package/src/parsing/parsers/compiler.ts +0 -93
  147. package/src/parsing/parsers/generic.ts +0 -28
  148. package/src/parsing/parsers/go.ts +0 -97
  149. package/src/parsing/parsers/node.ts +0 -69
  150. package/src/parsing/parsers/python.ts +0 -62
  151. package/src/parsing/parsers/rust.ts +0 -50
  152. package/src/parsing/parsers/shell.ts +0 -42
  153. package/src/parsing/types.ts +0 -47
  154. package/src/research/gap-analyzer.ts +0 -135
  155. package/src/research/insight-generator.ts +0 -123
  156. package/src/research/research-engine.ts +0 -116
  157. package/src/research/synergy-detector.ts +0 -126
  158. package/src/research/template-extractor.ts +0 -130
  159. package/src/research/trend-analyzer.ts +0 -127
  160. package/src/services/analytics.service.ts +0 -226
  161. package/src/services/code.service.ts +0 -271
  162. package/src/services/error.service.ts +0 -266
  163. package/src/services/git.service.ts +0 -132
  164. package/src/services/notification.service.ts +0 -41
  165. package/src/services/prevention.service.ts +0 -159
  166. package/src/services/research.service.ts +0 -98
  167. package/src/services/solution.service.ts +0 -174
  168. package/src/services/synapse.service.ts +0 -59
  169. package/src/services/terminal.service.ts +0 -81
  170. package/src/synapses/activation.ts +0 -80
  171. package/src/synapses/decay.ts +0 -38
  172. package/src/synapses/hebbian.ts +0 -69
  173. package/src/synapses/pathfinder.ts +0 -81
  174. package/src/synapses/synapse-manager.ts +0 -113
  175. package/src/types/code.types.ts +0 -52
  176. package/src/types/config.types.ts +0 -103
  177. package/src/types/error.types.ts +0 -67
  178. package/src/types/ipc.types.ts +0 -8
  179. package/src/types/mcp.types.ts +0 -53
  180. package/src/types/research.types.ts +0 -28
  181. package/src/types/solution.types.ts +0 -30
  182. package/src/types/synapse.types.ts +0 -50
  183. package/src/utils/events.ts +0 -45
  184. package/src/utils/hash.ts +0 -5
  185. package/src/utils/logger.ts +0 -48
  186. package/src/utils/paths.ts +0 -19
  187. package/tests/e2e/test_code_intelligence.py +0 -1015
  188. package/tests/e2e/test_error_memory.py +0 -451
  189. package/tests/e2e/test_full_integration.py +0 -534
  190. package/tests/fixtures/code-modules/modules.ts +0 -83
  191. package/tests/fixtures/errors/go.ts +0 -9
  192. package/tests/fixtures/errors/node.ts +0 -24
  193. package/tests/fixtures/errors/python.ts +0 -21
  194. package/tests/fixtures/errors/rust.ts +0 -25
  195. package/tests/fixtures/errors/shell.ts +0 -15
  196. package/tests/fixtures/solutions/solutions.ts +0 -27
  197. package/tests/helpers/setup-db.ts +0 -52
  198. package/tests/integration/code-flow.test.ts +0 -86
  199. package/tests/integration/error-flow.test.ts +0 -83
  200. package/tests/integration/ipc-flow.test.ts +0 -166
  201. package/tests/integration/learning-cycle.test.ts +0 -82
  202. package/tests/integration/synapse-flow.test.ts +0 -117
  203. package/tests/unit/code/analyzer.test.ts +0 -58
  204. package/tests/unit/code/fingerprint.test.ts +0 -51
  205. package/tests/unit/code/scorer.test.ts +0 -55
  206. package/tests/unit/learning/confidence-scorer.test.ts +0 -60
  207. package/tests/unit/learning/decay.test.ts +0 -45
  208. package/tests/unit/learning/pattern-extractor.test.ts +0 -50
  209. package/tests/unit/matching/error-matcher.test.ts +0 -69
  210. package/tests/unit/matching/fingerprint.test.ts +0 -47
  211. package/tests/unit/matching/similarity.test.ts +0 -65
  212. package/tests/unit/matching/tfidf.test.ts +0 -71
  213. package/tests/unit/matching/tokenizer.test.ts +0 -83
  214. package/tests/unit/parsing/parsers.test.ts +0 -113
  215. package/tests/unit/research/gap-analyzer.test.ts +0 -45
  216. package/tests/unit/research/trend-analyzer.test.ts +0 -45
  217. package/tests/unit/synapses/activation.test.ts +0 -80
  218. package/tests/unit/synapses/decay.test.ts +0 -27
  219. package/tests/unit/synapses/hebbian.test.ts +0 -96
  220. package/tests/unit/synapses/pathfinder.test.ts +0 -72
  221. package/tsconfig.json +0 -18
package/src/cli/colors.ts DELETED
@@ -1,116 +0,0 @@
1
- import chalk from 'chalk';
2
-
3
- // Brand colors matching the dashboard
4
- export const c = {
5
- // Primary palette
6
- blue: chalk.hex('#5b9cff'),
7
- purple: chalk.hex('#b47aff'),
8
- cyan: chalk.hex('#47e5ff'),
9
- green: chalk.hex('#3dffa0'),
10
- red: chalk.hex('#ff5577'),
11
- orange: chalk.hex('#ffb347'),
12
- dim: chalk.hex('#8b8fb0'),
13
- dimmer: chalk.hex('#4a4d6e'),
14
-
15
- // Semantic
16
- label: chalk.hex('#8b8fb0'),
17
- value: chalk.white.bold,
18
- heading: chalk.hex('#5b9cff').bold,
19
- success: chalk.hex('#3dffa0').bold,
20
- error: chalk.hex('#ff5577').bold,
21
- warn: chalk.hex('#ffb347').bold,
22
- info: chalk.hex('#47e5ff'),
23
- };
24
-
25
- export const icons = {
26
- brain: '🧠',
27
- check: '✓',
28
- cross: '✗',
29
- arrow: '→',
30
- dot: '●',
31
- circle: '○',
32
- bar: '█',
33
- barLight: '░',
34
- dash: '─',
35
- pipe: '│',
36
- corner: '└',
37
- tee: '├',
38
- star: '★',
39
- bolt: '⚡',
40
- search: '🔍',
41
- gear: '⚙',
42
- chart: '📊',
43
- module: '📦',
44
- synapse: '🔗',
45
- insight: '💡',
46
- warn: '⚠',
47
- error: '❌',
48
- ok: '✅',
49
- clock: '⏱',
50
- };
51
-
52
- export function header(title: string, icon?: string): string {
53
- const prefix = icon ? `${icon} ` : '';
54
- const line = c.dimmer(icons.dash.repeat(40));
55
- return `\n${line}\n${prefix}${c.heading(title)}\n${line}`;
56
- }
57
-
58
- export function keyValue(key: string, value: string | number, indent = 2): string {
59
- const pad = ' '.repeat(indent);
60
- return `${pad}${c.label(key + ':')} ${c.value(String(value))}`;
61
- }
62
-
63
- export function statusBadge(status: string): string {
64
- switch (status.toLowerCase()) {
65
- case 'resolved':
66
- case 'active':
67
- case 'running':
68
- return c.green(`[${status.toUpperCase()}]`);
69
- case 'open':
70
- case 'unresolved':
71
- return c.red(`[${status.toUpperCase()}]`);
72
- case 'warning':
73
- return c.warn(`[${status.toUpperCase()}]`);
74
- default:
75
- return c.dim(`[${status.toUpperCase()}]`);
76
- }
77
- }
78
-
79
- export function priorityBadge(priority: number | string): string {
80
- const p = typeof priority === 'string' ? priority.toLowerCase() : '';
81
- const n = typeof priority === 'number' ? priority : 0;
82
- if (p === 'critical' || n >= 9) return c.red.bold(`[CRITICAL]`);
83
- if (p === 'high' || n >= 7) return c.orange.bold(`[HIGH]`);
84
- if (p === 'medium' || n >= 4) return c.blue(`[MEDIUM]`);
85
- return c.dim(`[LOW]`);
86
- }
87
-
88
- export function progressBar(current: number, total: number, width = 20): string {
89
- const pct = Math.min(1, current / Math.max(1, total));
90
- const filled = Math.round(pct * width);
91
- const empty = width - filled;
92
- return c.cyan(icons.bar.repeat(filled)) + c.dimmer(icons.barLight.repeat(empty));
93
- }
94
-
95
- export function divider(width = 40): string {
96
- return c.dimmer(icons.dash.repeat(width));
97
- }
98
-
99
- export function table(rows: string[][], colWidths?: number[]): string {
100
- if (rows.length === 0) return '';
101
- const widths = colWidths ?? rows[0].map((_, i) =>
102
- Math.max(...rows.map(r => stripAnsi(r[i] ?? '').length))
103
- );
104
- return rows.map(row =>
105
- row.map((cell, i) => {
106
- const stripped = stripAnsi(cell);
107
- const pad = Math.max(0, (widths[i] ?? stripped.length) - stripped.length);
108
- return cell + ' '.repeat(pad);
109
- }).join(' ')
110
- ).join('\n');
111
- }
112
-
113
- function stripAnsi(str: string): string {
114
- // eslint-disable-next-line no-control-regex
115
- return str.replace(/\x1b\[[0-9;]*m/g, '');
116
- }
@@ -1,169 +0,0 @@
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, header, divider } from '../colors.js';
6
-
7
- function getConfigPath(): string {
8
- return path.join(getDataDir(), 'config.json');
9
- }
10
-
11
- function readConfig(): Record<string, unknown> {
12
- const configPath = getConfigPath();
13
- if (fs.existsSync(configPath)) {
14
- return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
15
- }
16
- return {};
17
- }
18
-
19
- function writeConfig(config: Record<string, unknown>): void {
20
- const configPath = getConfigPath();
21
- fs.mkdirSync(path.dirname(configPath), { recursive: true });
22
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
23
- }
24
-
25
- function getNestedValue(obj: Record<string, unknown>, keyPath: string): unknown {
26
- const parts = keyPath.split('.');
27
- let current: unknown = obj;
28
- for (const part of parts) {
29
- if (current === null || current === undefined || typeof current !== 'object') return undefined;
30
- current = (current as Record<string, unknown>)[part];
31
- }
32
- return current;
33
- }
34
-
35
- function setNestedValue(obj: Record<string, unknown>, keyPath: string, value: unknown): void {
36
- const parts = keyPath.split('.');
37
- let current: Record<string, unknown> = obj;
38
- for (let i = 0; i < parts.length - 1; i++) {
39
- const part = parts[i];
40
- if (!current[part] || typeof current[part] !== 'object') {
41
- current[part] = {};
42
- }
43
- current = current[part] as Record<string, unknown>;
44
- }
45
- current[parts[parts.length - 1]] = value;
46
- }
47
-
48
- function deleteNestedValue(obj: Record<string, unknown>, keyPath: string): boolean {
49
- const parts = keyPath.split('.');
50
- let current: Record<string, unknown> = obj;
51
- for (let i = 0; i < parts.length - 1; i++) {
52
- const part = parts[i];
53
- if (!current[part] || typeof current[part] !== 'object') return false;
54
- current = current[part] as Record<string, unknown>;
55
- }
56
- const last = parts[parts.length - 1];
57
- if (last in current) {
58
- delete current[last];
59
- return true;
60
- }
61
- return false;
62
- }
63
-
64
- function parseValue(value: string): unknown {
65
- if (value === 'true') return true;
66
- if (value === 'false') return false;
67
- if (value === 'null') return null;
68
- const num = Number(value);
69
- if (!isNaN(num) && value.trim() !== '') return num;
70
- // Try JSON for arrays/objects
71
- if ((value.startsWith('[') && value.endsWith(']')) || (value.startsWith('{') && value.endsWith('}'))) {
72
- try { return JSON.parse(value); } catch { /* fall through */ }
73
- }
74
- return value;
75
- }
76
-
77
- function printObject(obj: unknown, indent = 0): void {
78
- const pad = ' '.repeat(indent);
79
- if (obj === null || obj === undefined) {
80
- console.log(`${pad}${c.dim('(not set)')}`);
81
- return;
82
- }
83
- if (typeof obj !== 'object') {
84
- console.log(`${pad}${c.value(String(obj))}`);
85
- return;
86
- }
87
- for (const [key, val] of Object.entries(obj as Record<string, unknown>)) {
88
- if (val && typeof val === 'object' && !Array.isArray(val)) {
89
- console.log(`${pad}${c.cyan(key + ':')}`);
90
- printObject(val, indent + 2);
91
- } else {
92
- const display = Array.isArray(val) ? JSON.stringify(val) : String(val);
93
- console.log(`${pad}${c.label(key + ':')} ${c.value(display)}`);
94
- }
95
- }
96
- }
97
-
98
- export function configCommand(): Command {
99
- const cmd = new Command('config')
100
- .description('View and modify Brain configuration');
101
-
102
- cmd
103
- .command('show')
104
- .description('Show current configuration')
105
- .argument('[key]', 'Specific config key (e.g., learning.intervalMs)')
106
- .action((key?: string) => {
107
- const config = readConfig();
108
-
109
- if (key) {
110
- const value = getNestedValue(config, key);
111
- if (value === undefined) {
112
- console.log(`${c.dim(`Key "${key}" is not set in config overrides.`)}`);
113
- } else {
114
- console.log(`${c.label(key + ':')} ${c.value(typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value))}`);
115
- }
116
- return;
117
- }
118
-
119
- console.log(header('Brain Configuration', icons.gear));
120
- console.log(` ${c.label('Config file:')} ${c.dim(getConfigPath())}\n`);
121
-
122
- if (Object.keys(config).length === 0) {
123
- console.log(` ${c.dim('No custom overrides. Using defaults.')}`);
124
- console.log(` ${c.dim('Set values with:')} ${c.cyan('brain config set <key> <value>')}`);
125
- } else {
126
- printObject(config, 2);
127
- }
128
- console.log(`\n${divider()}`);
129
- });
130
-
131
- cmd
132
- .command('set')
133
- .description('Set a configuration value')
134
- .argument('<key>', 'Config key path (e.g., learning.intervalMs)')
135
- .argument('<value>', 'Value to set')
136
- .action((key: string, value: string) => {
137
- const config = readConfig();
138
- const parsed = parseValue(value);
139
- setNestedValue(config, key, parsed);
140
- writeConfig(config);
141
-
142
- console.log(`${icons.ok} ${c.label(key)} ${c.dim(icons.arrow)} ${c.value(String(parsed))}`);
143
- console.log(` ${c.dim('Restart the daemon for changes to take effect:')} ${c.cyan('brain stop && brain start')}`);
144
- });
145
-
146
- cmd
147
- .command('delete')
148
- .description('Remove a configuration override (revert to default)')
149
- .argument('<key>', 'Config key path to remove')
150
- .action((key: string) => {
151
- const config = readConfig();
152
- if (deleteNestedValue(config, key)) {
153
- writeConfig(config);
154
- console.log(`${icons.ok} ${c.dim(`Removed "${key}" — will use default value.`)}`);
155
- console.log(` ${c.dim('Restart the daemon for changes to take effect:')} ${c.cyan('brain stop && brain start')}`);
156
- } else {
157
- console.log(`${c.dim(`Key "${key}" not found in config overrides.`)}`);
158
- }
159
- });
160
-
161
- cmd
162
- .command('path')
163
- .description('Show the config file path')
164
- .action(() => {
165
- console.log(getConfigPath());
166
- });
167
-
168
- return cmd;
169
- }
@@ -1,124 +0,0 @@
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; hooks?: Array<{ command?: string }> }) => {
88
- // Support both flat format (h.command) and nested format (h.hooks[].command)
89
- if (h.command?.includes('brain') || h.command?.includes('post-tool-use')) return true;
90
- return h.hooks?.some(
91
- (inner) => inner.command?.includes('brain') || inner.command?.includes('post-tool-use'),
92
- );
93
- },
94
- );
95
- if (hasPostToolUse) {
96
- pass('Auto-detect hook active');
97
- } else {
98
- fail('Auto-detect hook not configured', 'errors won\'t be tracked automatically');
99
- allGood = false;
100
- }
101
- } catch {
102
- fail('Auto-detect hook not configured');
103
- allGood = false;
104
- }
105
-
106
- // 5. DB file size
107
- const dbPath = path.join(getDataDir(), 'brain.db');
108
- try {
109
- const stat = fs.statSync(dbPath);
110
- pass('Database file', `${(stat.size / 1024 / 1024).toFixed(1)} MB at ${dbPath}`);
111
- } catch {
112
- fail('Database file not found');
113
- allGood = false;
114
- }
115
-
116
- console.log();
117
- if (allGood) {
118
- console.log(` ${icons.ok} ${c.success('All checks passed!')}`);
119
- } else {
120
- console.log(` ${icons.warn} ${c.warn('Some checks failed.')} Run ${c.cyan('brain start')} and check your MCP config.`);
121
- }
122
- console.log(`\n${divider()}`);
123
- });
124
- }
@@ -1,83 +0,0 @@
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 +0,0 @@
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
- }