@massu/core 1.10.6 → 1.10.7

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.
package/src/page-deps.ts CHANGED
@@ -6,6 +6,7 @@ import { resolve } from 'path';
6
6
  import type Database from 'better-sqlite3';
7
7
  import { getConfig, getProjectRoot } from './config.ts';
8
8
  import { ensureWithinRoot } from './security-utils.ts';
9
+ import { t } from './lib/sql-table-names.ts';
9
10
 
10
11
  export interface PageChain {
11
12
  page: string;
@@ -61,7 +62,7 @@ function traceImports(
61
62
  visited.add(startFile);
62
63
 
63
64
  const imports = dataDb.prepare(
64
- 'SELECT target_file, imported_names FROM massu_imports WHERE source_file = ?'
65
+ `SELECT target_file, imported_names FROM ${t('imports')} WHERE source_file = ?`
65
66
  ).all(startFile) as { target_file: string; imported_names: string }[];
66
67
 
67
68
  for (const imp of imports) {
@@ -117,7 +118,7 @@ function findTablesFromRouters(routerNames: string[], dataDb: Database.Database)
117
118
  // Look up router files from the tRPC index
118
119
  for (const routerName of routerNames) {
119
120
  const procs = dataDb.prepare(
120
- 'SELECT DISTINCT router_file FROM massu_trpc_procedures WHERE router_name = ?'
121
+ `SELECT DISTINCT router_file FROM ${t('trpc_procedures')} WHERE router_name = ?`
121
122
  ).all(routerName) as { router_file: string }[];
122
123
 
123
124
  for (const proc of procs) {
@@ -150,7 +151,7 @@ function findTablesFromRouters(routerNames: string[], dataDb: Database.Database)
150
151
  */
151
152
  export function buildPageDeps(dataDb: Database.Database, codegraphDb: Database.Database): number {
152
153
  // Clear existing data
153
- dataDb.exec('DELETE FROM massu_page_deps');
154
+ dataDb.exec(`DELETE FROM ${t('page_deps')}`);
154
155
 
155
156
  // Find all page.tsx files from CodeGraph
156
157
  const pages = codegraphDb.prepare(
@@ -158,7 +159,7 @@ export function buildPageDeps(dataDb: Database.Database, codegraphDb: Database.D
158
159
  ).all() as { path: string }[];
159
160
 
160
161
  const insertStmt = dataDb.prepare(
161
- 'INSERT INTO massu_page_deps (page_file, route, portal, components, hooks, routers, tables_touched) VALUES (?, ?, ?, ?, ?, ?, ?)'
162
+ `INSERT INTO ${t('page_deps')} (page_file, route, portal, components, hooks, routers, tables_touched) VALUES (?, ?, ?, ?, ?, ?, ?)`
162
163
  );
163
164
 
164
165
  let count = 0;
@@ -200,7 +201,7 @@ export function buildPageDeps(dataDb: Database.Database, codegraphDb: Database.D
200
201
  * Get the dependency chain for a specific page.
201
202
  */
202
203
  export function getPageChain(dataDb: Database.Database, pageFile: string): PageChain | null {
203
- const row = dataDb.prepare('SELECT * FROM massu_page_deps WHERE page_file = ?').get(pageFile) as {
204
+ const row = dataDb.prepare(`SELECT * FROM ${t('page_deps')} WHERE page_file = ?`).get(pageFile) as {
204
205
  page_file: string; route: string; portal: string;
205
206
  components: string; hooks: string; routers: string; tables_touched: string;
206
207
  } | undefined;
@@ -229,7 +230,7 @@ export function findAffectedPages(dataDb: Database.Database, file: string): Page
229
230
  // Find all pages that import this file (directly or transitively)
230
231
  // First, find who imports this file
231
232
  const importers = dataDb.prepare(
232
- 'SELECT source_file FROM massu_imports WHERE target_file = ?'
233
+ `SELECT source_file FROM ${t('imports')} WHERE target_file = ?`
233
234
  ).all(file) as { source_file: string }[];
234
235
 
235
236
  const affectedFiles = new Set<string>([file, ...importers.map(i => i.source_file)]);
@@ -244,7 +245,7 @@ export function findAffectedPages(dataDb: Database.Database, file: string): Page
244
245
  const next: string[] = [];
245
246
  for (const f of frontier) {
246
247
  const upstreamImporters = dataDb.prepare(
247
- 'SELECT source_file FROM massu_imports WHERE target_file = ?'
248
+ `SELECT source_file FROM ${t('imports')} WHERE target_file = ?`
248
249
  ).all(f) as { source_file: string }[];
249
250
 
250
251
  for (const imp of upstreamImporters) {
@@ -260,7 +261,7 @@ export function findAffectedPages(dataDb: Database.Database, file: string): Page
260
261
  }
261
262
 
262
263
  // Now find which pages are in the affected set
263
- const allPages = dataDb.prepare('SELECT * FROM massu_page_deps').all() as {
264
+ const allPages = dataDb.prepare(`SELECT * FROM ${t('page_deps')}`).all() as {
264
265
  page_file: string; route: string; portal: string;
265
266
  components: string; hooks: string; routers: string; tables_touched: string;
266
267
  }[];
@@ -5,6 +5,7 @@ import { readFileSync, readdirSync } from 'fs';
5
5
  import { join, relative } from 'path';
6
6
  import type Database from 'better-sqlite3';
7
7
  import { getProjectRoot, getConfig } from '../config.ts';
8
+ import { t } from '../lib/sql-table-names.ts';
8
9
 
9
10
  interface CouplingMatch {
10
11
  frontendFile: string;
@@ -23,20 +24,20 @@ export function buildPythonCouplingIndex(dataDb: Database.Database): number {
23
24
  const srcDir = join(projectRoot, config.paths.source);
24
25
 
25
26
  // Get all routes from DB
26
- const routes = dataDb.prepare('SELECT id, method, path FROM massu_py_routes').all() as {
27
+ const routes = dataDb.prepare(`SELECT id, method, path FROM ${t('py_routes')}`).all() as {
27
28
  id: number; method: string; path: string;
28
29
  }[];
29
30
 
30
31
  if (routes.length === 0) return 0;
31
32
 
32
33
  // Clear existing callers
33
- dataDb.exec('DELETE FROM massu_py_route_callers');
34
+ dataDb.exec(`DELETE FROM ${t('py_route_callers')}`);
34
35
 
35
36
  // Walk frontend files (TS/TSX/JS/JSX)
36
37
  const frontendFiles = walkFrontendFiles(srcDir);
37
38
 
38
39
  const insertStmt = dataDb.prepare(
39
- 'INSERT INTO massu_py_route_callers (route_id, frontend_file, line, call_pattern) VALUES (?, ?, ?, ?)'
40
+ `INSERT INTO ${t('py_route_callers')} (route_id, frontend_file, line, call_pattern) VALUES (?, ?, ?, ?)`
40
41
  );
41
42
 
42
43
  let count = 0;
@@ -3,6 +3,7 @@
3
3
 
4
4
  import type Database from 'better-sqlite3';
5
5
  import { getConfig } from '../config.ts';
6
+ import { t } from '../lib/sql-table-names.ts';
6
7
 
7
8
  export interface DomainViolation {
8
9
  sourceFile: string;
@@ -44,7 +45,7 @@ export function findPythonDomainViolations(dataDb: Database.Database): DomainVio
44
45
  if (domains.length === 0) return [];
45
46
 
46
47
  const imports = dataDb.prepare(
47
- 'SELECT source_file, target_file FROM massu_py_imports'
48
+ `SELECT source_file, target_file FROM ${t('py_imports')}`
48
49
  ).all() as { source_file: string; target_file: string }[];
49
50
 
50
51
  const violations: DomainViolation[] = [];
@@ -74,7 +75,7 @@ export function findPythonDomainViolations(dataDb: Database.Database): DomainVio
74
75
  */
75
76
  export function getPythonFilesInDomain(dataDb: Database.Database, domainName: string): string[] {
76
77
  const allFiles = dataDb.prepare(
77
- 'SELECT DISTINCT source_file as f FROM massu_py_imports UNION SELECT DISTINCT target_file as f FROM massu_py_imports'
78
+ `SELECT DISTINCT source_file as f FROM ${t('py_imports')} UNION SELECT DISTINCT target_file as f FROM ${t('py_imports')}`
78
79
  ).all() as { f: string }[];
79
80
 
80
81
  return allFiles
@@ -3,6 +3,7 @@
3
3
 
4
4
  import type Database from 'better-sqlite3';
5
5
  import { classifyPythonFileDomain } from './domain-enforcer.ts';
6
+ import { t } from '../lib/sql-table-names.ts';
6
7
 
7
8
  export interface PythonImpactReport {
8
9
  file: string;
@@ -26,28 +27,28 @@ export function analyzePythonImpact(dataDb: Database.Database, file: string): Py
26
27
 
27
28
  // 2. Routes defined in this file
28
29
  const routes = dataDb.prepare(
29
- 'SELECT method, path, function_name FROM massu_py_routes WHERE file = ?'
30
+ `SELECT method, path, function_name FROM ${t('py_routes')} WHERE file = ?`
30
31
  ).all(file) as { method: string; path: string; function_name: string }[];
31
32
 
32
33
  // 3. Models defined in this file
33
34
  const models = dataDb.prepare(
34
- 'SELECT class_name, table_name FROM massu_py_models WHERE file = ?'
35
+ `SELECT class_name, table_name FROM ${t('py_models')} WHERE file = ?`
35
36
  ).all(file) as { class_name: string; table_name: string | null }[];
36
37
 
37
38
  // 4. Frontend callers (via routes in this file)
38
- const routeIds = dataDb.prepare('SELECT id FROM massu_py_routes WHERE file = ?').all(file) as { id: number }[];
39
+ const routeIds = dataDb.prepare(`SELECT id FROM ${t('py_routes')} WHERE file = ?`).all(file) as { id: number }[];
39
40
  const frontendCallers: string[] = [];
40
41
  if (routeIds.length > 0) {
41
42
  const placeholders = routeIds.map(() => '?').join(',');
42
43
  const callers = dataDb.prepare(
43
- `SELECT DISTINCT frontend_file FROM massu_py_route_callers WHERE route_id IN (${placeholders})`
44
+ `SELECT DISTINCT frontend_file FROM ${t('py_route_callers')} WHERE route_id IN (${placeholders})`
44
45
  ).all(...routeIds.map(r => r.id)) as { frontend_file: string }[];
45
46
  frontendCallers.push(...callers.map(c => c.frontend_file));
46
47
  }
47
48
 
48
49
  // 5. Domain crossings
49
50
  const imports = dataDb.prepare(
50
- 'SELECT target_file FROM massu_py_imports WHERE source_file = ?'
51
+ `SELECT target_file FROM ${t('py_imports')} WHERE source_file = ?`
51
52
  ).all(file) as { target_file: string }[];
52
53
 
53
54
  const domainCrossings = imports
@@ -71,7 +72,7 @@ function collectTransitiveImporters(dataDb: Database.Database, file: string, max
71
72
  let depth = 0;
72
73
 
73
74
  const importerStmt = dataDb.prepare(
74
- 'SELECT source_file FROM massu_py_imports WHERE target_file = ?'
75
+ `SELECT source_file FROM ${t('py_imports')} WHERE target_file = ?`
75
76
  );
76
77
 
77
78
  while (queue.length > 0 && depth < maxDepth) {
@@ -6,6 +6,7 @@ import { resolve, join, relative, dirname } from 'path';
6
6
  import type Database from 'better-sqlite3';
7
7
  import { parsePythonImports } from './import-parser.ts';
8
8
  import { getProjectRoot } from '../config.ts';
9
+ import { t } from '../lib/sql-table-names.ts';
9
10
 
10
11
  /**
11
12
  * Resolve a Python module path to a file path.
@@ -82,10 +83,10 @@ export function buildPythonImportIndex(dataDb: Database.Database, pythonRoot: st
82
83
  const absRoot = resolve(projectRoot, pythonRoot);
83
84
 
84
85
  // Clear existing Python import edges
85
- dataDb.exec('DELETE FROM massu_py_imports');
86
+ dataDb.exec(`DELETE FROM ${t('py_imports')}`);
86
87
 
87
88
  const insertStmt = dataDb.prepare(
88
- 'INSERT INTO massu_py_imports (source_file, target_file, import_type, imported_names, line) VALUES (?, ?, ?, ?, ?)'
89
+ `INSERT INTO ${t('py_imports')} (source_file, target_file, import_type, imported_names, line) VALUES (?, ?, ?, ?, ?)`
89
90
  );
90
91
 
91
92
  const files = walkPythonFiles(absRoot, excludeDirs);
@@ -6,11 +6,12 @@ import { join, relative } from 'path';
6
6
  import type Database from 'better-sqlite3';
7
7
  import { parseAlembicMigration } from './migration-parser.ts';
8
8
  import { getProjectRoot } from '../config.ts';
9
+ import { t } from '../lib/sql-table-names.ts';
9
10
 
10
11
  export function buildPythonMigrationIndex(dataDb: Database.Database, alembicDir: string): number {
11
12
  const projectRoot = getProjectRoot();
12
13
  const absDir = join(projectRoot, alembicDir);
13
- dataDb.exec('DELETE FROM massu_py_migrations');
14
+ dataDb.exec(`DELETE FROM ${t('py_migrations')}`);
14
15
 
15
16
  // Look for version files in versions/ subdirectory
16
17
  const versionsDir = join(absDir, 'versions');
@@ -28,7 +29,7 @@ export function buildPythonMigrationIndex(dataDb: Database.Database, alembicDir:
28
29
  }
29
30
 
30
31
  const insertStmt = dataDb.prepare(
31
- 'INSERT INTO massu_py_migrations (revision, down_revision, file, description, operations, is_head) VALUES (?, ?, ?, ?, ?, ?)'
32
+ `INSERT INTO ${t('py_migrations')} (revision, down_revision, file, description, operations, is_head) VALUES (?, ?, ?, ?, ?, ?)`
32
33
  );
33
34
 
34
35
  let count = 0;
@@ -80,11 +81,11 @@ export interface DriftReport {
80
81
  }
81
82
 
82
83
  export function detectMigrationDrift(dataDb: Database.Database): DriftReport {
83
- const models = dataDb.prepare('SELECT class_name, table_name, columns FROM massu_py_models WHERE table_name IS NOT NULL').all() as {
84
+ const models = dataDb.prepare(`SELECT class_name, table_name, columns FROM ${t('py_models')} WHERE table_name IS NOT NULL`).all() as {
84
85
  class_name: string; table_name: string; columns: string;
85
86
  }[];
86
87
 
87
- const migrations = dataDb.prepare('SELECT operations FROM massu_py_migrations').all() as { operations: string }[];
88
+ const migrations = dataDb.prepare(`SELECT operations FROM ${t('py_migrations')}`).all() as { operations: string }[];
88
89
 
89
90
  // Collect all tables and columns mentioned in migrations
90
91
  const migratedTables = new Set<string>();
@@ -6,6 +6,7 @@ import { join, relative } from 'path';
6
6
  import type Database from 'better-sqlite3';
7
7
  import { parsePythonModels } from './model-parser.ts';
8
8
  import { getProjectRoot } from '../config.ts';
9
+ import { t } from '../lib/sql-table-names.ts';
9
10
 
10
11
  function walkPyFiles(dir: string, excludeDirs: string[]): string[] {
11
12
  const files: string[] = [];
@@ -26,14 +27,14 @@ function walkPyFiles(dir: string, excludeDirs: string[]): string[] {
26
27
  export function buildPythonModelIndex(dataDb: Database.Database, pythonRoot: string, excludeDirs: string[] = ['__pycache__', '.venv', 'venv', '.mypy_cache', '.pytest_cache']): number {
27
28
  const projectRoot = getProjectRoot();
28
29
  const absRoot = join(projectRoot, pythonRoot);
29
- dataDb.exec('DELETE FROM massu_py_models');
30
- dataDb.exec('DELETE FROM massu_py_fk_edges');
30
+ dataDb.exec(`DELETE FROM ${t('py_models')}`);
31
+ dataDb.exec(`DELETE FROM ${t('py_fk_edges')}`);
31
32
 
32
33
  const insertModel = dataDb.prepare(
33
- 'INSERT INTO massu_py_models (class_name, table_name, file, line, columns, relationships, foreign_keys) VALUES (?, ?, ?, ?, ?, ?, ?)'
34
+ `INSERT INTO ${t('py_models')} (class_name, table_name, file, line, columns, relationships, foreign_keys) VALUES (?, ?, ?, ?, ?, ?, ?)`
34
35
  );
35
36
  const insertFk = dataDb.prepare(
36
- 'INSERT INTO massu_py_fk_edges (source_table, source_column, target_table, target_column) VALUES (?, ?, ?, ?)'
37
+ `INSERT INTO ${t('py_fk_edges')} (source_table, source_column, target_table, target_column) VALUES (?, ?, ?, ?)`
37
38
  );
38
39
 
39
40
  const files = walkPyFiles(absRoot, excludeDirs);
@@ -6,6 +6,7 @@ import { join, relative } from 'path';
6
6
  import type Database from 'better-sqlite3';
7
7
  import { parsePythonRoutes } from './route-parser.ts';
8
8
  import { getProjectRoot } from '../config.ts';
9
+ import { t } from '../lib/sql-table-names.ts';
9
10
 
10
11
  function walkPyFiles(dir: string, excludeDirs: string[]): string[] {
11
12
  const files: string[] = [];
@@ -26,11 +27,11 @@ function walkPyFiles(dir: string, excludeDirs: string[]): string[] {
26
27
  export function buildPythonRouteIndex(dataDb: Database.Database, pythonRoot: string, excludeDirs: string[] = ['__pycache__', '.venv', 'venv', '.mypy_cache', '.pytest_cache']): number {
27
28
  const projectRoot = getProjectRoot();
28
29
  const absRoot = join(projectRoot, pythonRoot);
29
- dataDb.exec('DELETE FROM massu_py_routes');
30
- dataDb.exec('DELETE FROM massu_py_route_callers');
30
+ dataDb.exec(`DELETE FROM ${t('py_routes')}`);
31
+ dataDb.exec(`DELETE FROM ${t('py_route_callers')}`);
31
32
 
32
33
  const insertStmt = dataDb.prepare(
33
- 'INSERT INTO massu_py_routes (file, method, path, function_name, dependencies, request_model, response_model, is_authenticated, line) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'
34
+ `INSERT INTO ${t('py_routes')} (file, method, path, function_name, dependencies, request_model, response_model, is_authenticated, line) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
34
35
  );
35
36
 
36
37
  const files = walkPyFiles(absRoot, excludeDirs);
@@ -5,6 +5,7 @@ import type Database from 'better-sqlite3';
5
5
  import type { ToolDefinition, ToolResult } from './tools.ts';
6
6
  import { getConfig } from './config.ts';
7
7
  import { classifyPythonFileDomain } from './python/domain-enforcer.ts';
8
+ import { t } from './lib/sql-table-names.ts';
8
9
 
9
10
  /** Get the configured tool prefix */
10
11
  function p(name: string): string {
@@ -195,7 +196,7 @@ function handlePyImports(args: Record<string, unknown>, dataDb: Database.Databas
195
196
  if (depth > maxDepth || visited.has(f)) return;
196
197
  visited.add(f);
197
198
  const imports = dataDb.prepare(
198
- 'SELECT target_file, imported_names FROM massu_py_imports WHERE source_file = ?'
199
+ `SELECT target_file, imported_names FROM ${t('py_imports')} WHERE source_file = ?`
199
200
  ).all(f) as { target_file: string; imported_names: string }[];
200
201
  for (const imp of imports) {
201
202
  const indent = ' '.repeat(depth);
@@ -207,7 +208,7 @@ function handlePyImports(args: Record<string, unknown>, dataDb: Database.Databas
207
208
  traverse(file, 0);
208
209
  } else {
209
210
  const imports = dataDb.prepare(
210
- 'SELECT target_file, import_type, imported_names, line FROM massu_py_imports WHERE source_file = ? ORDER BY line'
211
+ `SELECT target_file, import_type, imported_names, line FROM ${t('py_imports')} WHERE source_file = ? ORDER BY line`
211
212
  ).all(file) as { target_file: string; import_type: string; imported_names: string; line: number }[];
212
213
  lines.push(`## Imports from ${file} (${imports.length} edges)`);
213
214
  for (const imp of imports) {
@@ -217,7 +218,7 @@ function handlePyImports(args: Record<string, unknown>, dataDb: Database.Databas
217
218
  }
218
219
  } else if (importedBy) {
219
220
  const importers = dataDb.prepare(
220
- 'SELECT source_file, imported_names, line FROM massu_py_imports WHERE target_file = ? ORDER BY source_file'
221
+ `SELECT source_file, imported_names, line FROM ${t('py_imports')} WHERE target_file = ? ORDER BY source_file`
221
222
  ).all(importedBy) as { source_file: string; imported_names: string; line: number }[];
222
223
  lines.push(`## Files importing ${importedBy} (${importers.length} edges)`);
223
224
  for (const imp of importers) {
@@ -225,8 +226,8 @@ function handlePyImports(args: Record<string, unknown>, dataDb: Database.Databas
225
226
  lines.push(`- ${imp.source_file}:${imp.line}${names.length > 0 ? ' (' + names.join(', ') + ')' : ''}`);
226
227
  }
227
228
  } else {
228
- const total = (dataDb.prepare('SELECT COUNT(*) as cnt FROM massu_py_imports').get() as { cnt: number }).cnt;
229
- const files = (dataDb.prepare('SELECT COUNT(DISTINCT source_file) as cnt FROM massu_py_imports').get() as { cnt: number }).cnt;
229
+ const total = (dataDb.prepare(`SELECT COUNT(*) as cnt FROM ${t('py_imports')}`).get() as { cnt: number }).cnt;
230
+ const files = (dataDb.prepare(`SELECT COUNT(DISTINCT source_file) as cnt FROM ${t('py_imports')}`).get() as { cnt: number }).cnt;
230
231
  lines.push('## Python Import Index Summary');
231
232
  lines.push(`- Total import edges: ${total}`);
232
233
  lines.push(`- Files with imports: ${files}`);
@@ -241,7 +242,7 @@ function handlePyImports(args: Record<string, unknown>, dataDb: Database.Databas
241
242
  function handlePyRoutes(args: Record<string, unknown>, dataDb: Database.Database): ToolResult {
242
243
  const lines: string[] = [];
243
244
  const limit = Math.min(Math.max((args.limit as number) || 500, 1), 5000);
244
- let query = 'SELECT * FROM massu_py_routes WHERE 1=1';
245
+ let query = `SELECT * FROM ${t('py_routes')} WHERE 1=1`;
245
246
  const params: unknown[] = [];
246
247
 
247
248
  if (args.method) { query += ' AND method = ?'; params.push((args.method as string).toUpperCase()); }
@@ -258,7 +259,7 @@ function handlePyRoutes(args: Record<string, unknown>, dataDb: Database.Database
258
259
  }[];
259
260
 
260
261
  if (args.uncoupled) {
261
- const callerCountStmt = dataDb.prepare('SELECT COUNT(*) as cnt FROM massu_py_route_callers WHERE route_id = ?');
262
+ const callerCountStmt = dataDb.prepare(`SELECT COUNT(*) as cnt FROM ${t('py_route_callers')} WHERE route_id = ?`);
262
263
  const uncoupledRoutes = routes.filter(r => {
263
264
  const callers = (callerCountStmt.get(r.id) as { cnt: number }).cnt;
264
265
  return callers === 0;
@@ -286,8 +287,8 @@ function handlePyCoupling(args: Record<string, unknown>, dataDb: Database.Databa
286
287
  let query = `
287
288
  SELECT rc.frontend_file, rc.call_pattern, rc.line as caller_line,
288
289
  r.method, r.path, r.function_name, r.file as backend_file
289
- FROM massu_py_route_callers rc
290
- JOIN massu_py_routes r ON rc.route_id = r.id
290
+ FROM ${t('py_route_callers')} rc
291
+ JOIN ${t('py_routes')} r ON rc.route_id = r.id
291
292
  WHERE 1=1
292
293
  `;
293
294
  const params: unknown[] = [];
@@ -319,7 +320,7 @@ function handlePyModels(args: Record<string, unknown>, dataDb: Database.Database
319
320
  const lines: string[] = [];
320
321
 
321
322
  if (args.fk_graph) {
322
- const edges = dataDb.prepare('SELECT * FROM massu_py_fk_edges ORDER BY source_table').all() as {
323
+ const edges = dataDb.prepare(`SELECT * FROM ${t('py_fk_edges')} ORDER BY source_table`).all() as {
323
324
  source_table: string; source_column: string; target_table: string; target_column: string;
324
325
  }[];
325
326
  lines.push(`## Foreign Key Graph (${edges.length} edges)`);
@@ -329,7 +330,7 @@ function handlePyModels(args: Record<string, unknown>, dataDb: Database.Database
329
330
  } else if (args.model || args.table) {
330
331
  const search = (args.model || args.table) as string;
331
332
  const model = dataDb.prepare(
332
- 'SELECT * FROM massu_py_models WHERE class_name = ? OR table_name = ?'
333
+ `SELECT * FROM ${t('py_models')} WHERE class_name = ? OR table_name = ?`
333
334
  ).get(search, search) as {
334
335
  class_name: string; table_name: string; file: string; line: number;
335
336
  columns: string; relationships: string; foreign_keys: string;
@@ -365,7 +366,7 @@ function handlePyModels(args: Record<string, unknown>, dataDb: Database.Database
365
366
  }
366
367
  } else {
367
368
  const limit = Math.min(Math.max((args.limit as number) || 500, 1), 5000);
368
- const models = dataDb.prepare(`SELECT class_name, table_name, file, line FROM massu_py_models ORDER BY class_name LIMIT ${limit}`).all() as {
369
+ const models = dataDb.prepare(`SELECT class_name, table_name, file, line FROM ${t('py_models')} ORDER BY class_name LIMIT ${limit}`).all() as {
369
370
  class_name: string; table_name: string; file: string; line: number;
370
371
  }[];
371
372
  lines.push(`## Python Models (${models.length})`);
@@ -382,10 +383,10 @@ function handlePyMigrations(args: Record<string, unknown>, dataDb: Database.Data
382
383
 
383
384
  if (args.drift) {
384
385
  // Compare models against migration operations
385
- const models = dataDb.prepare('SELECT class_name, table_name, columns FROM massu_py_models').all() as {
386
+ const models = dataDb.prepare(`SELECT class_name, table_name, columns FROM ${t('py_models')}`).all() as {
386
387
  class_name: string; table_name: string; columns: string;
387
388
  }[];
388
- const migrations = dataDb.prepare('SELECT revision, operations FROM massu_py_migrations').all() as {
389
+ const migrations = dataDb.prepare(`SELECT revision, operations FROM ${t('py_migrations')}`).all() as {
389
390
  revision: string; operations: string;
390
391
  }[];
391
392
 
@@ -409,7 +410,7 @@ function handlePyMigrations(args: Record<string, unknown>, dataDb: Database.Data
409
410
  }
410
411
  } else if (args.chain) {
411
412
  const limit = Math.min(Math.max((args.limit as number) || 500, 1), 5000);
412
- const migrations = dataDb.prepare(`SELECT * FROM massu_py_migrations ORDER BY id LIMIT ${limit}`).all() as {
413
+ const migrations = dataDb.prepare(`SELECT * FROM ${t('py_migrations')} ORDER BY id LIMIT ${limit}`).all() as {
413
414
  revision: string; down_revision: string | null; file: string; description: string | null; is_head: number;
414
415
  }[];
415
416
  lines.push(`## Migration Chain (${migrations.length} revisions)`);
@@ -418,7 +419,7 @@ function handlePyMigrations(args: Record<string, unknown>, dataDb: Database.Data
418
419
  lines.push(`- ${m.revision}${head} ← ${m.down_revision || 'BASE'}: ${m.description || '(no description)'} (${m.file})`);
419
420
  }
420
421
  } else if (args.revision) {
421
- const m = dataDb.prepare('SELECT * FROM massu_py_migrations WHERE revision = ?').get(args.revision) as {
422
+ const m = dataDb.prepare(`SELECT * FROM ${t('py_migrations')} WHERE revision = ?`).get(args.revision) as {
422
423
  revision: string; down_revision: string | null; file: string; description: string | null;
423
424
  operations: string; is_head: number;
424
425
  } | undefined;
@@ -435,8 +436,8 @@ function handlePyMigrations(args: Record<string, unknown>, dataDb: Database.Data
435
436
  }
436
437
  }
437
438
  } else {
438
- const total = (dataDb.prepare('SELECT COUNT(*) as cnt FROM massu_py_migrations').get() as { cnt: number }).cnt;
439
- const head = dataDb.prepare('SELECT revision FROM massu_py_migrations WHERE is_head = 1').get() as { revision: string } | undefined;
439
+ const total = (dataDb.prepare(`SELECT COUNT(*) as cnt FROM ${t('py_migrations')}`).get() as { cnt: number }).cnt;
440
+ const head = dataDb.prepare(`SELECT revision FROM ${t('py_migrations')} WHERE is_head = 1`).get() as { revision: string } | undefined;
440
441
  lines.push('## Migration Summary');
441
442
  lines.push(`- Total revisions: ${total}`);
442
443
  lines.push(`- Current head: ${head?.revision || 'N/A'}`);
@@ -460,7 +461,7 @@ function handlePyDomains(args: Record<string, unknown>, dataDb: Database.Databas
460
461
  lines.push(`Allowed imports from: ${domainConfig.allowed_imports_from.join(', ') || 'any'}`);
461
462
  }
462
463
  } else if (args.crossings) {
463
- const imports = dataDb.prepare('SELECT source_file, target_file FROM massu_py_imports').all() as {
464
+ const imports = dataDb.prepare(`SELECT source_file, target_file FROM ${t('py_imports')}`).all() as {
464
465
  source_file: string; target_file: string;
465
466
  }[];
466
467
  const violations: string[] = [];
@@ -481,7 +482,7 @@ function handlePyDomains(args: Record<string, unknown>, dataDb: Database.Databas
481
482
  if (violations.length > 50) lines.push(`... and ${violations.length - 50} more`);
482
483
  } else if (args.domain) {
483
484
  const domainName = args.domain as string;
484
- const allFiles = dataDb.prepare('SELECT DISTINCT source_file FROM massu_py_imports UNION SELECT DISTINCT target_file FROM massu_py_imports').all() as { source_file: string }[];
485
+ const allFiles = dataDb.prepare(`SELECT DISTINCT source_file FROM ${t('py_imports')} UNION SELECT DISTINCT target_file FROM ${t('py_imports')}`).all() as { source_file: string }[];
485
486
  const filesInDomain = allFiles.filter(f => classifyPythonFileDomain(f.source_file) === domainName);
486
487
  lines.push(`## Domain: ${domainName} (${filesInDomain.length} files)`);
487
488
  for (const f of filesInDomain) {
@@ -503,7 +504,7 @@ function handlePyImpact(args: Record<string, unknown>, dataDb: Database.Database
503
504
 
504
505
  // 1. Who imports this file
505
506
  const importedBy = dataDb.prepare(
506
- 'SELECT source_file FROM massu_py_imports WHERE target_file = ?'
507
+ `SELECT source_file FROM ${t('py_imports')} WHERE target_file = ?`
507
508
  ).all(file) as { source_file: string }[];
508
509
  lines.push(`### Imported By (${importedBy.length} files)`);
509
510
  for (const imp of importedBy.slice(0, 20)) {
@@ -512,7 +513,7 @@ function handlePyImpact(args: Record<string, unknown>, dataDb: Database.Database
512
513
 
513
514
  // 2. Routes in this file
514
515
  const routes = dataDb.prepare(
515
- 'SELECT method, path, function_name FROM massu_py_routes WHERE file = ?'
516
+ `SELECT method, path, function_name FROM ${t('py_routes')} WHERE file = ?`
516
517
  ).all(file) as { method: string; path: string; function_name: string }[];
517
518
  if (routes.length > 0) {
518
519
  lines.push(`\n### Routes Defined (${routes.length})`);
@@ -523,7 +524,7 @@ function handlePyImpact(args: Record<string, unknown>, dataDb: Database.Database
523
524
 
524
525
  // 3. Models in this file
525
526
  const models = dataDb.prepare(
526
- 'SELECT class_name, table_name FROM massu_py_models WHERE file = ?'
527
+ `SELECT class_name, table_name FROM ${t('py_models')} WHERE file = ?`
527
528
  ).all(file) as { class_name: string; table_name: string }[];
528
529
  if (models.length > 0) {
529
530
  lines.push(`\n### Models Defined (${models.length})`);
@@ -533,11 +534,11 @@ function handlePyImpact(args: Record<string, unknown>, dataDb: Database.Database
533
534
  }
534
535
 
535
536
  // 4. Frontend callers (via routes)
536
- const routeIds = dataDb.prepare('SELECT id FROM massu_py_routes WHERE file = ?').all(file) as { id: number }[];
537
+ const routeIds = dataDb.prepare(`SELECT id FROM ${t('py_routes')} WHERE file = ?`).all(file) as { id: number }[];
537
538
  if (routeIds.length > 0) {
538
539
  const placeholders = routeIds.map(() => '?').join(',');
539
540
  const callers = dataDb.prepare(
540
- `SELECT DISTINCT frontend_file FROM massu_py_route_callers WHERE route_id IN (${placeholders})`
541
+ `SELECT DISTINCT frontend_file FROM ${t('py_route_callers')} WHERE route_id IN (${placeholders})`
541
542
  ).all(...routeIds.map(r => r.id)) as { frontend_file: string }[];
542
543
  if (callers.length > 0) {
543
544
  lines.push(`\n### Frontend Callers (${callers.length} files)`);
@@ -566,7 +567,7 @@ function handlePyContext(args: Record<string, unknown>, dataDb: Database.Databas
566
567
 
567
568
  // Imports
568
569
  const imports = dataDb.prepare(
569
- 'SELECT target_file, imported_names FROM massu_py_imports WHERE source_file = ? LIMIT 20'
570
+ `SELECT target_file, imported_names FROM ${t('py_imports')} WHERE source_file = ? LIMIT 20`
570
571
  ).all(file) as { target_file: string; imported_names: string }[];
571
572
  if (imports.length > 0) {
572
573
  lines.push('\n### Imports');
@@ -578,7 +579,7 @@ function handlePyContext(args: Record<string, unknown>, dataDb: Database.Databas
578
579
 
579
580
  // Imported by
580
581
  const importedBy = dataDb.prepare(
581
- 'SELECT source_file FROM massu_py_imports WHERE target_file = ? LIMIT 10'
582
+ `SELECT source_file FROM ${t('py_imports')} WHERE target_file = ? LIMIT 10`
582
583
  ).all(file) as { source_file: string }[];
583
584
  if (importedBy.length > 0) {
584
585
  lines.push('\n### Imported By');
@@ -588,7 +589,7 @@ function handlePyContext(args: Record<string, unknown>, dataDb: Database.Databas
588
589
  }
589
590
 
590
591
  // Routes
591
- const routes = dataDb.prepare('SELECT method, path, function_name, line FROM massu_py_routes WHERE file = ?')
592
+ const routes = dataDb.prepare(`SELECT method, path, function_name, line FROM ${t('py_routes')} WHERE file = ?`)
592
593
  .all(file) as { method: string; path: string; function_name: string; line: number }[];
593
594
  if (routes.length > 0) {
594
595
  lines.push('\n### Routes');
@@ -598,7 +599,7 @@ function handlePyContext(args: Record<string, unknown>, dataDb: Database.Databas
598
599
  }
599
600
 
600
601
  // Models
601
- const models = dataDb.prepare('SELECT class_name, table_name, line FROM massu_py_models WHERE file = ?')
602
+ const models = dataDb.prepare(`SELECT class_name, table_name, line FROM ${t('py_models')} WHERE file = ?`)
602
603
  .all(file) as { class_name: string; table_name: string; line: number }[];
603
604
  if (models.length > 0) {
604
605
  lines.push('\n### Models');
@@ -1,4 +1,4 @@
1
- // AUTO-GENERATED by scripts/bundle-license-pubkey.mjs at 2026-05-17T15:41:04.385Z.
1
+ // AUTO-GENERATED by scripts/bundle-license-pubkey.mjs at 2026-05-17T15:51:32.373Z.
2
2
  // Source pem: packages/core/security/license-pubkey.pem
3
3
  // RAW-bytes sha256: 18a63d64fdec9e5a368fc45feaa49bed6ced815967e582bc7b8af534f22a9475
4
4
  // DO NOT EDIT — regenerate via `node scripts/bundle-license-pubkey.mjs` or
@@ -1,4 +1,4 @@
1
- // AUTO-GENERATED by scripts/bundle-pubkey.mjs at 2026-05-17T15:41:04.323Z.
1
+ // AUTO-GENERATED by scripts/bundle-pubkey.mjs at 2026-05-17T15:51:32.309Z.
2
2
  // Source pem: packages/core/security/registry-pubkey.pem
3
3
  // RAW-bytes sha256: 3b6226d036c472e533110d11a7d0cd2773ce1d7d4f1003517d5bd69c5418ed4c
4
4
  // DO NOT EDIT — regenerate via `node scripts/bundle-pubkey.mjs` or