@timmeck/brain 1.9.0 → 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 (214) hide show
  1. package/README.md +19 -0
  2. package/brain.log +1164 -0
  3. package/{src/cli/commands/dashboard.ts → dashboard.html} +688 -807
  4. package/dist/api/server.d.ts +4 -18
  5. package/dist/api/server.js +4 -173
  6. package/dist/api/server.js.map +1 -1
  7. package/dist/brain.d.ts +1 -0
  8. package/dist/brain.js +6 -1
  9. package/dist/brain.js.map +1 -1
  10. package/dist/cli/colors.d.ts +4 -25
  11. package/dist/cli/colors.js +3 -89
  12. package/dist/cli/colors.js.map +1 -1
  13. package/dist/cli/commands/peers.d.ts +2 -0
  14. package/dist/cli/commands/peers.js +38 -0
  15. package/dist/cli/commands/peers.js.map +1 -0
  16. package/dist/db/connection.d.ts +1 -2
  17. package/dist/db/connection.js +1 -18
  18. package/dist/db/connection.js.map +1 -1
  19. package/dist/index.js +3 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/ipc/__tests__/protocol.test.d.ts +1 -0
  22. package/dist/ipc/__tests__/protocol.test.js +117 -0
  23. package/dist/ipc/__tests__/protocol.test.js.map +1 -0
  24. package/dist/ipc/client.d.ts +1 -16
  25. package/dist/ipc/client.js +1 -100
  26. package/dist/ipc/client.js.map +1 -1
  27. package/dist/ipc/protocol.d.ts +1 -8
  28. package/dist/ipc/protocol.js +1 -28
  29. package/dist/ipc/protocol.js.map +1 -1
  30. package/dist/ipc/router.js +8 -0
  31. package/dist/ipc/router.js.map +1 -1
  32. package/dist/ipc/server.d.ts +1 -22
  33. package/dist/ipc/server.js +1 -163
  34. package/dist/ipc/server.js.map +1 -1
  35. package/dist/mcp/http-server.d.ts +1 -7
  36. package/dist/mcp/http-server.js +6 -117
  37. package/dist/mcp/http-server.js.map +1 -1
  38. package/dist/mcp/server.js +5 -61
  39. package/dist/mcp/server.js.map +1 -1
  40. package/dist/signals/__tests__/fingerprint.test.d.ts +1 -0
  41. package/dist/signals/__tests__/fingerprint.test.js +118 -0
  42. package/dist/signals/__tests__/fingerprint.test.js.map +1 -0
  43. package/dist/types/ipc.types.d.ts +1 -11
  44. package/dist/utils/__tests__/hash.test.d.ts +1 -0
  45. package/dist/utils/__tests__/hash.test.js +32 -0
  46. package/dist/utils/__tests__/hash.test.js.map +1 -0
  47. package/dist/utils/__tests__/paths.test.d.ts +1 -0
  48. package/dist/utils/__tests__/paths.test.js +75 -0
  49. package/dist/utils/__tests__/paths.test.js.map +1 -0
  50. package/dist/utils/events.d.ts +4 -8
  51. package/dist/utils/events.js +2 -14
  52. package/dist/utils/events.js.map +1 -1
  53. package/dist/utils/hash.d.ts +1 -1
  54. package/dist/utils/hash.js +1 -4
  55. package/dist/utils/hash.js.map +1 -1
  56. package/dist/utils/logger.d.ts +3 -2
  57. package/dist/utils/logger.js +8 -35
  58. package/dist/utils/logger.js.map +1 -1
  59. package/dist/utils/paths.d.ts +2 -1
  60. package/dist/utils/paths.js +4 -13
  61. package/dist/utils/paths.js.map +1 -1
  62. package/package.json +2 -1
  63. package/BRAIN_PLAN.md +0 -3324
  64. package/reddit_post.md +0 -45
  65. package/src/api/server.ts +0 -395
  66. package/src/brain.ts +0 -313
  67. package/src/cli/colors.ts +0 -116
  68. package/src/cli/commands/config.ts +0 -169
  69. package/src/cli/commands/doctor.ts +0 -124
  70. package/src/cli/commands/explain.ts +0 -83
  71. package/src/cli/commands/export.ts +0 -31
  72. package/src/cli/commands/import.ts +0 -199
  73. package/src/cli/commands/insights.ts +0 -65
  74. package/src/cli/commands/learn.ts +0 -24
  75. package/src/cli/commands/modules.ts +0 -53
  76. package/src/cli/commands/network.ts +0 -67
  77. package/src/cli/commands/projects.ts +0 -42
  78. package/src/cli/commands/query.ts +0 -120
  79. package/src/cli/commands/start.ts +0 -105
  80. package/src/cli/commands/status.ts +0 -75
  81. package/src/cli/commands/stop.ts +0 -34
  82. package/src/cli/ipc-helper.ts +0 -22
  83. package/src/cli/update-check.ts +0 -63
  84. package/src/code/analyzer.ts +0 -117
  85. package/src/code/fingerprint.ts +0 -87
  86. package/src/code/matcher.ts +0 -129
  87. package/src/code/parsers/generic.ts +0 -29
  88. package/src/code/parsers/python.ts +0 -54
  89. package/src/code/parsers/typescript.ts +0 -65
  90. package/src/code/registry.ts +0 -60
  91. package/src/code/scorer.ts +0 -120
  92. package/src/config.ts +0 -135
  93. package/src/dashboard/server.ts +0 -142
  94. package/src/db/connection.ts +0 -22
  95. package/src/db/migrations/001_core_schema.ts +0 -120
  96. package/src/db/migrations/002_learning_schema.ts +0 -38
  97. package/src/db/migrations/003_code_schema.ts +0 -53
  98. package/src/db/migrations/004_synapses_schema.ts +0 -57
  99. package/src/db/migrations/005_fts_indexes.ts +0 -78
  100. package/src/db/migrations/006_synapses_phase3.ts +0 -17
  101. package/src/db/migrations/007_feedback.ts +0 -13
  102. package/src/db/migrations/008_git_integration.ts +0 -38
  103. package/src/db/migrations/009_embeddings.ts +0 -8
  104. package/src/db/migrations/index.ts +0 -70
  105. package/src/db/repositories/antipattern.repository.ts +0 -66
  106. package/src/db/repositories/code-module.repository.ts +0 -142
  107. package/src/db/repositories/error.repository.ts +0 -189
  108. package/src/db/repositories/insight.repository.ts +0 -99
  109. package/src/db/repositories/notification.repository.ts +0 -66
  110. package/src/db/repositories/project.repository.ts +0 -93
  111. package/src/db/repositories/rule.repository.ts +0 -108
  112. package/src/db/repositories/solution.repository.ts +0 -154
  113. package/src/db/repositories/synapse.repository.ts +0 -163
  114. package/src/db/repositories/terminal.repository.ts +0 -101
  115. package/src/embeddings/engine.ts +0 -238
  116. package/src/hooks/post-tool-use.ts +0 -92
  117. package/src/hooks/post-write.ts +0 -129
  118. package/src/index.ts +0 -63
  119. package/src/ipc/client.ts +0 -118
  120. package/src/ipc/protocol.ts +0 -35
  121. package/src/ipc/router.ts +0 -133
  122. package/src/ipc/server.ts +0 -176
  123. package/src/learning/confidence-scorer.ts +0 -80
  124. package/src/learning/decay.ts +0 -46
  125. package/src/learning/learning-engine.ts +0 -170
  126. package/src/learning/pattern-extractor.ts +0 -90
  127. package/src/learning/rule-generator.ts +0 -74
  128. package/src/main.rs:10:5 +0 -0
  129. package/src/matching/error-matcher.ts +0 -166
  130. package/src/matching/fingerprint.ts +0 -34
  131. package/src/matching/similarity.ts +0 -61
  132. package/src/matching/tfidf.ts +0 -74
  133. package/src/matching/tokenizer.ts +0 -41
  134. package/src/mcp/auto-detect.ts +0 -93
  135. package/src/mcp/http-server.ts +0 -140
  136. package/src/mcp/server.ts +0 -73
  137. package/src/mcp/tools.ts +0 -328
  138. package/src/parsing/error-parser.ts +0 -28
  139. package/src/parsing/parsers/compiler.ts +0 -93
  140. package/src/parsing/parsers/generic.ts +0 -28
  141. package/src/parsing/parsers/go.ts +0 -97
  142. package/src/parsing/parsers/node.ts +0 -69
  143. package/src/parsing/parsers/python.ts +0 -62
  144. package/src/parsing/parsers/rust.ts +0 -50
  145. package/src/parsing/parsers/shell.ts +0 -42
  146. package/src/parsing/types.ts +0 -47
  147. package/src/research/gap-analyzer.ts +0 -135
  148. package/src/research/insight-generator.ts +0 -123
  149. package/src/research/research-engine.ts +0 -116
  150. package/src/research/synergy-detector.ts +0 -126
  151. package/src/research/template-extractor.ts +0 -130
  152. package/src/research/trend-analyzer.ts +0 -127
  153. package/src/services/analytics.service.ts +0 -226
  154. package/src/services/code.service.ts +0 -271
  155. package/src/services/error.service.ts +0 -266
  156. package/src/services/git.service.ts +0 -132
  157. package/src/services/notification.service.ts +0 -41
  158. package/src/services/prevention.service.ts +0 -159
  159. package/src/services/research.service.ts +0 -98
  160. package/src/services/solution.service.ts +0 -174
  161. package/src/services/synapse.service.ts +0 -59
  162. package/src/services/terminal.service.ts +0 -81
  163. package/src/synapses/activation.ts +0 -80
  164. package/src/synapses/decay.ts +0 -38
  165. package/src/synapses/hebbian.ts +0 -69
  166. package/src/synapses/pathfinder.ts +0 -81
  167. package/src/synapses/synapse-manager.ts +0 -113
  168. package/src/types/code.types.ts +0 -52
  169. package/src/types/config.types.ts +0 -103
  170. package/src/types/error.types.ts +0 -67
  171. package/src/types/ipc.types.ts +0 -8
  172. package/src/types/mcp.types.ts +0 -53
  173. package/src/types/research.types.ts +0 -28
  174. package/src/types/solution.types.ts +0 -30
  175. package/src/types/synapse.types.ts +0 -50
  176. package/src/utils/events.ts +0 -45
  177. package/src/utils/hash.ts +0 -5
  178. package/src/utils/logger.ts +0 -48
  179. package/src/utils/paths.ts +0 -19
  180. package/tests/e2e/test_code_intelligence.py +0 -1015
  181. package/tests/e2e/test_error_memory.py +0 -451
  182. package/tests/e2e/test_full_integration.py +0 -534
  183. package/tests/fixtures/code-modules/modules.ts +0 -83
  184. package/tests/fixtures/errors/go.ts +0 -9
  185. package/tests/fixtures/errors/node.ts +0 -24
  186. package/tests/fixtures/errors/python.ts +0 -21
  187. package/tests/fixtures/errors/rust.ts +0 -25
  188. package/tests/fixtures/errors/shell.ts +0 -15
  189. package/tests/fixtures/solutions/solutions.ts +0 -27
  190. package/tests/helpers/setup-db.ts +0 -52
  191. package/tests/integration/code-flow.test.ts +0 -86
  192. package/tests/integration/error-flow.test.ts +0 -83
  193. package/tests/integration/ipc-flow.test.ts +0 -166
  194. package/tests/integration/learning-cycle.test.ts +0 -82
  195. package/tests/integration/synapse-flow.test.ts +0 -117
  196. package/tests/unit/code/analyzer.test.ts +0 -58
  197. package/tests/unit/code/fingerprint.test.ts +0 -51
  198. package/tests/unit/code/scorer.test.ts +0 -55
  199. package/tests/unit/learning/confidence-scorer.test.ts +0 -60
  200. package/tests/unit/learning/decay.test.ts +0 -45
  201. package/tests/unit/learning/pattern-extractor.test.ts +0 -50
  202. package/tests/unit/matching/error-matcher.test.ts +0 -69
  203. package/tests/unit/matching/fingerprint.test.ts +0 -47
  204. package/tests/unit/matching/similarity.test.ts +0 -65
  205. package/tests/unit/matching/tfidf.test.ts +0 -71
  206. package/tests/unit/matching/tokenizer.test.ts +0 -83
  207. package/tests/unit/parsing/parsers.test.ts +0 -113
  208. package/tests/unit/research/gap-analyzer.test.ts +0 -45
  209. package/tests/unit/research/trend-analyzer.test.ts +0 -45
  210. package/tests/unit/synapses/activation.test.ts +0 -80
  211. package/tests/unit/synapses/decay.test.ts +0 -27
  212. package/tests/unit/synapses/hebbian.test.ts +0 -96
  213. package/tests/unit/synapses/pathfinder.test.ts +0 -72
  214. package/tsconfig.json +0 -18
