@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.
- package/CHANGELOG.md +1334 -0
- package/dist/adapter.js +6 -4
- package/dist/cli.js +581 -448
- package/dist/detect/adapters/.bundle-shasums.json +4 -7
- package/dist/detect/adapters/rails.js +6 -4
- package/dist/detect/adapters/spring.js +6 -4
- package/dist/detect/adapters/tree-sitter-loader.js +6 -4
- package/dist/hooks/auto-learning-pipeline.js +24 -1
- package/dist/hooks/classify-failure.js +32 -3
- package/dist/hooks/cost-tracker.js +24 -0
- package/dist/hooks/fix-detector.js +97 -1
- package/dist/hooks/incident-pipeline.js +30 -1
- package/dist/hooks/intent-suggester.js +6 -3
- package/dist/hooks/post-edit-context.js +24 -1
- package/dist/hooks/post-tool-use.js +67 -19
- package/dist/hooks/pre-compact.js +28 -3
- package/dist/hooks/pre-delete-check.js +53 -27
- package/dist/hooks/pre-tool-use-gate.js +962 -0
- package/dist/hooks/quality-event.js +24 -0
- package/dist/hooks/rule-enforcement-pipeline.js +24 -1
- package/dist/hooks/security-gate.js +29 -16
- package/dist/hooks/session-end.js +31 -6
- package/dist/hooks/session-start.js +8709 -8459
- package/dist/hooks/user-prompt.js +25 -1
- package/package.json +15 -18
- package/commands/README.md +0 -134
- package/src/adapter.ts +0 -121
- package/src/adr-generator.ts +0 -292
- package/src/analytics.ts +0 -373
- package/src/audit-trail.ts +0 -450
- package/src/backfill-sessions.ts +0 -182
- package/src/changelog-generator.ts +0 -178
- package/src/cli.ts +0 -246
- package/src/cloud-sync.ts +0 -234
- package/src/commands/adapters.ts +0 -824
- package/src/commands/changelog.ts +0 -165
- package/src/commands/config-check-drift.ts +0 -133
- package/src/commands/config-refresh.ts +0 -396
- package/src/commands/config-upgrade.ts +0 -127
- package/src/commands/doctor.ts +0 -485
- package/src/commands/hook-runner.ts +0 -145
- package/src/commands/init.ts +0 -1823
- package/src/commands/install-commands.ts +0 -692
- package/src/commands/install-hooks.ts +0 -27
- package/src/commands/permissions.ts +0 -150
- package/src/commands/refresh-log.ts +0 -37
- package/src/commands/show-template.ts +0 -65
- package/src/commands/template-engine.ts +0 -301
- package/src/commands/watch.ts +0 -430
- package/src/config.ts +0 -765
- package/src/cost-tracker.ts +0 -355
- package/src/db.ts +0 -370
- package/src/dependency-scorer.ts +0 -337
- package/src/detect/adapters/aspnet.ts +0 -4
- package/src/detect/adapters/discover.ts +0 -469
- package/src/detect/adapters/file-sampler.ts +0 -225
- package/src/detect/adapters/go-chi.ts +0 -4
- package/src/detect/adapters/index.ts +0 -51
- package/src/detect/adapters/nextjs-trpc.ts +0 -166
- package/src/detect/adapters/parse-guard.ts +0 -133
- package/src/detect/adapters/phoenix.ts +0 -4
- package/src/detect/adapters/python-django.ts +0 -208
- package/src/detect/adapters/python-fastapi.ts +0 -223
- package/src/detect/adapters/python-flask.ts +0 -235
- package/src/detect/adapters/query-helpers.ts +0 -170
- package/src/detect/adapters/rails.ts +0 -4
- package/src/detect/adapters/runner.ts +0 -284
- package/src/detect/adapters/spring.ts +0 -4
- package/src/detect/adapters/swift-swiftui.ts +0 -171
- package/src/detect/adapters/tree-sitter-loader.ts +0 -517
- package/src/detect/adapters/types.ts +0 -191
- package/src/detect/codebase-introspector.ts +0 -210
- package/src/detect/domain-inferrer.ts +0 -197
- package/src/detect/drift.ts +0 -199
- package/src/detect/framework-detector.ts +0 -307
- package/src/detect/index.ts +0 -209
- package/src/detect/manifest-registry.ts +0 -261
- package/src/detect/migrate.ts +0 -362
- package/src/detect/monorepo-detector.ts +0 -348
- package/src/detect/package-detector.ts +0 -828
- package/src/detect/passthrough.ts +0 -108
- package/src/detect/regex-fallback.ts +0 -449
- package/src/detect/source-dir-detector.ts +0 -297
- package/src/detect/vr-command-map.ts +0 -167
- package/src/docs-map.json +0 -100
- package/src/docs-tools.ts +0 -519
- package/src/domains.ts +0 -182
- package/src/hooks/auto-learning-pipeline.ts +0 -228
- package/src/hooks/classify-failure.ts +0 -259
- package/src/hooks/cost-tracker.ts +0 -66
- package/src/hooks/fix-detector.ts +0 -186
- package/src/hooks/incident-pipeline.ts +0 -190
- package/src/hooks/intent-suggester.ts +0 -131
- package/src/hooks/post-edit-context.ts +0 -91
- package/src/hooks/post-tool-use.ts +0 -413
- package/src/hooks/pre-compact.ts +0 -168
- package/src/hooks/pre-delete-check.ts +0 -243
- package/src/hooks/quality-event.ts +0 -127
- package/src/hooks/rule-enforcement-pipeline.ts +0 -159
- package/src/hooks/security-gate.ts +0 -153
- package/src/hooks/session-end.ts +0 -467
- package/src/hooks/session-start.ts +0 -436
- package/src/hooks/user-prompt.ts +0 -157
- package/src/import-resolver.ts +0 -226
- package/src/knowledge-db.ts +0 -176
- package/src/knowledge-indexer.ts +0 -708
- package/src/knowledge-tools.ts +0 -1414
- package/src/lib/fileLock.ts +0 -203
- package/src/lib/framework-supports.ts +0 -85
- package/src/lib/gitToplevel.ts +0 -22
- package/src/lib/hook-registry.ts +0 -43
- package/src/lib/installLock.ts +0 -66
- package/src/lib/memory-path.ts +0 -49
- package/src/lib/pidLiveness.ts +0 -67
- package/src/lib/settings-local.ts +0 -110
- package/src/lib/sql-table-names.ts +0 -71
- package/src/license.ts +0 -547
- package/src/lsp/auto-detect.ts +0 -98
- package/src/lsp/client.ts +0 -776
- package/src/lsp/enrich.ts +0 -127
- package/src/lsp/types.ts +0 -221
- package/src/memory-db.ts +0 -1614
- package/src/memory-file-ingest.ts +0 -128
- package/src/memory-tools.ts +0 -424
- package/src/middleware-tree.ts +0 -71
- package/src/observability-tools.ts +0 -343
- package/src/observation-extractor.ts +0 -418
- package/src/page-deps.ts +0 -285
- package/src/permissions.ts +0 -422
- package/src/prompt-analyzer.ts +0 -332
- package/src/python/coupling-detector.ts +0 -125
- package/src/python/domain-enforcer.ts +0 -84
- package/src/python/impact-analyzer.ts +0 -96
- package/src/python/import-parser.ts +0 -244
- package/src/python/import-resolver.ts +0 -136
- package/src/python/migration-indexer.ts +0 -116
- package/src/python/migration-parser.ts +0 -332
- package/src/python/model-indexer.ts +0 -71
- package/src/python/model-parser.ts +0 -279
- package/src/python/route-indexer.ts +0 -59
- package/src/python/route-parser.ts +0 -317
- package/src/python-tools.ts +0 -630
- package/src/regression-detector.ts +0 -319
- package/src/rules.ts +0 -57
- package/src/schema-mapper.ts +0 -232
- package/src/security/adapter-origin.ts +0 -130
- package/src/security/adapter-verifier.ts +0 -319
- package/src/security/atomic-write.ts +0 -164
- package/src/security/fetcher.ts +0 -200
- package/src/security/install-tracking.ts +0 -319
- package/src/security/license-pubkey.generated.ts +0 -30
- package/src/security/license-response-verifier.ts +0 -149
- package/src/security/local-fingerprint.ts +0 -225
- package/src/security/manifest-cache.ts +0 -333
- package/src/security/manifest-schema.ts +0 -129
- package/src/security/registry-pubkey.generated.ts +0 -35
- package/src/security/telemetry.ts +0 -320
- package/src/security-scorer.ts +0 -405
- package/src/security-utils.ts +0 -133
- package/src/sentinel-db.ts +0 -580
- package/src/sentinel-scanner.ts +0 -406
- package/src/sentinel-tools.ts +0 -513
- package/src/sentinel-types.ts +0 -140
- package/src/server-dispatch.ts +0 -225
- package/src/server.ts +0 -107
- package/src/session-archiver.ts +0 -111
- package/src/session-state-generator.ts +0 -174
- package/src/team-knowledge.ts +0 -407
- package/src/tool-db-needs.ts +0 -232
- package/src/tools.ts +0 -1206
- package/src/transcript-parser.ts +0 -458
- package/src/trpc-index.ts +0 -215
- package/src/validate-features-runner.ts +0 -107
- package/src/validation-engine.ts +0 -358
- package/src/watch/daemon.ts +0 -385
- package/src/watch/lockfile-detector.ts +0 -65
- package/src/watch/paths.ts +0 -279
- package/src/watch/state.ts +0 -178
package/src/db.ts
DELETED
|
@@ -1,370 +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, join } from 'path';
|
|
6
|
-
import { existsSync, mkdirSync, readdirSync, statSync } from 'fs';
|
|
7
|
-
import { getResolvedPaths } from './config.ts';
|
|
8
|
-
import { t } from './lib/sql-table-names.ts';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Thrown by `getCodeGraphDb()` when `.codegraph/codegraph.db` is missing.
|
|
12
|
-
*
|
|
13
|
-
* Caught at the JSON-RPC dispatch layer (server.ts) and translated to a
|
|
14
|
-
* structured `-32001` error response carrying a remedy hint and the resolved
|
|
15
|
-
* DB path. The thrown error is INTERNAL; user-facing copy lives in the
|
|
16
|
-
* dispatcher's error envelope.
|
|
17
|
-
*
|
|
18
|
-
* @see `docs/plans/2026-05-10-server-lazy-db-deps.md` P-C-001 + P-A-004
|
|
19
|
-
*/
|
|
20
|
-
export class CodegraphDbNotInitializedError extends Error {
|
|
21
|
-
readonly dbPath: string;
|
|
22
|
-
constructor(dbPath: string) {
|
|
23
|
-
super(`CodeGraph database not found at ${dbPath}`);
|
|
24
|
-
this.name = 'CodegraphDbNotInitializedError';
|
|
25
|
-
this.dbPath = dbPath;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Connection to CodeGraph's read-only SQLite database.
|
|
31
|
-
* We NEVER write to this DB - it belongs to vanilla CodeGraph.
|
|
32
|
-
*
|
|
33
|
-
* Throws `CodegraphDbNotInitializedError` (internal signal) when the DB is
|
|
34
|
-
* missing. The MCP dispatcher catches and translates to a structured
|
|
35
|
-
* JSON-RPC error pointing at `npx @colbymchenry/codegraph init`.
|
|
36
|
-
*/
|
|
37
|
-
export function getCodeGraphDb(): Database.Database {
|
|
38
|
-
const dbPath = getResolvedPaths().codegraphDbPath;
|
|
39
|
-
if (!existsSync(dbPath)) {
|
|
40
|
-
throw new CodegraphDbNotInitializedError(dbPath);
|
|
41
|
-
}
|
|
42
|
-
const db = new Database(dbPath, { readonly: true });
|
|
43
|
-
db.pragma('journal_mode = WAL');
|
|
44
|
-
return db;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Connection to Massu's supplementary SQLite database.
|
|
49
|
-
* This stores import edges, tRPC mappings, domain classifications, etc.
|
|
50
|
-
*/
|
|
51
|
-
export function getDataDb(): Database.Database {
|
|
52
|
-
const dbPath = getResolvedPaths().dataDbPath;
|
|
53
|
-
const dir = dirname(dbPath);
|
|
54
|
-
if (!existsSync(dir)) {
|
|
55
|
-
mkdirSync(dir, { recursive: true });
|
|
56
|
-
}
|
|
57
|
-
const db = new Database(dbPath);
|
|
58
|
-
db.pragma('journal_mode = WAL');
|
|
59
|
-
db.pragma('foreign_keys = ON');
|
|
60
|
-
initDataSchema(db);
|
|
61
|
-
return db;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function initDataSchema(db: Database.Database): void {
|
|
65
|
-
db.exec(`
|
|
66
|
-
CREATE TABLE IF NOT EXISTS ${t('imports')} (
|
|
67
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
68
|
-
source_file TEXT NOT NULL,
|
|
69
|
-
target_file TEXT NOT NULL,
|
|
70
|
-
import_type TEXT NOT NULL CHECK(import_type IN ('named', 'default', 'namespace', 'side_effect', 'dynamic')),
|
|
71
|
-
imported_names TEXT NOT NULL DEFAULT '[]',
|
|
72
|
-
line INTEGER NOT NULL DEFAULT 0
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
CREATE INDEX IF NOT EXISTS idx_massu_imports_source ON ${t('imports')}(source_file);
|
|
76
|
-
CREATE INDEX IF NOT EXISTS idx_massu_imports_target ON ${t('imports')}(target_file);
|
|
77
|
-
|
|
78
|
-
CREATE TABLE IF NOT EXISTS ${t('trpc_procedures')} (
|
|
79
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
80
|
-
router_file TEXT NOT NULL,
|
|
81
|
-
router_name TEXT NOT NULL,
|
|
82
|
-
procedure_name TEXT NOT NULL,
|
|
83
|
-
procedure_type TEXT NOT NULL CHECK(procedure_type IN ('query', 'mutation')),
|
|
84
|
-
has_ui_caller INTEGER NOT NULL DEFAULT 0
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
CREATE INDEX IF NOT EXISTS idx_massu_trpc_router ON ${t('trpc_procedures')}(router_name);
|
|
88
|
-
|
|
89
|
-
CREATE TABLE IF NOT EXISTS ${t('trpc_call_sites')} (
|
|
90
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
91
|
-
procedure_id INTEGER NOT NULL,
|
|
92
|
-
file TEXT NOT NULL,
|
|
93
|
-
line INTEGER NOT NULL DEFAULT 0,
|
|
94
|
-
call_pattern TEXT NOT NULL,
|
|
95
|
-
FOREIGN KEY (procedure_id) REFERENCES ${t('trpc_procedures')}(id)
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
CREATE INDEX IF NOT EXISTS idx_massu_call_sites_proc ON ${t('trpc_call_sites')}(procedure_id);
|
|
99
|
-
|
|
100
|
-
CREATE TABLE IF NOT EXISTS ${t('page_deps')} (
|
|
101
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
102
|
-
page_file TEXT NOT NULL,
|
|
103
|
-
route TEXT NOT NULL,
|
|
104
|
-
portal TEXT NOT NULL DEFAULT 'unknown',
|
|
105
|
-
components TEXT NOT NULL DEFAULT '[]',
|
|
106
|
-
hooks TEXT NOT NULL DEFAULT '[]',
|
|
107
|
-
routers TEXT NOT NULL DEFAULT '[]',
|
|
108
|
-
tables_touched TEXT NOT NULL DEFAULT '[]'
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
CREATE INDEX IF NOT EXISTS idx_massu_page_deps_page ON ${t('page_deps')}(page_file);
|
|
112
|
-
|
|
113
|
-
CREATE TABLE IF NOT EXISTS ${t('middleware_tree')} (
|
|
114
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
115
|
-
file TEXT NOT NULL UNIQUE
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
CREATE TABLE IF NOT EXISTS ${t('meta')} (
|
|
119
|
-
key TEXT PRIMARY KEY,
|
|
120
|
-
value TEXT NOT NULL
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
-- ============================================================
|
|
124
|
-
-- Python Code Intelligence Tables
|
|
125
|
-
-- ============================================================
|
|
126
|
-
|
|
127
|
-
CREATE TABLE IF NOT EXISTS ${t('py_imports')} (
|
|
128
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
129
|
-
source_file TEXT NOT NULL,
|
|
130
|
-
target_file TEXT NOT NULL,
|
|
131
|
-
import_type TEXT NOT NULL CHECK(import_type IN ('absolute', 'relative', 'from_absolute', 'from_relative')),
|
|
132
|
-
imported_names TEXT NOT NULL DEFAULT '[]',
|
|
133
|
-
line INTEGER NOT NULL DEFAULT 0
|
|
134
|
-
);
|
|
135
|
-
CREATE INDEX IF NOT EXISTS idx_massu_py_imports_source ON ${t('py_imports')}(source_file);
|
|
136
|
-
CREATE INDEX IF NOT EXISTS idx_massu_py_imports_target ON ${t('py_imports')}(target_file);
|
|
137
|
-
|
|
138
|
-
CREATE TABLE IF NOT EXISTS ${t('py_meta')} (
|
|
139
|
-
key TEXT PRIMARY KEY,
|
|
140
|
-
value TEXT NOT NULL
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
CREATE TABLE IF NOT EXISTS ${t('py_routes')} (
|
|
144
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
145
|
-
file TEXT NOT NULL,
|
|
146
|
-
method TEXT NOT NULL,
|
|
147
|
-
path TEXT NOT NULL,
|
|
148
|
-
function_name TEXT NOT NULL,
|
|
149
|
-
dependencies TEXT NOT NULL DEFAULT '[]',
|
|
150
|
-
request_model TEXT,
|
|
151
|
-
response_model TEXT,
|
|
152
|
-
is_authenticated INTEGER NOT NULL DEFAULT 0,
|
|
153
|
-
line INTEGER NOT NULL DEFAULT 0
|
|
154
|
-
);
|
|
155
|
-
CREATE INDEX IF NOT EXISTS idx_massu_py_routes_path ON ${t('py_routes')}(path);
|
|
156
|
-
CREATE INDEX IF NOT EXISTS idx_massu_py_routes_file ON ${t('py_routes')}(file);
|
|
157
|
-
|
|
158
|
-
CREATE TABLE IF NOT EXISTS ${t('py_route_callers')} (
|
|
159
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
160
|
-
route_id INTEGER NOT NULL REFERENCES ${t('py_routes')}(id) ON DELETE CASCADE,
|
|
161
|
-
frontend_file TEXT NOT NULL,
|
|
162
|
-
line INTEGER NOT NULL DEFAULT 0,
|
|
163
|
-
call_pattern TEXT NOT NULL
|
|
164
|
-
);
|
|
165
|
-
CREATE INDEX IF NOT EXISTS idx_massu_py_route_callers_route ON ${t('py_route_callers')}(route_id);
|
|
166
|
-
|
|
167
|
-
CREATE TABLE IF NOT EXISTS ${t('py_models')} (
|
|
168
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
169
|
-
class_name TEXT NOT NULL,
|
|
170
|
-
table_name TEXT,
|
|
171
|
-
file TEXT NOT NULL,
|
|
172
|
-
line INTEGER NOT NULL DEFAULT 0,
|
|
173
|
-
columns TEXT NOT NULL DEFAULT '[]',
|
|
174
|
-
relationships TEXT NOT NULL DEFAULT '[]',
|
|
175
|
-
foreign_keys TEXT NOT NULL DEFAULT '[]'
|
|
176
|
-
);
|
|
177
|
-
CREATE INDEX IF NOT EXISTS idx_massu_py_models_file ON ${t('py_models')}(file);
|
|
178
|
-
CREATE INDEX IF NOT EXISTS idx_massu_py_models_table ON ${t('py_models')}(table_name);
|
|
179
|
-
|
|
180
|
-
CREATE TABLE IF NOT EXISTS ${t('py_fk_edges')} (
|
|
181
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
182
|
-
source_table TEXT NOT NULL,
|
|
183
|
-
source_column TEXT NOT NULL,
|
|
184
|
-
target_table TEXT NOT NULL,
|
|
185
|
-
target_column TEXT NOT NULL
|
|
186
|
-
);
|
|
187
|
-
CREATE INDEX IF NOT EXISTS idx_massu_py_fk_source ON ${t('py_fk_edges')}(source_table);
|
|
188
|
-
CREATE INDEX IF NOT EXISTS idx_massu_py_fk_target ON ${t('py_fk_edges')}(target_table);
|
|
189
|
-
|
|
190
|
-
CREATE TABLE IF NOT EXISTS ${t('py_migrations')} (
|
|
191
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
192
|
-
revision TEXT NOT NULL UNIQUE,
|
|
193
|
-
down_revision TEXT,
|
|
194
|
-
file TEXT NOT NULL,
|
|
195
|
-
description TEXT,
|
|
196
|
-
operations TEXT NOT NULL DEFAULT '[]',
|
|
197
|
-
is_head INTEGER NOT NULL DEFAULT 0
|
|
198
|
-
);
|
|
199
|
-
CREATE INDEX IF NOT EXISTS idx_massu_py_migrations_rev ON ${t('py_migrations')}(revision);
|
|
200
|
-
|
|
201
|
-
-- ============================================================
|
|
202
|
-
-- Sentinel: Feature Registry Tables
|
|
203
|
-
-- ============================================================
|
|
204
|
-
|
|
205
|
-
-- Core feature definition
|
|
206
|
-
CREATE TABLE IF NOT EXISTS ${t('sentinel')} (
|
|
207
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
208
|
-
feature_key TEXT UNIQUE NOT NULL,
|
|
209
|
-
domain TEXT NOT NULL,
|
|
210
|
-
subdomain TEXT,
|
|
211
|
-
title TEXT NOT NULL,
|
|
212
|
-
description TEXT,
|
|
213
|
-
status TEXT NOT NULL DEFAULT 'active'
|
|
214
|
-
CHECK(status IN ('planned', 'active', 'deprecated', 'removed')),
|
|
215
|
-
priority TEXT DEFAULT 'standard'
|
|
216
|
-
CHECK(priority IN ('critical', 'standard', 'nice-to-have')),
|
|
217
|
-
portal_scope TEXT DEFAULT '[]',
|
|
218
|
-
created_at TEXT DEFAULT (datetime('now')),
|
|
219
|
-
updated_at TEXT DEFAULT (datetime('now')),
|
|
220
|
-
removed_at TEXT,
|
|
221
|
-
removed_reason TEXT
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
CREATE INDEX IF NOT EXISTS idx_sentinel_domain ON ${t('sentinel')}(domain);
|
|
225
|
-
CREATE INDEX IF NOT EXISTS idx_sentinel_status ON ${t('sentinel')}(status);
|
|
226
|
-
CREATE INDEX IF NOT EXISTS idx_sentinel_key ON ${t('sentinel')}(feature_key);
|
|
227
|
-
|
|
228
|
-
-- Feature-to-component mapping (many-to-many)
|
|
229
|
-
CREATE TABLE IF NOT EXISTS ${t('sentinel_components')} (
|
|
230
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
231
|
-
feature_id INTEGER NOT NULL REFERENCES ${t('sentinel')}(id) ON DELETE CASCADE,
|
|
232
|
-
component_file TEXT NOT NULL,
|
|
233
|
-
component_name TEXT,
|
|
234
|
-
role TEXT DEFAULT 'implementation'
|
|
235
|
-
CHECK(role IN ('implementation', 'ui', 'data', 'utility')),
|
|
236
|
-
is_primary BOOLEAN DEFAULT 0,
|
|
237
|
-
UNIQUE(feature_id, component_file, component_name)
|
|
238
|
-
);
|
|
239
|
-
|
|
240
|
-
-- Feature-to-procedure mapping (many-to-many)
|
|
241
|
-
CREATE TABLE IF NOT EXISTS ${t('sentinel_procedures')} (
|
|
242
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
243
|
-
feature_id INTEGER NOT NULL REFERENCES ${t('sentinel')}(id) ON DELETE CASCADE,
|
|
244
|
-
router_name TEXT NOT NULL,
|
|
245
|
-
procedure_name TEXT NOT NULL,
|
|
246
|
-
procedure_type TEXT,
|
|
247
|
-
UNIQUE(feature_id, router_name, procedure_name)
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
-- Feature-to-page mapping (where feature is accessible)
|
|
251
|
-
CREATE TABLE IF NOT EXISTS ${t('sentinel_pages')} (
|
|
252
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
253
|
-
feature_id INTEGER NOT NULL REFERENCES ${t('sentinel')}(id) ON DELETE CASCADE,
|
|
254
|
-
page_route TEXT NOT NULL,
|
|
255
|
-
portal TEXT,
|
|
256
|
-
UNIQUE(feature_id, page_route, portal)
|
|
257
|
-
);
|
|
258
|
-
|
|
259
|
-
-- Feature dependency graph
|
|
260
|
-
CREATE TABLE IF NOT EXISTS ${t('sentinel_deps')} (
|
|
261
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
262
|
-
feature_id INTEGER NOT NULL REFERENCES ${t('sentinel')}(id) ON DELETE CASCADE,
|
|
263
|
-
depends_on_feature_id INTEGER NOT NULL REFERENCES ${t('sentinel')}(id) ON DELETE CASCADE,
|
|
264
|
-
dependency_type TEXT DEFAULT 'requires'
|
|
265
|
-
CHECK(dependency_type IN ('requires', 'enhances', 'replaces')),
|
|
266
|
-
UNIQUE(feature_id, depends_on_feature_id)
|
|
267
|
-
);
|
|
268
|
-
|
|
269
|
-
-- Feature change log (audit trail)
|
|
270
|
-
CREATE TABLE IF NOT EXISTS ${t('sentinel_changelog')} (
|
|
271
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
272
|
-
feature_id INTEGER NOT NULL REFERENCES ${t('sentinel')}(id) ON DELETE CASCADE,
|
|
273
|
-
change_type TEXT NOT NULL,
|
|
274
|
-
changed_by TEXT,
|
|
275
|
-
change_detail TEXT,
|
|
276
|
-
commit_hash TEXT,
|
|
277
|
-
created_at TEXT DEFAULT (datetime('now'))
|
|
278
|
-
);
|
|
279
|
-
|
|
280
|
-
CREATE INDEX IF NOT EXISTS idx_sentinel_components_file ON ${t('sentinel_components')}(component_file);
|
|
281
|
-
CREATE INDEX IF NOT EXISTS idx_sentinel_procedures_router ON ${t('sentinel_procedures')}(router_name);
|
|
282
|
-
CREATE INDEX IF NOT EXISTS idx_sentinel_pages_route ON ${t('sentinel_pages')}(page_route);
|
|
283
|
-
CREATE INDEX IF NOT EXISTS idx_sentinel_changelog_feature ON ${t('sentinel_changelog')}(feature_id);
|
|
284
|
-
`);
|
|
285
|
-
|
|
286
|
-
// FTS5 for feature search (separate exec since virtual tables can't be in same batch)
|
|
287
|
-
db.exec(`
|
|
288
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS ${t('sentinel_fts')} USING fts5(
|
|
289
|
-
feature_key, title, description, domain, subdomain,
|
|
290
|
-
content=${t('sentinel')}, content_rowid=id
|
|
291
|
-
);
|
|
292
|
-
`);
|
|
293
|
-
|
|
294
|
-
// FTS5 sync triggers
|
|
295
|
-
db.exec(`
|
|
296
|
-
CREATE TRIGGER IF NOT EXISTS massu_sentinel_ai AFTER INSERT ON ${t('sentinel')} BEGIN
|
|
297
|
-
INSERT INTO ${t('sentinel_fts')}(rowid, feature_key, title, description, domain, subdomain)
|
|
298
|
-
VALUES (new.id, new.feature_key, new.title, new.description, new.domain, new.subdomain);
|
|
299
|
-
END;
|
|
300
|
-
|
|
301
|
-
CREATE TRIGGER IF NOT EXISTS massu_sentinel_ad AFTER DELETE ON ${t('sentinel')} BEGIN
|
|
302
|
-
INSERT INTO ${t('sentinel_fts')}(${t('sentinel_fts')}, rowid, feature_key, title, description, domain, subdomain)
|
|
303
|
-
VALUES ('delete', old.id, old.feature_key, old.title, old.description, old.domain, old.subdomain);
|
|
304
|
-
END;
|
|
305
|
-
|
|
306
|
-
CREATE TRIGGER IF NOT EXISTS massu_sentinel_au AFTER UPDATE ON ${t('sentinel')} BEGIN
|
|
307
|
-
INSERT INTO ${t('sentinel_fts')}(${t('sentinel_fts')}, rowid, feature_key, title, description, domain, subdomain)
|
|
308
|
-
VALUES ('delete', old.id, old.feature_key, old.title, old.description, old.domain, old.subdomain);
|
|
309
|
-
INSERT INTO ${t('sentinel_fts')}(rowid, feature_key, title, description, domain, subdomain)
|
|
310
|
-
VALUES (new.id, new.feature_key, new.title, new.description, new.domain, new.subdomain);
|
|
311
|
-
END;
|
|
312
|
-
`);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Check if Massu indexes are stale compared to CodeGraph timestamps.
|
|
317
|
-
*/
|
|
318
|
-
export function isDataStale(dataDb: Database.Database, codegraphDb: Database.Database): boolean {
|
|
319
|
-
const lastBuild = dataDb.prepare(`SELECT value FROM ${t('meta')} WHERE key = 'last_build_time'`).get() as { value: string } | undefined;
|
|
320
|
-
if (!lastBuild) return true;
|
|
321
|
-
|
|
322
|
-
// CodeGraph stores indexed_at as unix timestamp (integer)
|
|
323
|
-
const latestIndexed = codegraphDb.prepare("SELECT MAX(indexed_at) as latest FROM files").get() as { latest: number } | undefined;
|
|
324
|
-
if (!latestIndexed?.latest) return true;
|
|
325
|
-
|
|
326
|
-
// Convert CodeGraph's unix timestamp to ms and compare with our ISO date
|
|
327
|
-
return (latestIndexed.latest * 1000) > new Date(lastBuild.value).getTime();
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Update the last build timestamp in massu_meta.
|
|
332
|
-
*/
|
|
333
|
-
export function updateBuildTimestamp(dataDb: Database.Database): void {
|
|
334
|
-
dataDb.prepare(`INSERT OR REPLACE INTO ${t('meta')} (key, value) VALUES ('last_build_time', ?)`).run(new Date().toISOString());
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Check if Python indexes are stale based on massu_py_meta.last_build_time.
|
|
339
|
-
*/
|
|
340
|
-
export function isPythonDataStale(dataDb: Database.Database, pythonRoot: string): boolean {
|
|
341
|
-
const lastBuild = dataDb.prepare(`SELECT value FROM ${t('py_meta')} WHERE key = 'last_build_time'`).get() as { value: string } | undefined;
|
|
342
|
-
if (!lastBuild) return true;
|
|
343
|
-
|
|
344
|
-
const lastBuildTime = new Date(lastBuild.value).getTime();
|
|
345
|
-
// Check if any .py file is newer than last build
|
|
346
|
-
function checkDir(dir: string): boolean {
|
|
347
|
-
try {
|
|
348
|
-
const entries = readdirSync(dir, { withFileTypes: true });
|
|
349
|
-
for (const entry of entries) {
|
|
350
|
-
const fullPath = join(dir, entry.name);
|
|
351
|
-
if (entry.isDirectory()) {
|
|
352
|
-
if (['__pycache__', '.venv', 'venv', 'node_modules', '.mypy_cache', '.pytest_cache'].includes(entry.name)) continue;
|
|
353
|
-
if (checkDir(fullPath)) return true;
|
|
354
|
-
} else if (entry.name.endsWith('.py')) {
|
|
355
|
-
if (statSync(fullPath).mtimeMs > lastBuildTime) return true;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
} catch { /* directory may not exist */ }
|
|
359
|
-
return false;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
return checkDir(pythonRoot);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Update the Python build timestamp in massu_py_meta.
|
|
367
|
-
*/
|
|
368
|
-
export function updatePythonBuildTimestamp(dataDb: Database.Database): void {
|
|
369
|
-
dataDb.prepare(`INSERT OR REPLACE INTO ${t('py_meta')} (key, value) VALUES ('last_build_time', ?)`).run(new Date().toISOString());
|
|
370
|
-
}
|