@timmeck/brain 1.0.0 → 1.1.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 (198) hide show
  1. package/BRAIN_PLAN.md +3324 -3324
  2. package/LICENSE +21 -21
  3. package/README.md +194 -188
  4. package/dist/brain.js +2 -0
  5. package/dist/brain.js.map +1 -1
  6. package/dist/cli/colors.d.ts +50 -0
  7. package/dist/cli/colors.js +106 -0
  8. package/dist/cli/colors.js.map +1 -0
  9. package/dist/cli/commands/config.d.ts +2 -0
  10. package/dist/cli/commands/config.js +165 -0
  11. package/dist/cli/commands/config.js.map +1 -0
  12. package/dist/cli/commands/dashboard.js +222 -8
  13. package/dist/cli/commands/dashboard.js.map +1 -1
  14. package/dist/cli/commands/export.js +3 -0
  15. package/dist/cli/commands/export.js.map +1 -1
  16. package/dist/cli/commands/import.js +24 -15
  17. package/dist/cli/commands/import.js.map +1 -1
  18. package/dist/cli/commands/insights.js +33 -6
  19. package/dist/cli/commands/insights.js.map +1 -1
  20. package/dist/cli/commands/learn.d.ts +2 -0
  21. package/dist/cli/commands/learn.js +22 -0
  22. package/dist/cli/commands/learn.js.map +1 -0
  23. package/dist/cli/commands/modules.js +25 -6
  24. package/dist/cli/commands/modules.js.map +1 -1
  25. package/dist/cli/commands/network.js +15 -9
  26. package/dist/cli/commands/network.js.map +1 -1
  27. package/dist/cli/commands/query.js +92 -25
  28. package/dist/cli/commands/query.js.map +1 -1
  29. package/dist/cli/commands/start.js +5 -4
  30. package/dist/cli/commands/start.js.map +1 -1
  31. package/dist/cli/commands/status.js +19 -16
  32. package/dist/cli/commands/status.js.map +1 -1
  33. package/dist/cli/commands/stop.js +5 -4
  34. package/dist/cli/commands/stop.js.map +1 -1
  35. package/dist/cli/ipc-helper.js +4 -3
  36. package/dist/cli/ipc-helper.js.map +1 -1
  37. package/dist/db/migrations/001_core_schema.js +115 -115
  38. package/dist/db/migrations/002_learning_schema.js +33 -33
  39. package/dist/db/migrations/003_code_schema.js +48 -48
  40. package/dist/db/migrations/004_synapses_schema.js +52 -52
  41. package/dist/db/migrations/005_fts_indexes.js +73 -73
  42. package/dist/db/migrations/index.js +6 -6
  43. package/dist/db/repositories/antipattern.repository.js +3 -3
  44. package/dist/db/repositories/code-module.repository.d.ts +1 -0
  45. package/dist/db/repositories/code-module.repository.js +8 -0
  46. package/dist/db/repositories/code-module.repository.js.map +1 -1
  47. package/dist/db/repositories/error.repository.js +46 -46
  48. package/dist/db/repositories/insight.repository.js +3 -3
  49. package/dist/db/repositories/notification.repository.js +3 -3
  50. package/dist/db/repositories/project.repository.js +21 -21
  51. package/dist/db/repositories/rule.repository.js +24 -24
  52. package/dist/db/repositories/solution.repository.js +50 -50
  53. package/dist/db/repositories/synapse.repository.js +18 -18
  54. package/dist/db/repositories/terminal.repository.js +24 -24
  55. package/dist/index.js +4 -0
  56. package/dist/index.js.map +1 -1
  57. package/dist/ipc/router.d.ts +2 -0
  58. package/dist/ipc/router.js +7 -1
  59. package/dist/ipc/router.js.map +1 -1
  60. package/dist/services/code.service.d.ts +1 -1
  61. package/dist/services/code.service.js +5 -2
  62. package/dist/services/code.service.js.map +1 -1
  63. package/package.json +5 -4
  64. package/src/brain.ts +3 -0
  65. package/src/cli/colors.ts +116 -0
  66. package/src/cli/commands/config.ts +169 -0
  67. package/src/cli/commands/dashboard.ts +231 -8
  68. package/src/cli/commands/export.ts +4 -0
  69. package/src/cli/commands/import.ts +24 -15
  70. package/src/cli/commands/insights.ts +37 -5
  71. package/src/cli/commands/learn.ts +24 -0
  72. package/src/cli/commands/modules.ts +28 -5
  73. package/src/cli/commands/network.ts +15 -9
  74. package/src/cli/commands/query.ts +103 -26
  75. package/src/cli/commands/start.ts +5 -4
  76. package/src/cli/commands/status.ts +19 -16
  77. package/src/cli/commands/stop.ts +5 -4
  78. package/src/cli/ipc-helper.ts +4 -3
  79. package/src/code/analyzer.ts +77 -77
  80. package/src/code/fingerprint.ts +87 -87
  81. package/src/code/matcher.ts +64 -64
  82. package/src/code/parsers/generic.ts +29 -29
  83. package/src/code/parsers/python.ts +54 -54
  84. package/src/code/parsers/typescript.ts +65 -65
  85. package/src/code/registry.ts +60 -60
  86. package/src/code/scorer.ts +108 -108
  87. package/src/config.ts +111 -111
  88. package/src/db/connection.ts +22 -22
  89. package/src/db/migrations/001_core_schema.ts +120 -120
  90. package/src/db/migrations/002_learning_schema.ts +38 -38
  91. package/src/db/migrations/003_code_schema.ts +53 -53
  92. package/src/db/migrations/004_synapses_schema.ts +57 -57
  93. package/src/db/migrations/005_fts_indexes.ts +78 -78
  94. package/src/db/migrations/006_synapses_phase3.ts +17 -17
  95. package/src/db/migrations/index.ts +64 -64
  96. package/src/db/repositories/antipattern.repository.ts +66 -66
  97. package/src/db/repositories/code-module.repository.ts +9 -0
  98. package/src/db/repositories/error.repository.ts +149 -149
  99. package/src/db/repositories/insight.repository.ts +78 -78
  100. package/src/db/repositories/notification.repository.ts +66 -66
  101. package/src/db/repositories/project.repository.ts +93 -93
  102. package/src/db/repositories/rule.repository.ts +108 -108
  103. package/src/db/repositories/solution.repository.ts +154 -154
  104. package/src/db/repositories/synapse.repository.ts +153 -153
  105. package/src/db/repositories/terminal.repository.ts +101 -101
  106. package/src/hooks/post-tool-use.ts +90 -90
  107. package/src/hooks/post-write.ts +117 -117
  108. package/src/index.ts +4 -0
  109. package/src/ipc/client.ts +118 -118
  110. package/src/ipc/protocol.ts +35 -35
  111. package/src/ipc/router.ts +9 -1
  112. package/src/ipc/server.ts +110 -110
  113. package/src/learning/confidence-scorer.ts +47 -47
  114. package/src/learning/decay.ts +46 -46
  115. package/src/learning/learning-engine.ts +162 -162
  116. package/src/learning/pattern-extractor.ts +90 -90
  117. package/src/learning/rule-generator.ts +74 -74
  118. package/src/matching/error-matcher.ts +115 -115
  119. package/src/matching/fingerprint.ts +29 -29
  120. package/src/matching/similarity.ts +61 -61
  121. package/src/matching/tfidf.ts +74 -74
  122. package/src/matching/tokenizer.ts +41 -41
  123. package/src/mcp/auto-detect.ts +93 -93
  124. package/src/mcp/server.ts +73 -73
  125. package/src/mcp/tools.ts +290 -290
  126. package/src/parsing/error-parser.ts +28 -28
  127. package/src/parsing/parsers/compiler.ts +93 -93
  128. package/src/parsing/parsers/generic.ts +28 -28
  129. package/src/parsing/parsers/go.ts +97 -97
  130. package/src/parsing/parsers/node.ts +69 -69
  131. package/src/parsing/parsers/python.ts +62 -62
  132. package/src/parsing/parsers/rust.ts +50 -50
  133. package/src/parsing/parsers/shell.ts +42 -42
  134. package/src/parsing/types.ts +47 -47
  135. package/src/research/gap-analyzer.ts +135 -135
  136. package/src/research/insight-generator.ts +123 -123
  137. package/src/research/research-engine.ts +116 -116
  138. package/src/research/synergy-detector.ts +126 -126
  139. package/src/research/template-extractor.ts +130 -130
  140. package/src/research/trend-analyzer.ts +127 -127
  141. package/src/services/analytics.service.ts +87 -87
  142. package/src/services/code.service.ts +5 -2
  143. package/src/services/error.service.ts +164 -164
  144. package/src/services/notification.service.ts +41 -41
  145. package/src/services/prevention.service.ts +119 -119
  146. package/src/services/research.service.ts +93 -93
  147. package/src/services/solution.service.ts +116 -116
  148. package/src/services/synapse.service.ts +59 -59
  149. package/src/services/terminal.service.ts +81 -81
  150. package/src/synapses/activation.ts +80 -80
  151. package/src/synapses/decay.ts +38 -38
  152. package/src/synapses/hebbian.ts +69 -69
  153. package/src/synapses/pathfinder.ts +81 -81
  154. package/src/synapses/synapse-manager.ts +109 -109
  155. package/src/types/code.types.ts +52 -52
  156. package/src/types/config.types.ts +79 -79
  157. package/src/types/error.types.ts +67 -67
  158. package/src/types/ipc.types.ts +8 -8
  159. package/src/types/mcp.types.ts +53 -53
  160. package/src/types/research.types.ts +28 -28
  161. package/src/types/solution.types.ts +30 -30
  162. package/src/types/synapse.types.ts +49 -49
  163. package/src/utils/events.ts +45 -45
  164. package/src/utils/hash.ts +5 -5
  165. package/src/utils/logger.ts +48 -48
  166. package/src/utils/paths.ts +19 -19
  167. package/tests/fixtures/code-modules/modules.ts +83 -83
  168. package/tests/fixtures/errors/go.ts +9 -9
  169. package/tests/fixtures/errors/node.ts +24 -24
  170. package/tests/fixtures/errors/python.ts +21 -21
  171. package/tests/fixtures/errors/rust.ts +25 -25
  172. package/tests/fixtures/errors/shell.ts +15 -15
  173. package/tests/fixtures/solutions/solutions.ts +27 -27
  174. package/tests/helpers/setup-db.ts +52 -52
  175. package/tests/integration/code-flow.test.ts +86 -86
  176. package/tests/integration/error-flow.test.ts +83 -83
  177. package/tests/integration/ipc-flow.test.ts +166 -166
  178. package/tests/integration/learning-cycle.test.ts +82 -82
  179. package/tests/integration/synapse-flow.test.ts +117 -117
  180. package/tests/unit/code/analyzer.test.ts +58 -58
  181. package/tests/unit/code/fingerprint.test.ts +51 -51
  182. package/tests/unit/code/scorer.test.ts +55 -55
  183. package/tests/unit/learning/confidence-scorer.test.ts +60 -60
  184. package/tests/unit/learning/decay.test.ts +45 -45
  185. package/tests/unit/learning/pattern-extractor.test.ts +50 -50
  186. package/tests/unit/matching/error-matcher.test.ts +69 -69
  187. package/tests/unit/matching/fingerprint.test.ts +47 -47
  188. package/tests/unit/matching/similarity.test.ts +65 -65
  189. package/tests/unit/matching/tfidf.test.ts +71 -71
  190. package/tests/unit/matching/tokenizer.test.ts +83 -83
  191. package/tests/unit/parsing/parsers.test.ts +113 -113
  192. package/tests/unit/research/gap-analyzer.test.ts +45 -45
  193. package/tests/unit/research/trend-analyzer.test.ts +45 -45
  194. package/tests/unit/synapses/activation.test.ts +80 -80
  195. package/tests/unit/synapses/decay.test.ts +27 -27
  196. package/tests/unit/synapses/hebbian.test.ts +96 -96
  197. package/tests/unit/synapses/pathfinder.test.ts +72 -72
  198. package/tsconfig.json +18 -18
