@massu/core 1.10.8 → 1.12.1

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 (178) hide show
  1. package/CHANGELOG.md +1334 -0
  2. package/dist/adapter.js +6 -4
  3. package/dist/cli.js +581 -448
  4. package/dist/detect/adapters/.bundle-shasums.json +4 -7
  5. package/dist/detect/adapters/rails.js +6 -4
  6. package/dist/detect/adapters/spring.js +6 -4
  7. package/dist/detect/adapters/tree-sitter-loader.js +6 -4
  8. package/dist/hooks/auto-learning-pipeline.js +24 -1
  9. package/dist/hooks/classify-failure.js +32 -3
  10. package/dist/hooks/cost-tracker.js +24 -0
  11. package/dist/hooks/fix-detector.js +97 -1
  12. package/dist/hooks/incident-pipeline.js +30 -1
  13. package/dist/hooks/intent-suggester.js +6 -3
  14. package/dist/hooks/post-edit-context.js +24 -1
  15. package/dist/hooks/post-tool-use.js +67 -19
  16. package/dist/hooks/pre-compact.js +28 -3
  17. package/dist/hooks/pre-delete-check.js +53 -27
  18. package/dist/hooks/pre-tool-use-gate.js +962 -0
  19. package/dist/hooks/quality-event.js +24 -0
  20. package/dist/hooks/rule-enforcement-pipeline.js +24 -1
  21. package/dist/hooks/security-gate.js +29 -16
  22. package/dist/hooks/session-end.js +31 -6
  23. package/dist/hooks/session-start.js +8709 -8459
  24. package/dist/hooks/user-prompt.js +25 -1
  25. package/package.json +15 -18
  26. package/commands/README.md +0 -134
  27. package/src/adapter.ts +0 -121
  28. package/src/adr-generator.ts +0 -292
  29. package/src/analytics.ts +0 -373
  30. package/src/audit-trail.ts +0 -450
  31. package/src/backfill-sessions.ts +0 -182
  32. package/src/changelog-generator.ts +0 -178
  33. package/src/cli.ts +0 -246
  34. package/src/cloud-sync.ts +0 -234
  35. package/src/commands/adapters.ts +0 -824
  36. package/src/commands/changelog.ts +0 -165
  37. package/src/commands/config-check-drift.ts +0 -133
  38. package/src/commands/config-refresh.ts +0 -396
  39. package/src/commands/config-upgrade.ts +0 -127
  40. package/src/commands/doctor.ts +0 -485
  41. package/src/commands/hook-runner.ts +0 -145
  42. package/src/commands/init.ts +0 -1823
  43. package/src/commands/install-commands.ts +0 -692
  44. package/src/commands/install-hooks.ts +0 -27
  45. package/src/commands/permissions.ts +0 -150
  46. package/src/commands/refresh-log.ts +0 -37
  47. package/src/commands/show-template.ts +0 -65
  48. package/src/commands/template-engine.ts +0 -301
  49. package/src/commands/watch.ts +0 -430
  50. package/src/config.ts +0 -765
  51. package/src/cost-tracker.ts +0 -355
  52. package/src/db.ts +0 -370
  53. package/src/dependency-scorer.ts +0 -337
  54. package/src/detect/adapters/aspnet.ts +0 -4
  55. package/src/detect/adapters/discover.ts +0 -469
  56. package/src/detect/adapters/file-sampler.ts +0 -225
  57. package/src/detect/adapters/go-chi.ts +0 -4
  58. package/src/detect/adapters/index.ts +0 -51
  59. package/src/detect/adapters/nextjs-trpc.ts +0 -166
  60. package/src/detect/adapters/parse-guard.ts +0 -133
  61. package/src/detect/adapters/phoenix.ts +0 -4
  62. package/src/detect/adapters/python-django.ts +0 -208
  63. package/src/detect/adapters/python-fastapi.ts +0 -223
  64. package/src/detect/adapters/python-flask.ts +0 -235
  65. package/src/detect/adapters/query-helpers.ts +0 -170
  66. package/src/detect/adapters/rails.ts +0 -4
  67. package/src/detect/adapters/runner.ts +0 -284
  68. package/src/detect/adapters/spring.ts +0 -4
  69. package/src/detect/adapters/swift-swiftui.ts +0 -171
  70. package/src/detect/adapters/tree-sitter-loader.ts +0 -517
  71. package/src/detect/adapters/types.ts +0 -191
  72. package/src/detect/codebase-introspector.ts +0 -210
  73. package/src/detect/domain-inferrer.ts +0 -197
  74. package/src/detect/drift.ts +0 -199
  75. package/src/detect/framework-detector.ts +0 -307
  76. package/src/detect/index.ts +0 -209
  77. package/src/detect/manifest-registry.ts +0 -261
  78. package/src/detect/migrate.ts +0 -362
  79. package/src/detect/monorepo-detector.ts +0 -348
  80. package/src/detect/package-detector.ts +0 -828
  81. package/src/detect/passthrough.ts +0 -108
  82. package/src/detect/regex-fallback.ts +0 -449
  83. package/src/detect/source-dir-detector.ts +0 -297
  84. package/src/detect/vr-command-map.ts +0 -167
  85. package/src/docs-map.json +0 -100
  86. package/src/docs-tools.ts +0 -519
  87. package/src/domains.ts +0 -182
  88. package/src/hooks/auto-learning-pipeline.ts +0 -228
  89. package/src/hooks/classify-failure.ts +0 -259
  90. package/src/hooks/cost-tracker.ts +0 -66
  91. package/src/hooks/fix-detector.ts +0 -186
  92. package/src/hooks/incident-pipeline.ts +0 -190
  93. package/src/hooks/intent-suggester.ts +0 -131
  94. package/src/hooks/post-edit-context.ts +0 -91
  95. package/src/hooks/post-tool-use.ts +0 -413
  96. package/src/hooks/pre-compact.ts +0 -168
  97. package/src/hooks/pre-delete-check.ts +0 -243
  98. package/src/hooks/quality-event.ts +0 -127
  99. package/src/hooks/rule-enforcement-pipeline.ts +0 -159
  100. package/src/hooks/security-gate.ts +0 -153
  101. package/src/hooks/session-end.ts +0 -467
  102. package/src/hooks/session-start.ts +0 -436
  103. package/src/hooks/user-prompt.ts +0 -157
  104. package/src/import-resolver.ts +0 -226
  105. package/src/knowledge-db.ts +0 -176
  106. package/src/knowledge-indexer.ts +0 -708
  107. package/src/knowledge-tools.ts +0 -1414
  108. package/src/lib/fileLock.ts +0 -203
  109. package/src/lib/framework-supports.ts +0 -85
  110. package/src/lib/gitToplevel.ts +0 -22
  111. package/src/lib/hook-registry.ts +0 -43
  112. package/src/lib/installLock.ts +0 -66
  113. package/src/lib/memory-path.ts +0 -49
  114. package/src/lib/pidLiveness.ts +0 -67
  115. package/src/lib/settings-local.ts +0 -110
  116. package/src/lib/sql-table-names.ts +0 -71
  117. package/src/license.ts +0 -547
  118. package/src/lsp/auto-detect.ts +0 -98
  119. package/src/lsp/client.ts +0 -776
  120. package/src/lsp/enrich.ts +0 -127
  121. package/src/lsp/types.ts +0 -221
  122. package/src/memory-db.ts +0 -1614
  123. package/src/memory-file-ingest.ts +0 -128
  124. package/src/memory-tools.ts +0 -424
  125. package/src/middleware-tree.ts +0 -71
  126. package/src/observability-tools.ts +0 -343
  127. package/src/observation-extractor.ts +0 -418
  128. package/src/page-deps.ts +0 -285
  129. package/src/permissions.ts +0 -422
  130. package/src/prompt-analyzer.ts +0 -332
  131. package/src/python/coupling-detector.ts +0 -125
  132. package/src/python/domain-enforcer.ts +0 -84
  133. package/src/python/impact-analyzer.ts +0 -96
  134. package/src/python/import-parser.ts +0 -244
  135. package/src/python/import-resolver.ts +0 -136
  136. package/src/python/migration-indexer.ts +0 -116
  137. package/src/python/migration-parser.ts +0 -332
  138. package/src/python/model-indexer.ts +0 -71
  139. package/src/python/model-parser.ts +0 -279
  140. package/src/python/route-indexer.ts +0 -59
  141. package/src/python/route-parser.ts +0 -317
  142. package/src/python-tools.ts +0 -630
  143. package/src/regression-detector.ts +0 -319
  144. package/src/rules.ts +0 -57
  145. package/src/schema-mapper.ts +0 -232
  146. package/src/security/adapter-origin.ts +0 -130
  147. package/src/security/adapter-verifier.ts +0 -319
  148. package/src/security/atomic-write.ts +0 -164
  149. package/src/security/fetcher.ts +0 -200
  150. package/src/security/install-tracking.ts +0 -319
  151. package/src/security/license-pubkey.generated.ts +0 -30
  152. package/src/security/license-response-verifier.ts +0 -149
  153. package/src/security/local-fingerprint.ts +0 -225
  154. package/src/security/manifest-cache.ts +0 -333
  155. package/src/security/manifest-schema.ts +0 -129
  156. package/src/security/registry-pubkey.generated.ts +0 -35
  157. package/src/security/telemetry.ts +0 -320
  158. package/src/security-scorer.ts +0 -405
  159. package/src/security-utils.ts +0 -133
  160. package/src/sentinel-db.ts +0 -580
  161. package/src/sentinel-scanner.ts +0 -406
  162. package/src/sentinel-tools.ts +0 -513
  163. package/src/sentinel-types.ts +0 -140
  164. package/src/server-dispatch.ts +0 -225
  165. package/src/server.ts +0 -107
  166. package/src/session-archiver.ts +0 -111
  167. package/src/session-state-generator.ts +0 -174
  168. package/src/team-knowledge.ts +0 -407
  169. package/src/tool-db-needs.ts +0 -232
  170. package/src/tools.ts +0 -1206
  171. package/src/transcript-parser.ts +0 -458
  172. package/src/trpc-index.ts +0 -215
  173. package/src/validate-features-runner.ts +0 -107
  174. package/src/validation-engine.ts +0 -358
  175. package/src/watch/daemon.ts +0 -385
  176. package/src/watch/lockfile-detector.ts +0 -65
  177. package/src/watch/paths.ts +0 -279
  178. package/src/watch/state.ts +0 -178
