@massu/core 1.10.6 → 1.10.8
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/dist/cli.js +405 -337
- package/dist/hooks/post-edit-context.js +6 -1
- package/dist/hooks/pre-delete-check.js +12 -7
- package/package.json +1 -1
- package/src/db.ts +63 -62
- package/src/domains.ts +2 -1
- package/src/hooks/pre-delete-check.ts +7 -5
- package/src/import-resolver.ts +3 -2
- package/src/knowledge-tools.ts +3 -2
- package/src/lib/framework-supports.ts +85 -0
- package/src/lib/sql-table-names.ts +71 -0
- package/src/middleware-tree.ts +6 -5
- package/src/page-deps.ts +9 -8
- package/src/python/coupling-detector.ts +4 -3
- package/src/python/domain-enforcer.ts +3 -2
- package/src/python/impact-analyzer.ts +7 -6
- package/src/python/import-resolver.ts +3 -2
- package/src/python/migration-indexer.ts +5 -4
- package/src/python/model-indexer.ts +5 -4
- package/src/python/route-indexer.ts +4 -3
- package/src/python-tools.ts +30 -29
- package/src/security/license-pubkey.generated.ts +1 -1
- package/src/security/registry-pubkey.generated.ts +1 -1
- package/src/sentinel-db.ts +47 -46
- package/src/sentinel-scanner.ts +3 -2
- package/src/sentinel-tools.ts +2 -1
- package/src/tools.ts +18 -16
- package/src/trpc-index.ts +5 -4
- package/src/validate-features-runner.ts +5 -4
package/src/tools.ts
CHANGED
|
@@ -38,7 +38,9 @@ import { getKnowledgeToolDefinitions, isKnowledgeTool, handleKnowledgeToolCall }
|
|
|
38
38
|
import { getKnowledgeDb } from './knowledge-db.ts';
|
|
39
39
|
import { getPythonToolDefinitions, isPythonTool, handlePythonToolCall } from './python-tools.ts';
|
|
40
40
|
import { getConfig, getProjectRoot, getResolvedPaths } from './config.ts';
|
|
41
|
+
import { supportsRouter, supportsOrm } from './lib/framework-supports.ts';
|
|
41
42
|
import { getCurrentTier, getToolTier, isToolAllowed, annotateToolDefinitions, getLicenseToolDefinitions, isLicenseTool, handleLicenseToolCall, isCloudFeatureAvailable } from './license.ts';
|
|
43
|
+
import { t } from './lib/sql-table-names.ts';
|
|
42
44
|
|
|
43
45
|
export interface ToolDefinition {
|
|
44
46
|
name: string;
|
|
@@ -98,7 +100,7 @@ function ensureIndexes(dataDb: Database.Database, codegraphDb?: Database.Databas
|
|
|
98
100
|
const importCount = buildImportIndex(dataDb, codegraphDb);
|
|
99
101
|
results.push(`Import edges: ${importCount}`);
|
|
100
102
|
|
|
101
|
-
if (
|
|
103
|
+
if (supportsRouter('trpc')) {
|
|
102
104
|
const trpcStats = buildTrpcIndex(dataDb);
|
|
103
105
|
results.push(`tRPC procedures: ${trpcStats.totalProcedures} (${trpcStats.withCallers} with UI, ${trpcStats.withoutCallers} without)`);
|
|
104
106
|
}
|
|
@@ -202,7 +204,7 @@ export function getToolDefinitions(): ToolDefinition[] {
|
|
|
202
204
|
required: ['file'],
|
|
203
205
|
},
|
|
204
206
|
},
|
|
205
|
-
...(
|
|
207
|
+
...(supportsRouter('trpc') ? [
|
|
206
208
|
{
|
|
207
209
|
name: p('trpc_map'),
|
|
208
210
|
description: 'Map tRPC procedures to their UI call sites. Find which components call a router, which procedures have no UI callers, or list all procedures for a router.',
|
|
@@ -256,7 +258,7 @@ export function getToolDefinitions(): ToolDefinition[] {
|
|
|
256
258
|
required: [],
|
|
257
259
|
},
|
|
258
260
|
}] : []),
|
|
259
|
-
...(
|
|
261
|
+
...(supportsOrm('prisma') ? [{
|
|
260
262
|
name: p('schema'),
|
|
261
263
|
description: 'Prisma schema cross-reference. Show columns for a table, detect mismatches between code and schema, or verify column references in a file.',
|
|
262
264
|
inputSchema: {
|
|
@@ -572,7 +574,7 @@ function handleContext(file: string, dataDb: Database.Database, codegraphDb: Dat
|
|
|
572
574
|
|
|
573
575
|
// 5. Import edges
|
|
574
576
|
const imports = dataDb.prepare(
|
|
575
|
-
|
|
577
|
+
`SELECT target_file, imported_names FROM ${t('imports')} WHERE source_file = ? LIMIT 20`
|
|
576
578
|
).all(file) as { target_file: string; imported_names: string }[];
|
|
577
579
|
|
|
578
580
|
if (imports.length > 0) {
|
|
@@ -586,7 +588,7 @@ function handleContext(file: string, dataDb: Database.Database, codegraphDb: Dat
|
|
|
586
588
|
|
|
587
589
|
// 6. Imported BY
|
|
588
590
|
const importedBy = dataDb.prepare(
|
|
589
|
-
|
|
591
|
+
`SELECT source_file FROM ${t('imports')} WHERE target_file = ? LIMIT 20`
|
|
590
592
|
).all(file) as { source_file: string }[];
|
|
591
593
|
|
|
592
594
|
if (importedBy.length > 0) {
|
|
@@ -786,7 +788,7 @@ function handleTrpcMap(args: Record<string, unknown>, dataDb: Database.Database)
|
|
|
786
788
|
|
|
787
789
|
if (args.uncoupled) {
|
|
788
790
|
const uncoupled = dataDb.prepare(
|
|
789
|
-
|
|
791
|
+
`SELECT router_name, procedure_name, procedure_type, router_file FROM ${t('trpc_procedures')} WHERE has_ui_caller = 0 ORDER BY router_name, procedure_name`
|
|
790
792
|
).all() as { router_name: string; procedure_name: string; procedure_type: string; router_file: string }[];
|
|
791
793
|
|
|
792
794
|
lines.push(`## Uncoupled Procedures (${uncoupled.length} total)`);
|
|
@@ -803,7 +805,7 @@ function handleTrpcMap(args: Record<string, unknown>, dataDb: Database.Database)
|
|
|
803
805
|
}
|
|
804
806
|
} else if (args.router) {
|
|
805
807
|
const procs = dataDb.prepare(
|
|
806
|
-
|
|
808
|
+
`SELECT id, procedure_name, procedure_type, has_ui_caller FROM ${t('trpc_procedures')} WHERE router_name = ? ORDER BY procedure_name`
|
|
807
809
|
).all(args.router as string) as { id: number; procedure_name: string; procedure_type: string; has_ui_caller: number }[];
|
|
808
810
|
|
|
809
811
|
lines.push(`## Router: ${args.router} (${procs.length} procedures)`);
|
|
@@ -814,7 +816,7 @@ function handleTrpcMap(args: Record<string, unknown>, dataDb: Database.Database)
|
|
|
814
816
|
lines.push(`### ${args.router}.${proc.procedure_name} (${proc.procedure_type})${status}`);
|
|
815
817
|
|
|
816
818
|
const callSites = dataDb.prepare(
|
|
817
|
-
|
|
819
|
+
`SELECT file, line, call_pattern FROM ${t('trpc_call_sites')} WHERE procedure_id = ?`
|
|
818
820
|
).all(proc.id) as { file: string; line: number; call_pattern: string }[];
|
|
819
821
|
|
|
820
822
|
if (callSites.length > 0) {
|
|
@@ -829,7 +831,7 @@ function handleTrpcMap(args: Record<string, unknown>, dataDb: Database.Database)
|
|
|
829
831
|
}
|
|
830
832
|
} else if (args.procedure) {
|
|
831
833
|
const procs = dataDb.prepare(
|
|
832
|
-
|
|
834
|
+
`SELECT id, router_name, router_file, procedure_type, has_ui_caller FROM ${t('trpc_procedures')} WHERE procedure_name = ? ORDER BY router_name`
|
|
833
835
|
).all(args.procedure as string) as { id: number; router_name: string; router_file: string; procedure_type: string; has_ui_caller: number }[];
|
|
834
836
|
|
|
835
837
|
lines.push(`## Procedure "${args.procedure}" found in ${procs.length} routers`);
|
|
@@ -840,7 +842,7 @@ function handleTrpcMap(args: Record<string, unknown>, dataDb: Database.Database)
|
|
|
840
842
|
lines.push(`File: ${proc.router_file}`);
|
|
841
843
|
|
|
842
844
|
const callSites = dataDb.prepare(
|
|
843
|
-
|
|
845
|
+
`SELECT file, line, call_pattern FROM ${t('trpc_call_sites')} WHERE procedure_id = ?`
|
|
844
846
|
).all(proc.id) as { file: string; line: number; call_pattern: string }[];
|
|
845
847
|
|
|
846
848
|
if (callSites.length > 0) {
|
|
@@ -854,8 +856,8 @@ function handleTrpcMap(args: Record<string, unknown>, dataDb: Database.Database)
|
|
|
854
856
|
lines.push('');
|
|
855
857
|
}
|
|
856
858
|
} else {
|
|
857
|
-
const total = dataDb.prepare(
|
|
858
|
-
const coupled = dataDb.prepare(
|
|
859
|
+
const total = dataDb.prepare(`SELECT COUNT(*) as count FROM ${t('trpc_procedures')}`).get() as { count: number };
|
|
860
|
+
const coupled = dataDb.prepare(`SELECT COUNT(*) as count FROM ${t('trpc_procedures')} WHERE has_ui_caller = 1`).get() as { count: number };
|
|
859
861
|
const uncoupled = total.count - coupled.count;
|
|
860
862
|
|
|
861
863
|
// P-H009 (plan-stage-c-high-batch): when the index is empty, return an
|
|
@@ -893,11 +895,11 @@ function handleCouplingCheck(args: Record<string, unknown>, dataDb: Database.Dat
|
|
|
893
895
|
let uncoupledProcs;
|
|
894
896
|
if (stagedFiles) {
|
|
895
897
|
uncoupledProcs = dataDb.prepare(
|
|
896
|
-
`SELECT router_name, procedure_name, procedure_type, router_file FROM
|
|
898
|
+
`SELECT router_name, procedure_name, procedure_type, router_file FROM ${t('trpc_procedures')} WHERE has_ui_caller = 0 AND router_file IN (${stagedFiles.map(() => '?').join(',')})`
|
|
897
899
|
).all(...stagedFiles) as { router_name: string; procedure_name: string; procedure_type: string; router_file: string }[];
|
|
898
900
|
} else {
|
|
899
901
|
uncoupledProcs = dataDb.prepare(
|
|
900
|
-
|
|
902
|
+
`SELECT router_name, procedure_name, procedure_type, router_file FROM ${t('trpc_procedures')} WHERE has_ui_caller = 0`
|
|
901
903
|
).all() as { router_name: string; procedure_name: string; procedure_type: string; router_file: string }[];
|
|
902
904
|
}
|
|
903
905
|
|
|
@@ -922,7 +924,7 @@ function handleCouplingCheck(args: Record<string, unknown>, dataDb: Database.Dat
|
|
|
922
924
|
const pageImports = new Set<string>();
|
|
923
925
|
for (const page of allPages) {
|
|
924
926
|
const imports = dataDb.prepare(
|
|
925
|
-
|
|
927
|
+
`SELECT target_file FROM ${t('imports')} WHERE source_file = ?`
|
|
926
928
|
).all(page.path) as { target_file: string }[];
|
|
927
929
|
for (const imp of imports) {
|
|
928
930
|
pageImports.add(imp.target_file);
|
|
@@ -1009,7 +1011,7 @@ function handleImpact(file: string, dataDb: Database.Database, codegraphDb: Data
|
|
|
1009
1011
|
lines.push(`### Domain: ${fileDomain}`);
|
|
1010
1012
|
|
|
1011
1013
|
const imports = dataDb.prepare(
|
|
1012
|
-
|
|
1014
|
+
`SELECT target_file FROM ${t('imports')} WHERE source_file = ?`
|
|
1013
1015
|
).all(file) as { target_file: string }[];
|
|
1014
1016
|
|
|
1015
1017
|
const crossings: string[] = [];
|
package/src/trpc-index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { readFileSync, existsSync, readdirSync } from 'fs';
|
|
|
5
5
|
import { resolve, join } from 'path';
|
|
6
6
|
import type Database from 'better-sqlite3';
|
|
7
7
|
import { getConfig, getResolvedPaths, getProjectRoot } from './config.ts';
|
|
8
|
+
import { t } from './lib/sql-table-names.ts';
|
|
8
9
|
|
|
9
10
|
interface RouterMapping {
|
|
10
11
|
key: string; // e.g., "orders" (used as api.orders.*)
|
|
@@ -170,16 +171,16 @@ function escapeRegex(str: string): string {
|
|
|
170
171
|
*/
|
|
171
172
|
export function buildTrpcIndex(dataDb: Database.Database): { totalProcedures: number; withCallers: number; withoutCallers: number } {
|
|
172
173
|
// Clear existing data
|
|
173
|
-
dataDb.exec(
|
|
174
|
-
dataDb.exec(
|
|
174
|
+
dataDb.exec(`DELETE FROM ${t('trpc_call_sites')}`);
|
|
175
|
+
dataDb.exec(`DELETE FROM ${t('trpc_procedures')}`);
|
|
175
176
|
|
|
176
177
|
const routerMappings = parseRootRouter();
|
|
177
178
|
|
|
178
179
|
const insertProc = dataDb.prepare(
|
|
179
|
-
|
|
180
|
+
`INSERT INTO ${t('trpc_procedures')} (router_file, router_name, procedure_name, procedure_type, has_ui_caller) VALUES (?, ?, ?, ?, ?)`
|
|
180
181
|
);
|
|
181
182
|
const insertCallSite = dataDb.prepare(
|
|
182
|
-
|
|
183
|
+
`INSERT INTO ${t('trpc_call_sites')} (procedure_id, file, line, call_pattern) VALUES (?, ?, ?, ?)`
|
|
183
184
|
);
|
|
184
185
|
|
|
185
186
|
let totalProcedures = 0;
|
|
@@ -12,6 +12,7 @@ import Database from 'better-sqlite3';
|
|
|
12
12
|
import { resolve } from 'path';
|
|
13
13
|
import { existsSync } from 'fs';
|
|
14
14
|
import { getProjectRoot, getResolvedPaths } from './config.ts';
|
|
15
|
+
import { t } from './lib/sql-table-names.ts';
|
|
15
16
|
|
|
16
17
|
const PROJECT_ROOT = getProjectRoot();
|
|
17
18
|
|
|
@@ -35,7 +36,7 @@ function main(): void {
|
|
|
35
36
|
try {
|
|
36
37
|
// Check if sentinel tables exist
|
|
37
38
|
const tableExists = db.prepare(
|
|
38
|
-
"SELECT name FROM sqlite_master WHERE type='table' AND name
|
|
39
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name=`${t('sentinel')}`"
|
|
39
40
|
).get();
|
|
40
41
|
|
|
41
42
|
if (!tableExists) {
|
|
@@ -45,7 +46,7 @@ function main(): void {
|
|
|
45
46
|
|
|
46
47
|
// Count active features
|
|
47
48
|
const totalActive = db.prepare(
|
|
48
|
-
|
|
49
|
+
`SELECT COUNT(*) as count FROM ${t('sentinel')} WHERE status = 'active'`
|
|
49
50
|
).get() as { count: number };
|
|
50
51
|
|
|
51
52
|
if (totalActive.count === 0) {
|
|
@@ -56,8 +57,8 @@ function main(): void {
|
|
|
56
57
|
// Check for orphaned features (active features with missing primary component files)
|
|
57
58
|
const orphaned = db.prepare(`
|
|
58
59
|
SELECT s.feature_key, s.title, s.priority, c.component_file
|
|
59
|
-
FROM
|
|
60
|
-
JOIN
|
|
60
|
+
FROM ${t('sentinel')} s
|
|
61
|
+
JOIN ${t('sentinel_components')} c ON c.feature_id = s.id AND c.is_primary = 1
|
|
61
62
|
WHERE s.status = 'active'
|
|
62
63
|
ORDER BY s.priority DESC, s.domain, s.feature_key
|
|
63
64
|
`).all() as { feature_key: string; title: string; priority: string; component_file: string }[];
|