@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
@@ -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
- }
@@ -1,42 +0,0 @@
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 +0,0 @@
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,62 +0,0 @@
1
- import { Command } from 'commander';
2
- import { spawn } from 'node:child_process';
3
- import fs from 'node:fs';
4
- import path from 'node:path';
5
- import { getDataDir } from '../../utils/paths.js';
6
- import { c, icons } from '../colors.js';
7
- import { checkForUpdate } from '../update-check.js';
8
-
9
- export function startCommand(): Command {
10
- return new Command('start')
11
- .description('Start the Brain daemon')
12
- .option('-f, --foreground', 'Run in foreground (no detach)')
13
- .option('-c, --config <path>', 'Config file path')
14
- .action((opts) => {
15
- const pidPath = path.join(getDataDir(), 'brain.pid');
16
-
17
- // Check if already running
18
- if (fs.existsSync(pidPath)) {
19
- const pid = parseInt(fs.readFileSync(pidPath, 'utf8').trim(), 10);
20
- try {
21
- process.kill(pid, 0); // Check if process exists
22
- console.log(`${icons.brain} Brain daemon is ${c.green('already running')} ${c.dim(`(PID: ${pid})`)}`);
23
- return;
24
- } catch {
25
- // PID file stale, remove it
26
- fs.unlinkSync(pidPath);
27
- }
28
- }
29
-
30
- if (opts.foreground) {
31
- // Run in foreground — import dynamically to avoid loading everything at CLI parse time
32
- import('../../brain.js').then(({ BrainCore }) => {
33
- const core = new BrainCore();
34
- core.start(opts.config);
35
- });
36
- return;
37
- }
38
-
39
- // Spawn detached daemon
40
- const args = ['daemon'];
41
- if (opts.config) args.push('-c', opts.config);
42
-
43
- const entryPoint = path.resolve(import.meta.dirname, '../../index.js');
44
- const child = spawn(process.execPath, [entryPoint, ...args], {
45
- detached: true,
46
- stdio: 'ignore',
47
- });
48
- child.unref();
49
-
50
- console.log(`${icons.brain} ${c.info('Brain daemon starting')} ${c.dim(`(PID: ${child.pid})`)}`);
51
-
52
- // Wait briefly for PID file to appear
53
- setTimeout(async () => {
54
- if (fs.existsSync(pidPath)) {
55
- console.log(`${icons.ok} ${c.success('Brain daemon started successfully.')}`);
56
- } else {
57
- console.log(`${icons.clock} ${c.warn('Brain daemon may still be starting.')} Check: ${c.cyan('brain status')}`);
58
- }
59
- await checkForUpdate();
60
- }, 1000);
61
- });
62
- }