@@ -1,78 +1,78 @@
1
- import type Database from 'better-sqlite3';
2
-
3
- export function up(db: Database.Database): void {
4
- db.exec(`
5
- -- Full-text search for errors
6
- CREATE VIRTUAL TABLE IF NOT EXISTS errors_fts USING fts5(
7
- type, message, raw_output, context, file_path,
8
- content='errors',
9
- content_rowid='id'
10
- );
11
-
12
- -- Sync triggers for errors_fts
13
- CREATE TRIGGER IF NOT EXISTS errors_ai AFTER INSERT ON errors BEGIN
14
- INSERT INTO errors_fts(rowid, type, message, raw_output, context, file_path)
15
- VALUES (new.id, new.type, new.message, new.raw_output, new.context, new.file_path);
16
- END;
17
-
18
- CREATE TRIGGER IF NOT EXISTS errors_ad AFTER DELETE ON errors BEGIN
19
- INSERT INTO errors_fts(errors_fts, rowid, type, message, raw_output, context, file_path)
20
- VALUES ('delete', old.id, old.type, old.message, old.raw_output, old.context, old.file_path);
21
- END;
22
-
23
- CREATE TRIGGER IF NOT EXISTS errors_au AFTER UPDATE ON errors BEGIN
24
- INSERT INTO errors_fts(errors_fts, rowid, type, message, raw_output, context, file_path)
25
- VALUES ('delete', old.id, old.type, old.message, old.raw_output, old.context, old.file_path);
26
- INSERT INTO errors_fts(rowid, type, message, raw_output, context, file_path)
27
- VALUES (new.id, new.type, new.message, new.raw_output, new.context, new.file_path);
28
- END;
29
-
30
- -- Full-text search for solutions
31
- CREATE VIRTUAL TABLE IF NOT EXISTS solutions_fts USING fts5(
32
- description, commands, code_change,
33
- content='solutions',
34
- content_rowid='id'
35
- );
36
-
37
- CREATE TRIGGER IF NOT EXISTS solutions_ai AFTER INSERT ON solutions BEGIN
38
- INSERT INTO solutions_fts(rowid, description, commands, code_change)
39
- VALUES (new.id, new.description, new.commands, new.code_change);
40
- END;
41
-
42
- CREATE TRIGGER IF NOT EXISTS solutions_ad AFTER DELETE ON solutions BEGIN
43
- INSERT INTO solutions_fts(solutions_fts, rowid, description, commands, code_change)
44
- VALUES ('delete', old.id, old.description, old.commands, old.code_change);
45
- END;
46
-
47
- CREATE TRIGGER IF NOT EXISTS solutions_au AFTER UPDATE ON solutions BEGIN
48
- INSERT INTO solutions_fts(solutions_fts, rowid, description, commands, code_change)
49
- VALUES ('delete', old.id, old.description, old.commands, old.code_change);
50
- INSERT INTO solutions_fts(rowid, description, commands, code_change)
51
- VALUES (new.id, new.description, new.commands, new.code_change);
52
- END;
53
-
54
- -- Full-text search for code modules
55
- CREATE VIRTUAL TABLE IF NOT EXISTS code_modules_fts USING fts5(
56
- name, file_path, description, language,
57
- content='code_modules',
58
- content_rowid='id'
59
- );
60
-
61
- CREATE TRIGGER IF NOT EXISTS code_modules_ai AFTER INSERT ON code_modules BEGIN
62
- INSERT INTO code_modules_fts(rowid, name, file_path, description, language)
63
- VALUES (new.id, new.name, new.file_path, new.description, new.language);
64
- END;
65
-
66
- CREATE TRIGGER IF NOT EXISTS code_modules_ad AFTER DELETE ON code_modules BEGIN
67
- INSERT INTO code_modules_fts(code_modules_fts, rowid, name, file_path, description, language)
68
- VALUES ('delete', old.id, old.name, old.file_path, old.description, old.language);
69
- END;
70
-
71
- CREATE TRIGGER IF NOT EXISTS code_modules_au AFTER UPDATE ON code_modules BEGIN
72
- INSERT INTO code_modules_fts(code_modules_fts, rowid, name, file_path, description, language)
73
- VALUES ('delete', old.id, old.name, old.file_path, old.description, old.language);
74
- INSERT INTO code_modules_fts(rowid, name, file_path, description, language)
75
- VALUES (new.id, new.name, new.file_path, new.description, new.language);
76
- END;
77
- `);
78
- }
1
+ import type Database from 'better-sqlite3';
2
+
3
+ export function up(db: Database.Database): void {
4
+ db.exec(`
5
+ -- Full-text search for errors
6
+ CREATE VIRTUAL TABLE IF NOT EXISTS errors_fts USING fts5(
7
+ type, message, raw_output, context, file_path,
8
+ content='errors',
9
+ content_rowid='id'
10
+ );
11
+
12
+ -- Sync triggers for errors_fts
13
+ CREATE TRIGGER IF NOT EXISTS errors_ai AFTER INSERT ON errors BEGIN
14
+ INSERT INTO errors_fts(rowid, type, message, raw_output, context, file_path)
15
+ VALUES (new.id, new.type, new.message, new.raw_output, new.context, new.file_path);
16
+ END;
17
+
18
+ CREATE TRIGGER IF NOT EXISTS errors_ad AFTER DELETE ON errors BEGIN
19
+ INSERT INTO errors_fts(errors_fts, rowid, type, message, raw_output, context, file_path)
20
+ VALUES ('delete', old.id, old.type, old.message, old.raw_output, old.context, old.file_path);
21
+ END;
22
+
23
+ CREATE TRIGGER IF NOT EXISTS errors_au AFTER UPDATE ON errors BEGIN
24
+ INSERT INTO errors_fts(errors_fts, rowid, type, message, raw_output, context, file_path)
25
+ VALUES ('delete', old.id, old.type, old.message, old.raw_output, old.context, old.file_path);
26
+ INSERT INTO errors_fts(rowid, type, message, raw_output, context, file_path)
27
+ VALUES (new.id, new.type, new.message, new.raw_output, new.context, new.file_path);
28
+ END;
29
+
30
+ -- Full-text search for solutions
31
+ CREATE VIRTUAL TABLE IF NOT EXISTS solutions_fts USING fts5(
32
+ description, commands, code_change,
33
+ content='solutions',
34
+ content_rowid='id'
35
+ );
36
+
37
+ CREATE TRIGGER IF NOT EXISTS solutions_ai AFTER INSERT ON solutions BEGIN
38
+ INSERT INTO solutions_fts(rowid, description, commands, code_change)
39
+ VALUES (new.id, new.description, new.commands, new.code_change);
40
+ END;
41
+
42
+ CREATE TRIGGER IF NOT EXISTS solutions_ad AFTER DELETE ON solutions BEGIN
43
+ INSERT INTO solutions_fts(solutions_fts, rowid, description, commands, code_change)
44
+ VALUES ('delete', old.id, old.description, old.commands, old.code_change);
45
+ END;
46
+
47
+ CREATE TRIGGER IF NOT EXISTS solutions_au AFTER UPDATE ON solutions BEGIN
48
+ INSERT INTO solutions_fts(solutions_fts, rowid, description, commands, code_change)
49
+ VALUES ('delete', old.id, old.description, old.commands, old.code_change);
50
+ INSERT INTO solutions_fts(rowid, description, commands, code_change)
51
+ VALUES (new.id, new.description, new.commands, new.code_change);
52
+ END;
53
+
54
+ -- Full-text search for code modules
55
+ CREATE VIRTUAL TABLE IF NOT EXISTS code_modules_fts USING fts5(
56
+ name, file_path, description, language,
57
+ content='code_modules',
58
+ content_rowid='id'
59
+ );
60
+
61
+ CREATE TRIGGER IF NOT EXISTS code_modules_ai AFTER INSERT ON code_modules BEGIN
62
+ INSERT INTO code_modules_fts(rowid, name, file_path, description, language)
63
+ VALUES (new.id, new.name, new.file_path, new.description, new.language);
64
+ END;
65
+
66
+ CREATE TRIGGER IF NOT EXISTS code_modules_ad AFTER DELETE ON code_modules BEGIN
67
+ INSERT INTO code_modules_fts(code_modules_fts, rowid, name, file_path, description, language)
68
+ VALUES ('delete', old.id, old.name, old.file_path, old.description, old.language);
69
+ END;
70
+
71
+ CREATE TRIGGER IF NOT EXISTS code_modules_au AFTER UPDATE ON code_modules BEGIN
72
+ INSERT INTO code_modules_fts(code_modules_fts, rowid, name, file_path, description, language)
73
+ VALUES ('delete', old.id, old.name, old.file_path, old.description, old.language);
74
+ INSERT INTO code_modules_fts(rowid, name, file_path, description, language)
75
+ VALUES (new.id, new.name, new.file_path, new.description, new.language);
76
+ END;
77
+ `);
78
+ }
@@ -1,17 +1,17 @@
1
- import type Database from 'better-sqlite3';
2
-
3
- export function up(db: Database.Database): void {
4
- // Add activation_count and last_activated_at columns
5
- const columns = db.pragma('table_info(synapses)') as Array<{ name: string }>;
6
- const colNames = columns.map(c => c.name);
7
-
8
- if (!colNames.includes('activation_count')) {
9
- db.exec(`ALTER TABLE synapses ADD COLUMN activation_count INTEGER NOT NULL DEFAULT 1`);
10
- }
11
- if (!colNames.includes('last_activated_at')) {
12
- db.exec(`ALTER TABLE synapses ADD COLUMN last_activated_at TEXT NOT NULL DEFAULT (datetime('now'))`);
13
- }
14
-
15
- // Add index for decay queries
16
- db.exec(`CREATE INDEX IF NOT EXISTS idx_synapses_last_activated ON synapses(last_activated_at)`);
17
- }
1
+ import type Database from 'better-sqlite3';
2
+
3
+ export function up(db: Database.Database): void {
4
+ // Add activation_count and last_activated_at columns
5
+ const columns = db.pragma('table_info(synapses)') as Array<{ name: string }>;
6
+ const colNames = columns.map(c => c.name);
7
+
8
+ if (!colNames.includes('activation_count')) {
9
+ db.exec(`ALTER TABLE synapses ADD COLUMN activation_count INTEGER NOT NULL DEFAULT 1`);
10
+ }
11
+ if (!colNames.includes('last_activated_at')) {
12
+ db.exec(`ALTER TABLE synapses ADD COLUMN last_activated_at TEXT NOT NULL DEFAULT (datetime('now'))`);
13
+ }
14
+
15
+ // Add index for decay queries
16
+ db.exec(`CREATE INDEX IF NOT EXISTS idx_synapses_last_activated ON synapses(last_activated_at)`);
17
+ }
@@ -1,64 +1,64 @@
1
- import type Database from 'better-sqlite3';
2
- import { getLogger } from '../../utils/logger.js';
3
- import { up as coreSchema } from './001_core_schema.js';
4
- import { up as learningSchema } from './002_learning_schema.js';
5
- import { up as codeSchema } from './003_code_schema.js';
6
- import { up as synapsesSchema } from './004_synapses_schema.js';
7
- import { up as ftsIndexes } from './005_fts_indexes.js';
8
- import { up as synapsesPhase3 } from './006_synapses_phase3.js';
9
-
10
- interface Migration {
11
- version: number;
12
- name: string;
13
- up: (db: Database.Database) => void;
14
- }
15
-
16
- const migrations: Migration[] = [
17
- { version: 1, name: '001_core_schema', up: coreSchema },
18
- { version: 2, name: '002_learning_schema', up: learningSchema },
19
- { version: 3, name: '003_code_schema', up: codeSchema },
20
- { version: 4, name: '004_synapses_schema', up: synapsesSchema },
21
- { version: 5, name: '005_fts_indexes', up: ftsIndexes },
22
- { version: 6, name: '006_synapses_phase3', up: synapsesPhase3 },
23
- ];
24
-
25
- function ensureMigrationsTable(db: Database.Database): void {
26
- db.exec(`
27
- CREATE TABLE IF NOT EXISTS migrations (
28
- version INTEGER PRIMARY KEY,
29
- name TEXT NOT NULL,
30
- applied_at TEXT NOT NULL DEFAULT (datetime('now'))
31
- );
32
- `);
33
- }
34
-
35
- function getCurrentVersion(db: Database.Database): number {
36
- const row = db.prepare('SELECT MAX(version) as version FROM migrations').get() as { version: number | null } | undefined;
37
- return row?.version ?? 0;
38
- }
39
-
40
- export function runMigrations(db: Database.Database): void {
41
- const logger = getLogger();
42
- ensureMigrationsTable(db);
43
-
44
- const currentVersion = getCurrentVersion(db);
45
- const pending = migrations.filter(m => m.version > currentVersion);
46
-
47
- if (pending.length === 0) {
48
- logger.info('Database is up to date');
49
- return;
50
- }
51
-
52
- logger.info(`Running ${pending.length} migration(s) from version ${currentVersion}`);
53
-
54
- const runAll = db.transaction(() => {
55
- for (const migration of pending) {
56
- logger.info(`Applying migration ${migration.name}`);
57
- migration.up(db);
58
- db.prepare('INSERT INTO migrations (version, name) VALUES (?, ?)').run(migration.version, migration.name);
59
- }
60
- });
61
-
62
- runAll();
63
- logger.info(`Migrations complete. Now at version ${pending[pending.length - 1]!.version}`);
64
- }
1
+ import type Database from 'better-sqlite3';
2
+ import { getLogger } from '../../utils/logger.js';
3
+ import { up as coreSchema } from './001_core_schema.js';
4
+ import { up as learningSchema } from './002_learning_schema.js';
5
+ import { up as codeSchema } from './003_code_schema.js';
6
+ import { up as synapsesSchema } from './004_synapses_schema.js';
7
+ import { up as ftsIndexes } from './005_fts_indexes.js';
8
+ import { up as synapsesPhase3 } from './006_synapses_phase3.js';
9
+
10
+ interface Migration {
11
+ version: number;
12
+ name: string;
13
+ up: (db: Database.Database) => void;
14
+ }
15
+
16
+ const migrations: Migration[] = [
17
+ { version: 1, name: '001_core_schema', up: coreSchema },
18
+ { version: 2, name: '002_learning_schema', up: learningSchema },
19
+ { version: 3, name: '003_code_schema', up: codeSchema },
20
+ { version: 4, name: '004_synapses_schema', up: synapsesSchema },
21
+ { version: 5, name: '005_fts_indexes', up: ftsIndexes },
22
+ { version: 6, name: '006_synapses_phase3', up: synapsesPhase3 },
23
+ ];
24
+
25
+ function ensureMigrationsTable(db: Database.Database): void {
26
+ db.exec(`
27
+ CREATE TABLE IF NOT EXISTS migrations (
28
+ version INTEGER PRIMARY KEY,
29
+ name TEXT NOT NULL,
30
+ applied_at TEXT NOT NULL DEFAULT (datetime('now'))
31
+ );
32
+ `);
33
+ }
34
+
35
+ function getCurrentVersion(db: Database.Database): number {
36
+ const row = db.prepare('SELECT MAX(version) as version FROM migrations').get() as { version: number | null } | undefined;
37
+ return row?.version ?? 0;
38
+ }
39
+
40
+ export function runMigrations(db: Database.Database): void {
41
+ const logger = getLogger();
42
+ ensureMigrationsTable(db);
43
+
44
+ const currentVersion = getCurrentVersion(db);
45
+ const pending = migrations.filter(m => m.version > currentVersion);
46
+
47
+ if (pending.length === 0) {
48
+ logger.info('Database is up to date');
49
+ return;
50
+ }
51
+
52
+ logger.info(`Running ${pending.length} migration(s) from version ${currentVersion}`);
53
+
54
+ const runAll = db.transaction(() => {
55
+ for (const migration of pending) {
56
+ logger.info(`Applying migration ${migration.name}`);
57
+ migration.up(db);
58
+ db.prepare('INSERT INTO migrations (version, name) VALUES (?, ?)').run(migration.version, migration.name);
59
+ }
60
+ });
61
+
62
+ runAll();
63
+ logger.info(`Migrations complete. Now at version ${pending[pending.length - 1]!.version}`);
64
+ }
@@ -1,66 +1,66 @@
1
- import type Database from 'better-sqlite3';
2
- import type { Statement } from 'better-sqlite3';
3
-
4
- export interface AntipatternRecord {
5
- id: number;
6
- pattern: string;
7
- description: string;
8
- severity: string;
9
- suggestion: string | null;
10
- occurrences: number;
11
- project_id: number | null;
12
- global: number;
13
- created_at: string;
14
- }
15
-
16
- type AntipatternCreate = Omit<AntipatternRecord, 'id' | 'created_at'>;
17
- type AntipatternUpdate = Partial<Omit<AntipatternRecord, 'id' | 'created_at'>>;
18
-
19
- export class AntipatternRepository {
20
- private stmts: Record<string, Statement>;
21
-
22
- constructor(private db: Database.Database) {
23
- this.stmts = {
24
- create: db.prepare(`
25
- INSERT INTO antipatterns (pattern, description, severity, suggestion, occurrences, project_id, global)
26
- VALUES (@pattern, @description, @severity, @suggestion, @occurrences, @project_id, @global)
27
- `),
28
- getById: db.prepare('SELECT * FROM antipatterns WHERE id = ?'),
29
- delete: db.prepare('DELETE FROM antipatterns WHERE id = ?'),
30
- findByProject: db.prepare('SELECT * FROM antipatterns WHERE project_id = ? ORDER BY occurrences DESC'),
31
- findGlobal: db.prepare('SELECT * FROM antipatterns WHERE global = 1 ORDER BY occurrences DESC'),
32
- };
33
- }
34
-
35
- create(data: AntipatternCreate): number {
36
- const result = this.stmts.create.run(data);
37
- return result.lastInsertRowid as number;
38
- }
39
-
40
- getById(id: number): AntipatternRecord | undefined {
41
- return this.stmts.getById.get(id) as AntipatternRecord | undefined;
42
- }
43
-
44
- update(id: number, data: AntipatternUpdate): boolean {
45
- const fields = Object.keys(data).filter((key) => (data as Record<string, unknown>)[key] !== undefined);
46
- if (fields.length === 0) return false;
47
-
48
- const setClauses = fields.map((field) => `${field} = @${field}`).join(', ');
49
- const stmt = this.db.prepare(`UPDATE antipatterns SET ${setClauses} WHERE id = @id`);
50
- const result = stmt.run({ ...data, id });
51
- return result.changes > 0;
52
- }
53
-
54
- delete(id: number): boolean {
55
- const result = this.stmts.delete.run(id);
56
- return result.changes > 0;
57
- }
58
-
59
- findByProject(projectId: number): AntipatternRecord[] {
60
- return this.stmts.findByProject.all(projectId) as AntipatternRecord[];
61
- }
62
-
63
- findGlobal(): AntipatternRecord[] {
64
- return this.stmts.findGlobal.all() as AntipatternRecord[];
65
- }
66
- }
1
+ import type Database from 'better-sqlite3';
2
+ import type { Statement } from 'better-sqlite3';
3
+
4
+ export interface AntipatternRecord {
5
+ id: number;
6
+ pattern: string;
7
+ description: string;
8
+ severity: string;
9
+ suggestion: string | null;
10
+ occurrences: number;
11
+ project_id: number | null;
12
+ global: number;
13
+ created_at: string;
14
+ }
15
+
16
+ type AntipatternCreate = Omit<AntipatternRecord, 'id' | 'created_at'>;
17
+ type AntipatternUpdate = Partial<Omit<AntipatternRecord, 'id' | 'created_at'>>;
18
+
19
+ export class AntipatternRepository {
20
+ private stmts: Record<string, Statement>;
21
+
22
+ constructor(private db: Database.Database) {
23
+ this.stmts = {
24
+ create: db.prepare(`
25
+ INSERT INTO antipatterns (pattern, description, severity, suggestion, occurrences, project_id, global)
26
+ VALUES (@pattern, @description, @severity, @suggestion, @occurrences, @project_id, @global)
27
+ `),
28
+ getById: db.prepare('SELECT * FROM antipatterns WHERE id = ?'),
29
+ delete: db.prepare('DELETE FROM antipatterns WHERE id = ?'),
30
+ findByProject: db.prepare('SELECT * FROM antipatterns WHERE project_id = ? ORDER BY occurrences DESC'),
31
+ findGlobal: db.prepare('SELECT * FROM antipatterns WHERE global = 1 ORDER BY occurrences DESC'),
32
+ };
33
+ }
34
+
35
+ create(data: AntipatternCreate): number {
36
+ const result = this.stmts.create.run(data);
37
+ return result.lastInsertRowid as number;
38
+ }
39
+
40
+ getById(id: number): AntipatternRecord | undefined {
41
+ return this.stmts.getById.get(id) as AntipatternRecord | undefined;
42
+ }
43
+
44
+ update(id: number, data: AntipatternUpdate): boolean {
45
+ const fields = Object.keys(data).filter((key) => (data as Record<string, unknown>)[key] !== undefined);
46
+ if (fields.length === 0) return false;
47
+
48
+ const setClauses = fields.map((field) => `${field} = @${field}`).join(', ');
49
+ const stmt = this.db.prepare(`UPDATE antipatterns SET ${setClauses} WHERE id = @id`);
50
+ const result = stmt.run({ ...data, id });
51
+ return result.changes > 0;
52
+ }
53
+
54
+ delete(id: number): boolean {
55
+ const result = this.stmts.delete.run(id);
56
+ return result.changes > 0;
57
+ }
58
+
59
+ findByProject(projectId: number): AntipatternRecord[] {
60
+ return this.stmts.findByProject.all(projectId) as AntipatternRecord[];
61
+ }
62
+
63
+ findGlobal(): AntipatternRecord[] {
64
+ return this.stmts.findGlobal.all() as AntipatternRecord[];
65
+ }
66
+ }
@@ -18,6 +18,7 @@ export class CodeModuleRepository {
18
18
  delete: db.prepare('DELETE FROM code_modules WHERE id = ?'),
19
19
  findByFingerprint: db.prepare('SELECT * FROM code_modules WHERE fingerprint = ?'),
20
20
  findByProject: db.prepare('SELECT * FROM code_modules WHERE project_id = ? ORDER BY name ASC'),
21
+ findAll: db.prepare('SELECT * FROM code_modules ORDER BY name ASC'),
21
22
  countAll: db.prepare('SELECT COUNT(*) as count FROM code_modules'),
22
23
  search: db.prepare(`
23
24
  SELECT cm.* FROM code_modules cm
@@ -74,6 +75,14 @@ export class CodeModuleRepository {
74
75
  return this.stmts.search.all(query) as CodeModuleRecord[];
75
76
  }
76
77
 
78
+ findAll(limit?: number): CodeModuleRecord[] {
79
+ if (limit) {
80
+ const stmt = this.db.prepare('SELECT * FROM code_modules ORDER BY name ASC LIMIT ?');
81
+ return stmt.all(limit) as CodeModuleRecord[];
82
+ }
83
+ return this.stmts.findAll.all() as CodeModuleRecord[];
84
+ }
85
+
77
86
  countAll(): number {
78
87
  return (this.stmts.countAll.get() as { count: number }).count;
79
88
  }