@@ -1,226 +0,0 @@
1
- // Copyright (c) 2026 Massu. All rights reserved.
2
- // Licensed under BSL 1.1 - see LICENSE file for details.
3
-
4
- import { readFileSync, existsSync, statSync } from 'fs';
5
- import { resolve, dirname, join } from 'path';
6
- import type Database from 'better-sqlite3';
7
- import { getResolvedPaths, getProjectRoot } from './config.ts';
8
- import { ensureWithinRoot } from './security-utils.ts';
9
- import { t } from './lib/sql-table-names.ts';
10
-
11
- interface ImportEdge {
12
- source_file: string;
13
- target_file: string;
14
- import_type: 'named' | 'default' | 'namespace' | 'side_effect' | 'dynamic';
15
- imported_names: string; // JSON array
16
- line: number;
17
- }
18
-
19
- interface ParsedImport {
20
- type: 'named' | 'default' | 'namespace' | 'side_effect' | 'dynamic';
21
- names: string[];
22
- specifier: string;
23
- line: number;
24
- }
25
-
26
- /**
27
- * Parse import statements from TypeScript/JavaScript source code.
28
- */
29
- export function parseImports(source: string): ParsedImport[] {
30
- const imports: ParsedImport[] = [];
31
- const lines = source.split('\n');
32
-
33
- for (let i = 0; i < lines.length; i++) {
34
- const line = lines[i];
35
- const lineNum = i + 1;
36
-
37
- // Skip comments
38
- if (line.trimStart().startsWith('//') || line.trimStart().startsWith('*')) continue;
39
-
40
- // Named imports: import { Foo, Bar } from 'module'
41
- const namedMatch = line.match(/import\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/);
42
- if (namedMatch) {
43
- const names = namedMatch[1].split(',').map(n => n.trim().split(/\s+as\s+/)[0].trim()).filter(Boolean);
44
- imports.push({ type: 'named', names, specifier: namedMatch[2], line: lineNum });
45
- continue;
46
- }
47
-
48
- // Default import: import Foo from 'module'
49
- const defaultMatch = line.match(/import\s+([A-Z_$][\w$]*)\s+from\s+['"]([^'"]+)['"]/);
50
- if (defaultMatch) {
51
- imports.push({ type: 'default', names: [defaultMatch[1]], specifier: defaultMatch[2], line: lineNum });
52
- continue;
53
- }
54
-
55
- // Default + named: import Foo, { Bar } from 'module'
56
- const mixedMatch = line.match(/import\s+([A-Z_$][\w$]*)\s*,\s*\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/);
57
- if (mixedMatch) {
58
- const names = [mixedMatch[1], ...mixedMatch[2].split(',').map(n => n.trim().split(/\s+as\s+/)[0].trim()).filter(Boolean)];
59
- imports.push({ type: 'named', names, specifier: mixedMatch[3], line: lineNum });
60
- continue;
61
- }
62
-
63
- // Namespace import: import * as Foo from 'module'
64
- const namespaceMatch = line.match(/import\s+\*\s+as\s+(\w+)\s+from\s+['"]([^'"]+)['"]/);
65
- if (namespaceMatch) {
66
- imports.push({ type: 'namespace', names: [namespaceMatch[1]], specifier: namespaceMatch[2], line: lineNum });
67
- continue;
68
- }
69
-
70
- // Type imports: import type { Foo } from 'module'
71
- const typeMatch = line.match(/import\s+type\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/);
72
- if (typeMatch) {
73
- const names = typeMatch[1].split(',').map(n => n.trim().split(/\s+as\s+/)[0].trim()).filter(Boolean);
74
- imports.push({ type: 'named', names, specifier: typeMatch[2], line: lineNum });
75
- continue;
76
- }
77
-
78
- // Side effect import: import 'module'
79
- const sideEffectMatch = line.match(/^import\s+['"]([^'"]+)['"]/);
80
- if (sideEffectMatch) {
81
- imports.push({ type: 'side_effect', names: [], specifier: sideEffectMatch[1], line: lineNum });
82
- continue;
83
- }
84
-
85
- // Dynamic import: await import('module') or import('module')
86
- const dynamicMatch = line.match(/(?:await\s+)?import\s*\(\s*['"]([^'"]+)['"]\s*\)/);
87
- if (dynamicMatch) {
88
- imports.push({ type: 'dynamic', names: [], specifier: dynamicMatch[1], line: lineNum });
89
- continue;
90
- }
91
- }
92
-
93
- return imports;
94
- }
95
-
96
- /**
97
- * Resolve an import specifier to an absolute file path.
98
- * Returns null for external packages (bare imports).
99
- */
100
- export function resolveImportPath(specifier: string, fromFile: string): string | null {
101
- // Skip external/bare imports (no . or @ prefix)
102
- if (!specifier.startsWith('.') && !specifier.startsWith('@/')) {
103
- return null;
104
- }
105
-
106
- let basePath: string;
107
-
108
- // Handle @/ alias -> src/
109
- if (specifier.startsWith('@/')) {
110
- const paths = getResolvedPaths();
111
- basePath = resolve(paths.pathAlias['@'] ?? paths.srcDir, specifier.slice(2));
112
- } else {
113
- // Relative path
114
- basePath = resolve(dirname(fromFile), specifier);
115
- }
116
-
117
- // Try exact path first
118
- if (existsSync(basePath) && !isDirectory(basePath)) {
119
- return toRelative(basePath);
120
- }
121
-
122
- // Try with extensions
123
- const resolvedPaths = getResolvedPaths();
124
- for (const ext of resolvedPaths.extensions) {
125
- const withExt = basePath + ext;
126
- if (existsSync(withExt)) {
127
- return toRelative(withExt);
128
- }
129
- }
130
-
131
- // Try index files (if path is a directory or could be)
132
- for (const indexFile of resolvedPaths.indexFiles) {
133
- const indexPath = join(basePath, indexFile);
134
- if (existsSync(indexPath)) {
135
- return toRelative(indexPath);
136
- }
137
- }
138
-
139
- return null;
140
- }
141
-
142
- function isDirectory(path: string): boolean {
143
- try {
144
- return statSync(path).isDirectory();
145
- } catch {
146
- // Path doesn't exist or is inaccessible
147
- return false;
148
- }
149
- }
150
-
151
- function toRelative(absPath: string): string {
152
- const root = getProjectRoot();
153
- if (absPath.startsWith(root)) {
154
- return absPath.slice(root.length + 1);
155
- }
156
- return absPath;
157
- }
158
-
159
- /**
160
- * Build the full import graph for all files in CodeGraph.
161
- * Stores results in massu_imports table.
162
- */
163
- export function buildImportIndex(dataDb: Database.Database, codegraphDb: Database.Database): number {
164
- // Get all files from CodeGraph
165
- const files = codegraphDb.prepare("SELECT path FROM files WHERE path LIKE 'src/%'").all() as { path: string }[];
166
-
167
- // Clear existing import edges
168
- dataDb.exec(`DELETE FROM ${t('imports')}`);
169
-
170
- const insertStmt = dataDb.prepare(
171
- `INSERT INTO ${t('imports')} (source_file, target_file, import_type, imported_names, line) VALUES (?, ?, ?, ?, ?)`
172
- );
173
-
174
- let edgeCount = 0;
175
- const projectRoot = getProjectRoot();
176
-
177
- const insertMany = dataDb.transaction((edges: ImportEdge[]) => {
178
- for (const edge of edges) {
179
- insertStmt.run(edge.source_file, edge.target_file, edge.import_type, edge.imported_names, edge.line);
180
- }
181
- });
182
-
183
- const batchSize = 500;
184
- let batch: ImportEdge[] = [];
185
-
186
- for (const file of files) {
187
- const absPath = ensureWithinRoot(resolve(projectRoot, file.path), projectRoot);
188
- if (!existsSync(absPath)) continue;
189
-
190
- let source: string;
191
- try {
192
- source = readFileSync(absPath, 'utf-8');
193
- } catch {
194
- // Skip unreadable source files
195
- continue;
196
- }
197
-
198
- const imports = parseImports(source);
199
-
200
- for (const imp of imports) {
201
- const targetPath = resolveImportPath(imp.specifier, absPath);
202
- if (!targetPath) continue; // Skip external packages
203
-
204
- batch.push({
205
- source_file: file.path,
206
- target_file: targetPath,
207
- import_type: imp.type,
208
- imported_names: JSON.stringify(imp.names),
209
- line: imp.line,
210
- });
211
-
212
- edgeCount++;
213
-
214
- if (batch.length >= batchSize) {
215
- insertMany(batch);
216
- batch = [];
217
- }
218
- }
219
- }
220
-
221
- if (batch.length > 0) {
222
- insertMany(batch);
223
- }
224
-
225
- return edgeCount;
226
- }
@@ -1,176 +0,0 @@
1
- // Copyright (c) 2026 Massu. All rights reserved.
2
- // Licensed under BSL 1.1 - see LICENSE file for details.
3
-
4
- import Database from 'better-sqlite3';
5
- import { dirname } from 'path';
6
- import { existsSync, mkdirSync } from 'fs';
7
- import { getConfig, getResolvedPaths } from './config.ts';
8
-
9
- /**
10
- * Connection to Massu Knowledge's SQLite database.
11
- * Stores indexed .claude/ knowledge: rules, patterns, incidents, verifications, cross-references.
12
- * Separate from codegraph.db (CodeGraph data) and memory.db (session memory).
13
- */
14
- export function getKnowledgeDb(): Database.Database {
15
- const dbPath = getResolvedPaths().knowledgeDbPath;
16
- const dir = dirname(dbPath);
17
- if (!existsSync(dir)) {
18
- mkdirSync(dir, { recursive: true });
19
- }
20
- const db = new Database(dbPath);
21
- db.pragma('journal_mode = WAL');
22
- db.pragma('foreign_keys = ON');
23
- initKnowledgeSchema(db);
24
- return db;
25
- }
26
-
27
- export function initKnowledgeSchema(db: Database.Database): void {
28
- db.exec(`
29
- -- Core document chunks (parsed from .claude/**/*.md)
30
- CREATE TABLE IF NOT EXISTS knowledge_documents (
31
- id INTEGER PRIMARY KEY AUTOINCREMENT,
32
- file_path TEXT NOT NULL,
33
- category TEXT NOT NULL,
34
- title TEXT NOT NULL,
35
- description TEXT,
36
- content_hash TEXT NOT NULL,
37
- indexed_at TEXT NOT NULL,
38
- indexed_at_epoch INTEGER NOT NULL
39
- );
40
-
41
- CREATE UNIQUE INDEX IF NOT EXISTS idx_kd_filepath ON knowledge_documents(file_path);
42
- CREATE INDEX IF NOT EXISTS idx_kd_category ON knowledge_documents(category);
43
-
44
- -- Structured chunks within documents
45
- CREATE TABLE IF NOT EXISTS knowledge_chunks (
46
- id INTEGER PRIMARY KEY AUTOINCREMENT,
47
- document_id INTEGER NOT NULL REFERENCES knowledge_documents(id) ON DELETE CASCADE,
48
- chunk_type TEXT NOT NULL CHECK(chunk_type IN (
49
- 'section', 'table_row', 'code_block', 'rule', 'incident', 'pattern', 'command', 'mismatch'
50
- )),
51
- heading TEXT,
52
- content TEXT NOT NULL,
53
- line_start INTEGER,
54
- line_end INTEGER,
55
- metadata TEXT DEFAULT '{}'
56
- );
57
-
58
- CREATE INDEX IF NOT EXISTS idx_kc_doc ON knowledge_chunks(document_id);
59
- CREATE INDEX IF NOT EXISTS idx_kc_type ON knowledge_chunks(chunk_type);
60
- CREATE INDEX IF NOT EXISTS idx_kc_heading ON knowledge_chunks(heading);
61
-
62
- -- Canonical Rules index
63
- CREATE TABLE IF NOT EXISTS knowledge_rules (
64
- id INTEGER PRIMARY KEY AUTOINCREMENT,
65
- rule_id TEXT UNIQUE NOT NULL,
66
- rule_text TEXT NOT NULL,
67
- vr_type TEXT,
68
- reference_path TEXT,
69
- severity TEXT DEFAULT 'HIGH',
70
- prevention_summary TEXT
71
- );
72
-
73
- CREATE INDEX IF NOT EXISTS idx_kr_id ON knowledge_rules(rule_id);
74
-
75
- -- Verification Requirements index
76
- CREATE TABLE IF NOT EXISTS knowledge_verifications (
77
- id INTEGER PRIMARY KEY AUTOINCREMENT,
78
- vr_type TEXT UNIQUE NOT NULL,
79
- command TEXT NOT NULL,
80
- expected TEXT NOT NULL,
81
- use_when TEXT NOT NULL,
82
- catches TEXT,
83
- category TEXT
84
- );
85
-
86
- CREATE INDEX IF NOT EXISTS idx_kv_type ON knowledge_verifications(vr_type);
87
- CREATE INDEX IF NOT EXISTS idx_kv_category ON knowledge_verifications(category);
88
-
89
- -- Incident index
90
- CREATE TABLE IF NOT EXISTS knowledge_incidents (
91
- id INTEGER PRIMARY KEY AUTOINCREMENT,
92
- incident_num INTEGER UNIQUE NOT NULL,
93
- date TEXT NOT NULL,
94
- type TEXT NOT NULL,
95
- gap_found TEXT NOT NULL,
96
- prevention TEXT NOT NULL,
97
- cr_added TEXT,
98
- root_cause TEXT,
99
- user_quote TEXT
100
- );
101
-
102
- CREATE INDEX IF NOT EXISTS idx_ki_num ON knowledge_incidents(incident_num);
103
- CREATE INDEX IF NOT EXISTS idx_ki_type ON knowledge_incidents(type);
104
- CREATE INDEX IF NOT EXISTS idx_ki_cr ON knowledge_incidents(cr_added);
105
-
106
- -- Schema mismatch quick lookup
107
- -- P-H013 (plan-stage-c-high-batch): the source column no longer has a
108
- -- SQL DEFAULT. Previously the value was a JS template-literal
109
- -- interpolation baked into the customer SQLite at schema creation
110
- -- time, so later config changes were ignored. Default is now applied
111
- -- at INSERT time via getConfig().conventions.knowledgeSourceFiles[0]
112
- -- in knowledge-indexer.ts -- a runtime config lookup that reflects
113
- -- the customers current config.
114
- CREATE TABLE IF NOT EXISTS knowledge_schema_mismatches (
115
- id INTEGER PRIMARY KEY AUTOINCREMENT,
116
- table_name TEXT NOT NULL,
117
- wrong_column TEXT NOT NULL,
118
- correct_column TEXT NOT NULL,
119
- source TEXT NOT NULL
120
- );
121
-
122
- CREATE INDEX IF NOT EXISTS idx_ksm_table ON knowledge_schema_mismatches(table_name);
123
-
124
- -- Cross-reference graph
125
- CREATE TABLE IF NOT EXISTS knowledge_edges (
126
- id INTEGER PRIMARY KEY AUTOINCREMENT,
127
- source_type TEXT NOT NULL,
128
- source_id TEXT NOT NULL,
129
- target_type TEXT NOT NULL,
130
- target_id TEXT NOT NULL,
131
- edge_type TEXT NOT NULL
132
- );
133
-
134
- CREATE INDEX IF NOT EXISTS idx_ke_source ON knowledge_edges(source_type, source_id);
135
- CREATE INDEX IF NOT EXISTS idx_ke_target ON knowledge_edges(target_type, target_id);
136
- CREATE UNIQUE INDEX IF NOT EXISTS idx_ke_unique ON knowledge_edges(source_type, source_id, target_type, target_id, edge_type);
137
-
138
- -- Staleness tracking
139
- CREATE TABLE IF NOT EXISTS knowledge_meta (
140
- key TEXT PRIMARY KEY,
141
- value TEXT NOT NULL
142
- );
143
- `);
144
-
145
- // FTS5 in separate exec (can fail if schema mismatch on existing table)
146
- try {
147
- db.exec(`
148
- CREATE VIRTUAL TABLE IF NOT EXISTS knowledge_fts USING fts5(
149
- heading, content, chunk_type, file_path
150
- );
151
-
152
- CREATE TRIGGER IF NOT EXISTS kc_fts_insert AFTER INSERT ON knowledge_chunks BEGIN
153
- INSERT INTO knowledge_fts(rowid, heading, content, chunk_type, file_path)
154
- SELECT new.id, new.heading, new.content, new.chunk_type, kd.file_path
155
- FROM knowledge_documents kd WHERE kd.id = new.document_id;
156
- END;
157
-
158
- CREATE TRIGGER IF NOT EXISTS kc_fts_delete AFTER DELETE ON knowledge_chunks BEGIN
159
- INSERT INTO knowledge_fts(knowledge_fts, rowid, heading, content, chunk_type, file_path)
160
- SELECT 'delete', old.id, old.heading, old.content, old.chunk_type, kd.file_path
161
- FROM knowledge_documents kd WHERE kd.id = old.document_id;
162
- END;
163
-
164
- CREATE TRIGGER IF NOT EXISTS kc_fts_update AFTER UPDATE ON knowledge_chunks BEGIN
165
- INSERT INTO knowledge_fts(knowledge_fts, rowid, heading, content, chunk_type, file_path)
166
- SELECT 'delete', old.id, old.heading, old.content, old.chunk_type, kd.file_path
167
- FROM knowledge_documents kd WHERE kd.id = old.document_id;
168
- INSERT INTO knowledge_fts(rowid, heading, content, chunk_type, file_path)
169
- SELECT new.id, new.heading, new.content, new.chunk_type, kd.file_path
170
- FROM knowledge_documents kd WHERE kd.id = new.document_id;
171
- END;
172
- `);
173
- } catch {
174
- // FTS5 table may already exist with different schema — not fatal
175
- }
176
- }