@@ -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
- }
@@ -1,199 +0,0 @@
1
- import { Command } from 'commander';
2
- import { withIpc } from '../ipc-helper.js';
3
- import { readdirSync, readFileSync, statSync } from 'fs';
4
- import { resolve, basename, relative, extname } from 'path';
5
- import { c, icons, header, divider, progressBar } from '../colors.js';
6
-
7
- const DEFAULT_EXTENSIONS = new Set([
8
- '.ts', '.tsx', '.js', '.jsx', '.py', '.rs', '.go',
9
- '.java', '.c', '.cpp', '.h', '.hpp', '.rb', '.sh',
10
- '.html', '.css', '.scss', '.json', '.yaml', '.yml', '.toml',
11
- '.md', '.sql', '.php', '.svelte', '.vue', '.astro',
12
- ]);
13
-
14
- const EXCLUDE_DIRS = new Set([
15
- 'node_modules', 'dist', 'build', '.git', '.next',
16
- '__pycache__', 'vendor', 'coverage', '.cache', '.turbo',
17
- '.nuxt', '.output', 'target', 'out', 'venv', '.venv',
18
- 'env', '.env', 'site-packages',
19
- ]);
20
-
21
- const EXCLUDE_PATTERNS = [/\.min\./, /\.bundle\./, /\.d\.ts$/];
22
-
23
- const LANG_MAP: Record<string, string> = {
24
- ts: 'typescript', tsx: 'typescript', js: 'javascript', jsx: 'javascript',
25
- py: 'python', rs: 'rust', go: 'go', java: 'java',
26
- c: 'c', cpp: 'cpp', h: 'c', hpp: 'cpp',
27
- rb: 'ruby', sh: 'shell', bash: 'shell',
28
- html: 'html', css: 'css', scss: 'scss',
29
- json: 'json', yaml: 'yaml', yml: 'yaml', toml: 'toml',
30
- md: 'markdown', sql: 'sql', php: 'php',
31
- svelte: 'svelte', vue: 'vue', astro: 'astro',
32
- };
33
-
34
- function detectLanguage(filePath: string): string {
35
- const ext = extname(filePath).slice(1).toLowerCase();
36
- return LANG_MAP[ext] ?? ext;
37
- }
38
-
39
- function findSourceFiles(dir: string, extensions: Set<string>, maxSizeBytes: number): string[] {
40
- const files: string[] = [];
41
-
42
- function walk(current: string): void {
43
- let entries;
44
- try {
45
- entries = readdirSync(current, { withFileTypes: true });
46
- } catch {
47
- return; // skip unreadable directories
48
- }
49
-
50
- for (const entry of entries) {
51
- const fullPath = resolve(current, entry.name);
52
-
53
- if (entry.isDirectory()) {
54
- if (!EXCLUDE_DIRS.has(entry.name)) {
55
- walk(fullPath);
56
- }
57
- continue;
58
- }
59
-
60
- if (!entry.isFile()) continue;
61
-
62
- const ext = extname(entry.name).toLowerCase();
63
- if (!extensions.has(ext)) continue;
64
- if (EXCLUDE_PATTERNS.some(p => p.test(entry.name))) continue;
65
-
66
- try {
67
- const stat = statSync(fullPath);
68
- if (stat.size > maxSizeBytes) continue;
69
- files.push(fullPath);
70
- } catch {
71
- // skip unreadable files
72
- }
73
- }
74
- }
75
-
76
- walk(dir);
77
- return files.sort();
78
- }
79
-
80
- export function importCommand(): Command {
81
- return new Command('import')
82
- .description('Import source files from a project directory into Brain')
83
- .argument('<directory>', 'Project directory to scan')
84
- .option('-p, --project <name>', 'Project name (default: directory basename)')
85
- .option('-e, --extensions <list>', 'Comma-separated extensions (default: ts,tsx,js,jsx,py,rs,go,java,c,cpp,h,hpp,rb,sh,html,css,scss,json,yaml,yml,toml,md,sql,php,svelte,vue,astro)')
86
- .option('--dry-run', 'List files that would be imported without importing')
87
- .option('--max-size <kb>', 'Skip files larger than N KB', '100')
88
- .action(async (directory: string, opts) => {
89
- const dir = resolve(directory);
90
- const projectName = opts.project ?? basename(dir);
91
- const maxSizeKb = parseInt(opts.maxSize, 10) || 100;
92
- const maxSizeBytes = maxSizeKb * 1024;
93
-
94
- // Parse extensions
95
- let extensions = DEFAULT_EXTENSIONS;
96
- if (opts.extensions) {
97
- extensions = new Set(
98
- opts.extensions.split(',').map((e: string) => {
99
- const trimmed = e.trim();
100
- return trimmed.startsWith('.') ? trimmed : `.${trimmed}`;
101
- })
102
- );
103
- }
104
-
105
- // Verify directory exists
106
- try {
107
- const stat = statSync(dir);
108
- if (!stat.isDirectory()) {
109
- console.error(`Not a directory: ${dir}`);
110
- process.exit(1);
111
- }
112
- } catch {
113
- console.error(`Directory not found: ${dir}`);
114
- process.exit(1);
115
- }
116
-
117
- console.log(`${icons.search} ${c.info('Scanning')} ${c.value(dir)} ...`);
118
- const files = findSourceFiles(dir, extensions, maxSizeBytes);
119
-
120
- if (files.length === 0) {
121
- console.log(`${c.dim('No source files found.')}`);
122
- return;
123
- }
124
-
125
- console.log(`${icons.ok} Found ${c.value(files.length)} source files.\n`);
126
-
127
- if (opts.dryRun) {
128
- for (const f of files) {
129
- const rel = relative(dir, f);
130
- const lang = detectLanguage(f);
131
- console.log(` ${c.cyan(`[${lang}]`)} ${c.dim(rel)}`);
132
- }
133
- console.log(`\n${c.value(files.length)} files would be imported as project ${c.cyan(`"${projectName}"`)}.`);
134
- return;
135
- }
136
-
137
- // Import via IPC
138
- await withIpc(async (client) => {
139
- let imported = 0;
140
- let newCount = 0;
141
- let existingCount = 0;
142
- let failedCount = 0;
143
- let totalScore = 0;
144
-
145
- for (let i = 0; i < files.length; i++) {
146
- const filePath = files[i];
147
- const rel = relative(dir, filePath);
148
- const fileName = basename(filePath);
149
- const language = detectLanguage(filePath);
150
-
151
- let source: string;
152
- try {
153
- source = readFileSync(filePath, 'utf-8');
154
- } catch {
155
- failedCount++;
156
- process.stdout.write(` ${c.dim(`[${i + 1}/${files.length}]`)} ${c.dim(rel)} ${c.red('— read error')}\n`);
157
- continue;
158
- }
159
-
160
- // Skip empty files
161
- if (!source.trim()) continue;
162
-
163
- try {
164
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
165
- const result: any = await client.request('code.analyze', {
166
- project: projectName,
167
- name: fileName,
168
- filePath: rel,
169
- language,
170
- source,
171
- });
172
-
173
- const score = result.reusabilityScore ?? 0;
174
- const scoreColor = score >= 0.7 ? c.green : score >= 0.4 ? c.orange : c.red;
175
- const statusTag = result.isNew ? c.green('new') : c.dim('existing');
176
- totalScore += score;
177
- imported++;
178
-
179
- if (result.isNew) newCount++;
180
- else existingCount++;
181
-
182
- process.stdout.write(` ${c.dim(`[${i + 1}/${files.length}]`)} ${c.dim(rel)} ${c.dim(icons.arrow)} ${scoreColor(score.toFixed(2))} (${statusTag})\n`);
183
- } catch (err) {
184
- failedCount++;
185
- const msg = err instanceof Error ? err.message : String(err);
186
- process.stdout.write(` ${c.dim(`[${i + 1}/${files.length}]`)} ${c.dim(rel)} ${c.red(`— ${msg.slice(0, 80)}`)}\n`);
187
- }
188
- }
189
-
190
- const avgScore = imported > 0 ? (totalScore / imported).toFixed(2) : '0';
191
- console.log(header('Import Summary', icons.module));
192
- console.log(` ${c.label('Project:')} ${c.cyan(projectName)}`);
193
- console.log(` ${c.label('Imported:')} ${c.value(imported)} (${c.green(`${newCount} new`)}, ${c.dim(`${existingCount} existing`)})`);
194
- if (failedCount > 0) console.log(` ${c.label('Failed:')} ${c.red(failedCount)}`);
195
- console.log(` ${c.label('Avg score:')} ${c.value(avgScore)} ${progressBar(parseFloat(avgScore), 1)}`);
196
- console.log(divider());
197
- });
198
- });
199
- }
@@ -1,65 +0,0 @@
1
- import { Command } from 'commander';
2
- import { withIpc } from '../ipc-helper.js';
3
- import { c, icons, header, priorityBadge, divider } from '../colors.js';
4
-
5
- const TYPE_ICONS: Record<string, string> = {
6
- trend: '📈',
7
- pattern: '🔄',
8
- gap: '⚠',
9
- synergy: '⚡',
10
- optimization: '🎯',
11
- template_candidate: '🎨',
12
- project_suggestion: '💡',
13
- warning: '🚨',
14
- suggestion: '💡',
15
- };
16
-
17
- export function insightsCommand(): Command {
18
- return new Command('insights')
19
- .description('Show research insights')
20
- .option('--type <type>', 'Filter by type: trend, pattern, gap, synergy, optimization, template_candidate, project_suggestion, warning')
21
- .option('-l, --limit <n>', 'Maximum results', '20')
22
- .action(async (opts) => {
23
- await withIpc(async (client) => {
24
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
- const insights: any = await client.request('research.insights', {
26
- type: opts.type,
27
- activeOnly: true,
28
- limit: parseInt(opts.limit, 10),
29
- });
30
-
31
- if (!insights?.length) {
32
- console.log(`${icons.insight} ${c.dim('No active insights.')}`);
33
- return;
34
- }
35
-
36
- console.log(header(`${insights.length} Insights`, icons.insight));
37
-
38
- // Group by type
39
- const byType: Record<string, number> = {};
40
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
- for (const ins of insights as any[]) {
42
- byType[ins.type] = (byType[ins.type] || 0) + 1;
43
- }
44
- const typeSummary = Object.entries(byType)
45
- .sort((a, b) => b[1] - a[1])
46
- .map(([t, count]) => `${TYPE_ICONS[t] ?? '•'} ${c.cyan(t)} ${c.dim(`(${count})`)}`)
47
- .join(' ');
48
- console.log(` ${typeSummary}\n`);
49
-
50
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
- for (const ins of insights as any[]) {
52
- const typeIcon = TYPE_ICONS[ins.type] ?? '•';
53
- const pBadge = priorityBadge(ins.priority ?? 0);
54
- const typeTag = c.cyan(`[${ins.type}]`);
55
-
56
- console.log(` ${typeIcon} ${typeTag} ${pBadge} ${c.value(ins.title)}`);
57
- if (ins.description) {
58
- console.log(` ${c.dim(ins.description.slice(0, 150))}`);
59
- }
60
- console.log();
61
- }
62
- console.log(divider());
63
- });
64
- });
65
- }
@@ -1,24 +0,0 @@
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 learnCommand(): Command {
6
- return new Command('learn')
7
- .description('Trigger a learning cycle manually (pattern extraction + rule generation)')
8
- .action(async () => {
9
- await withIpc(async (client) => {
10
- console.log(`${icons.brain} ${c.info('Running learning cycle...')}`);
11
-
12
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
- const result: any = await client.request('learning.run', {});
14
-
15
- console.log(header('Learning Cycle Complete', icons.bolt));
16
- console.log(keyValue('New patterns', result.newPatterns ?? 0));
17
- console.log(keyValue('Updated rules', result.updatedRules ?? 0));
18
- console.log(keyValue('Pruned rules', result.prunedRules ?? 0));
19
- console.log(keyValue('New anti-patterns', result.newAntipatterns ?? 0));
20
- console.log(keyValue('Duration', `${result.duration ?? 0}ms`));
21
- console.log(`\n${divider()}`);
22
- });
23
- });
24
- }
@@ -1,53 +0,0 @@
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 +0,0 @@
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
- }