@massu/core 0.1.1 → 0.1.2
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/README.md +2 -2
- package/dist/hooks/cost-tracker.js +23 -35
- package/dist/hooks/post-edit-context.js +2 -2
- package/dist/hooks/post-tool-use.js +43 -58
- package/dist/hooks/pre-compact.js +23 -38
- package/dist/hooks/pre-delete-check.js +18 -31
- package/dist/hooks/quality-event.js +23 -35
- package/dist/hooks/session-end.js +62 -78
- package/dist/hooks/session-start.js +33 -42
- package/dist/hooks/user-prompt.js +23 -38
- package/package.json +8 -14
- package/src/adr-generator.ts +9 -2
- package/src/analytics.ts +9 -3
- package/src/audit-trail.ts +10 -3
- package/src/cloud-sync.ts +14 -18
- package/src/commands/init.ts +1 -5
- package/src/cost-tracker.ts +11 -6
- package/src/dependency-scorer.ts +9 -2
- package/src/docs-tools.ts +13 -10
- package/src/hooks/post-edit-context.ts +3 -3
- package/src/hooks/session-end.ts +3 -3
- package/src/hooks/session-start.ts +2 -2
- package/src/memory-db.ts +1351 -23
- package/src/memory-tools.ts +14 -15
- package/src/observability-tools.ts +13 -2
- package/src/prompt-analyzer.ts +9 -2
- package/src/regression-detector.ts +9 -3
- package/src/security-scorer.ts +9 -2
- package/src/sentinel-db.ts +43 -88
- package/src/sentinel-tools.ts +8 -11
- package/src/server.ts +1 -2
- package/src/team-knowledge.ts +9 -2
- package/src/tools.ts +771 -35
- package/src/validate-features-runner.ts +0 -1
- package/src/validation-engine.ts +9 -2
- package/dist/cli.js +0 -7890
- package/dist/server.js +0 -7008
- package/src/__tests__/adr-generator.test.ts +0 -260
- package/src/__tests__/analytics.test.ts +0 -282
- package/src/__tests__/audit-trail.test.ts +0 -382
- package/src/__tests__/backfill-sessions.test.ts +0 -690
- package/src/__tests__/cli.test.ts +0 -290
- package/src/__tests__/cloud-sync.test.ts +0 -261
- package/src/__tests__/config-sections.test.ts +0 -359
- package/src/__tests__/config.test.ts +0 -732
- package/src/__tests__/cost-tracker.test.ts +0 -348
- package/src/__tests__/db.test.ts +0 -177
- package/src/__tests__/dependency-scorer.test.ts +0 -325
- package/src/__tests__/docs-integration.test.ts +0 -178
- package/src/__tests__/docs-tools.test.ts +0 -199
- package/src/__tests__/domains.test.ts +0 -236
- package/src/__tests__/hooks.test.ts +0 -221
- package/src/__tests__/import-resolver.test.ts +0 -95
- package/src/__tests__/integration/path-traversal.test.ts +0 -134
- package/src/__tests__/integration/pricing-consistency.test.ts +0 -88
- package/src/__tests__/integration/tool-registration.test.ts +0 -146
- package/src/__tests__/memory-db.test.ts +0 -404
- package/src/__tests__/memory-enhancements.test.ts +0 -316
- package/src/__tests__/memory-tools.test.ts +0 -199
- package/src/__tests__/middleware-tree.test.ts +0 -177
- package/src/__tests__/observability-tools.test.ts +0 -595
- package/src/__tests__/observability.test.ts +0 -437
- package/src/__tests__/observation-extractor.test.ts +0 -167
- package/src/__tests__/page-deps.test.ts +0 -60
- package/src/__tests__/prompt-analyzer.test.ts +0 -298
- package/src/__tests__/regression-detector.test.ts +0 -295
- package/src/__tests__/rules.test.ts +0 -87
- package/src/__tests__/schema-mapper.test.ts +0 -29
- package/src/__tests__/security-scorer.test.ts +0 -238
- package/src/__tests__/security-utils.test.ts +0 -175
- package/src/__tests__/sentinel-db.test.ts +0 -491
- package/src/__tests__/sentinel-scanner.test.ts +0 -750
- package/src/__tests__/sentinel-tools.test.ts +0 -324
- package/src/__tests__/sentinel-types.test.ts +0 -750
- package/src/__tests__/server.test.ts +0 -452
- package/src/__tests__/session-archiver.test.ts +0 -524
- package/src/__tests__/session-state-generator.test.ts +0 -900
- package/src/__tests__/team-knowledge.test.ts +0 -327
- package/src/__tests__/tools.test.ts +0 -340
- package/src/__tests__/transcript-parser.test.ts +0 -195
- package/src/__tests__/trpc-index.test.ts +0 -25
- package/src/__tests__/validate-features-runner.test.ts +0 -517
- package/src/__tests__/validation-engine.test.ts +0 -300
- package/src/core-tools.ts +0 -685
- package/src/memory-queries.ts +0 -804
- package/src/memory-schema.ts +0 -546
- package/src/tool-helpers.ts +0 -41
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2026 Massu. All rights reserved.
|
|
2
|
-
// Licensed under BSL 1.1 - see LICENSE file for details.
|
|
3
|
-
|
|
4
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
5
|
-
import Database from 'better-sqlite3';
|
|
6
|
-
import { getSentinelToolDefinitions, handleSentinelToolCall } from '../sentinel-tools.ts';
|
|
7
|
-
import { upsertFeature, linkComponent, linkProcedure, linkPage } from '../sentinel-db.ts';
|
|
8
|
-
|
|
9
|
-
// Mock config
|
|
10
|
-
vi.mock('../config.ts', () => ({
|
|
11
|
-
getConfig: () => ({
|
|
12
|
-
toolPrefix: 'massu',
|
|
13
|
-
framework: { type: 'typescript', router: 'trpc', orm: 'prisma' },
|
|
14
|
-
paths: { source: 'src', routers: 'src/server/api/routers' },
|
|
15
|
-
domains: [],
|
|
16
|
-
}),
|
|
17
|
-
getProjectRoot: () => '/test/project',
|
|
18
|
-
}));
|
|
19
|
-
|
|
20
|
-
function createTestDb(): Database.Database {
|
|
21
|
-
const db = new Database(':memory:');
|
|
22
|
-
db.pragma('journal_mode = WAL');
|
|
23
|
-
db.pragma('foreign_keys = ON');
|
|
24
|
-
|
|
25
|
-
// Initialize sentinel schema
|
|
26
|
-
db.exec(`
|
|
27
|
-
CREATE TABLE IF NOT EXISTS massu_sentinel (
|
|
28
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
29
|
-
feature_key TEXT UNIQUE NOT NULL,
|
|
30
|
-
domain TEXT NOT NULL,
|
|
31
|
-
subdomain TEXT,
|
|
32
|
-
title TEXT NOT NULL,
|
|
33
|
-
description TEXT,
|
|
34
|
-
status TEXT NOT NULL DEFAULT 'active'
|
|
35
|
-
CHECK(status IN ('planned', 'active', 'deprecated', 'removed')),
|
|
36
|
-
priority TEXT DEFAULT 'standard'
|
|
37
|
-
CHECK(priority IN ('critical', 'standard', 'nice-to-have')),
|
|
38
|
-
portal_scope TEXT DEFAULT '[]',
|
|
39
|
-
created_at TEXT DEFAULT (datetime('now')),
|
|
40
|
-
updated_at TEXT DEFAULT (datetime('now')),
|
|
41
|
-
removed_at TEXT,
|
|
42
|
-
removed_reason TEXT
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
CREATE TABLE IF NOT EXISTS massu_sentinel_components (
|
|
46
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
47
|
-
feature_id INTEGER NOT NULL REFERENCES massu_sentinel(id) ON DELETE CASCADE,
|
|
48
|
-
component_file TEXT NOT NULL,
|
|
49
|
-
component_name TEXT,
|
|
50
|
-
role TEXT DEFAULT 'implementation'
|
|
51
|
-
CHECK(role IN ('implementation', 'ui', 'data', 'utility')),
|
|
52
|
-
is_primary BOOLEAN DEFAULT 0,
|
|
53
|
-
UNIQUE(feature_id, component_file, component_name)
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
CREATE TABLE IF NOT EXISTS massu_sentinel_procedures (
|
|
57
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
58
|
-
feature_id INTEGER NOT NULL REFERENCES massu_sentinel(id) ON DELETE CASCADE,
|
|
59
|
-
router_name TEXT NOT NULL,
|
|
60
|
-
procedure_name TEXT NOT NULL,
|
|
61
|
-
procedure_type TEXT,
|
|
62
|
-
UNIQUE(feature_id, router_name, procedure_name)
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
CREATE TABLE IF NOT EXISTS massu_sentinel_pages (
|
|
66
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
67
|
-
feature_id INTEGER NOT NULL REFERENCES massu_sentinel(id) ON DELETE CASCADE,
|
|
68
|
-
page_route TEXT NOT NULL,
|
|
69
|
-
portal TEXT,
|
|
70
|
-
UNIQUE(feature_id, page_route, portal)
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
CREATE TABLE IF NOT EXISTS massu_sentinel_deps (
|
|
74
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
75
|
-
feature_id INTEGER NOT NULL REFERENCES massu_sentinel(id) ON DELETE CASCADE,
|
|
76
|
-
depends_on_feature_id INTEGER NOT NULL REFERENCES massu_sentinel(id) ON DELETE CASCADE,
|
|
77
|
-
dependency_type TEXT DEFAULT 'requires'
|
|
78
|
-
CHECK(dependency_type IN ('requires', 'enhances', 'replaces')),
|
|
79
|
-
UNIQUE(feature_id, depends_on_feature_id)
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
CREATE TABLE IF NOT EXISTS massu_sentinel_changelog (
|
|
83
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
84
|
-
feature_id INTEGER NOT NULL REFERENCES massu_sentinel(id) ON DELETE CASCADE,
|
|
85
|
-
change_type TEXT NOT NULL,
|
|
86
|
-
changed_by TEXT,
|
|
87
|
-
change_detail TEXT,
|
|
88
|
-
commit_hash TEXT,
|
|
89
|
-
created_at TEXT DEFAULT (datetime('now'))
|
|
90
|
-
);
|
|
91
|
-
`);
|
|
92
|
-
|
|
93
|
-
db.exec(`
|
|
94
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS massu_sentinel_fts USING fts5(
|
|
95
|
-
feature_key, title, description, domain, subdomain,
|
|
96
|
-
content=massu_sentinel, content_rowid=id
|
|
97
|
-
);
|
|
98
|
-
`);
|
|
99
|
-
|
|
100
|
-
db.exec(`
|
|
101
|
-
CREATE TRIGGER IF NOT EXISTS massu_sentinel_ai AFTER INSERT ON massu_sentinel BEGIN
|
|
102
|
-
INSERT INTO massu_sentinel_fts(rowid, feature_key, title, description, domain, subdomain)
|
|
103
|
-
VALUES (new.id, new.feature_key, new.title, new.description, new.domain, new.subdomain);
|
|
104
|
-
END;
|
|
105
|
-
|
|
106
|
-
CREATE TRIGGER IF NOT EXISTS massu_sentinel_ad AFTER DELETE ON massu_sentinel BEGIN
|
|
107
|
-
INSERT INTO massu_sentinel_fts(massu_sentinel_fts, rowid, feature_key, title, description, domain, subdomain)
|
|
108
|
-
VALUES ('delete', old.id, old.feature_key, old.title, old.description, old.domain, old.subdomain);
|
|
109
|
-
END;
|
|
110
|
-
|
|
111
|
-
CREATE TRIGGER IF NOT EXISTS massu_sentinel_au AFTER UPDATE ON massu_sentinel BEGIN
|
|
112
|
-
INSERT INTO massu_sentinel_fts(massu_sentinel_fts, rowid, feature_key, title, description, domain, subdomain)
|
|
113
|
-
VALUES ('delete', old.id, old.feature_key, old.title, old.description, old.domain, old.subdomain);
|
|
114
|
-
INSERT INTO massu_sentinel_fts(rowid, feature_key, title, description, domain, subdomain)
|
|
115
|
-
VALUES (new.id, new.feature_key, new.title, new.description, new.domain, new.subdomain);
|
|
116
|
-
END;
|
|
117
|
-
`);
|
|
118
|
-
|
|
119
|
-
return db;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
describe('Sentinel Tools', () => {
|
|
123
|
-
let db: Database.Database;
|
|
124
|
-
|
|
125
|
-
beforeEach(() => {
|
|
126
|
-
db = createTestDb();
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
afterEach(() => {
|
|
130
|
-
db.close();
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
describe('getSentinelToolDefinitions', () => {
|
|
134
|
-
it('returns tool definitions array', () => {
|
|
135
|
-
const tools = getSentinelToolDefinitions();
|
|
136
|
-
expect(Array.isArray(tools)).toBe(true);
|
|
137
|
-
expect(tools.length).toBeGreaterThan(0);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('includes sentinel_search tool', () => {
|
|
141
|
-
const tools = getSentinelToolDefinitions();
|
|
142
|
-
const searchTool = tools.find(t => t.name === 'massu_sentinel_search');
|
|
143
|
-
expect(searchTool).toBeTruthy();
|
|
144
|
-
expect(searchTool?.description).toBeTruthy();
|
|
145
|
-
expect(searchTool?.inputSchema).toBeTruthy();
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('includes all sentinel tools', () => {
|
|
149
|
-
const tools = getSentinelToolDefinitions();
|
|
150
|
-
const toolNames = tools.map(t => t.name);
|
|
151
|
-
expect(toolNames).toContain('massu_sentinel_search');
|
|
152
|
-
expect(toolNames).toContain('massu_sentinel_detail');
|
|
153
|
-
expect(toolNames).toContain('massu_sentinel_impact');
|
|
154
|
-
expect(toolNames).toContain('massu_sentinel_validate');
|
|
155
|
-
expect(toolNames).toContain('massu_sentinel_register');
|
|
156
|
-
expect(toolNames).toContain('massu_sentinel_parity');
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
describe('handleSentinelToolCall - sentinel_search', () => {
|
|
161
|
-
beforeEach(() => {
|
|
162
|
-
upsertFeature(db, {
|
|
163
|
-
feature_key: 'auth.login',
|
|
164
|
-
domain: 'auth',
|
|
165
|
-
title: 'User Login',
|
|
166
|
-
description: 'Login feature',
|
|
167
|
-
status: 'active',
|
|
168
|
-
});
|
|
169
|
-
upsertFeature(db, {
|
|
170
|
-
feature_key: 'auth.register',
|
|
171
|
-
domain: 'auth',
|
|
172
|
-
title: 'User Registration',
|
|
173
|
-
status: 'active',
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('searches features with query', () => {
|
|
178
|
-
const result = handleSentinelToolCall('massu_sentinel_search', { query: 'login' }, db);
|
|
179
|
-
expect(result.content[0].type).toBe('text');
|
|
180
|
-
expect(result.content[0].text).toContain('auth.login');
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('filters by domain', () => {
|
|
184
|
-
const result = handleSentinelToolCall('massu_sentinel_search', { domain: 'auth' }, db);
|
|
185
|
-
expect(result.content[0].text).toContain('auth.login');
|
|
186
|
-
expect(result.content[0].text).toContain('auth.register');
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it('returns empty result for no matches', () => {
|
|
190
|
-
const result = handleSentinelToolCall('massu_sentinel_search', { query: 'nonexistent' }, db);
|
|
191
|
-
expect(result.content[0].text).toContain('No features found');
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
describe('handleSentinelToolCall - sentinel_detail', () => {
|
|
196
|
-
it('retrieves feature detail by key', () => {
|
|
197
|
-
const featureId = upsertFeature(db, {
|
|
198
|
-
feature_key: 'test.feature',
|
|
199
|
-
domain: 'test',
|
|
200
|
-
title: 'Test Feature',
|
|
201
|
-
});
|
|
202
|
-
linkComponent(db, featureId, 'src/test.ts', 'TestComponent', 'ui', true);
|
|
203
|
-
|
|
204
|
-
const result = handleSentinelToolCall('massu_sentinel_detail', { feature_key: 'test.feature' }, db);
|
|
205
|
-
expect(result.content[0].text).toContain('Test Feature');
|
|
206
|
-
expect(result.content[0].text).toContain('src/test.ts');
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it('retrieves feature detail by id', () => {
|
|
210
|
-
const featureId = upsertFeature(db, {
|
|
211
|
-
feature_key: 'test.feature',
|
|
212
|
-
domain: 'test',
|
|
213
|
-
title: 'Test Feature',
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
const result = handleSentinelToolCall('massu_sentinel_detail', { feature_id: featureId }, db);
|
|
217
|
-
expect(result.content[0].text).toContain('Test Feature');
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
it('returns error for missing feature', () => {
|
|
221
|
-
const result = handleSentinelToolCall('massu_sentinel_detail', { feature_key: 'nonexistent' }, db);
|
|
222
|
-
expect(result.content[0].text).toContain('not found');
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
describe('handleSentinelToolCall - sentinel_impact', () => {
|
|
227
|
-
it('analyzes file deletion impact', () => {
|
|
228
|
-
const featureId = upsertFeature(db, {
|
|
229
|
-
feature_key: 'test.feature',
|
|
230
|
-
domain: 'test',
|
|
231
|
-
title: 'Test Feature',
|
|
232
|
-
status: 'active',
|
|
233
|
-
priority: 'critical',
|
|
234
|
-
});
|
|
235
|
-
linkComponent(db, featureId, 'src/test.ts', 'TestComponent', 'implementation', true);
|
|
236
|
-
|
|
237
|
-
const result = handleSentinelToolCall('massu_sentinel_impact', { files: ['src/test.ts'] }, db);
|
|
238
|
-
expect(result.content[0].text).toContain('Impact Analysis');
|
|
239
|
-
expect(result.content[0].text).toContain('Orphaned');
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it('returns error for missing files argument', () => {
|
|
243
|
-
const result = handleSentinelToolCall('massu_sentinel_impact', {}, db);
|
|
244
|
-
expect(result.content[0].text).toContain('Error');
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
describe('handleSentinelToolCall - sentinel_validate', () => {
|
|
249
|
-
it('validates all features', () => {
|
|
250
|
-
upsertFeature(db, {
|
|
251
|
-
feature_key: 'test.feature',
|
|
252
|
-
domain: 'test',
|
|
253
|
-
title: 'Test Feature',
|
|
254
|
-
status: 'active',
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
const result = handleSentinelToolCall('massu_sentinel_validate', {}, db);
|
|
258
|
-
expect(result.content[0].text).toContain('Validation Report');
|
|
259
|
-
expect(result.content[0].text).toContain('Summary');
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
it('filters by domain', () => {
|
|
263
|
-
upsertFeature(db, {
|
|
264
|
-
feature_key: 'test.feature',
|
|
265
|
-
domain: 'test',
|
|
266
|
-
title: 'Test Feature',
|
|
267
|
-
status: 'active',
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
const result = handleSentinelToolCall('massu_sentinel_validate', { domain: 'test' }, db);
|
|
271
|
-
expect(result.content[0].text).toContain('Domain filter: test');
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
describe('handleSentinelToolCall - sentinel_register', () => {
|
|
276
|
-
it('registers a new feature', () => {
|
|
277
|
-
const result = handleSentinelToolCall('massu_sentinel_register', {
|
|
278
|
-
feature_key: 'new.feature',
|
|
279
|
-
domain: 'test',
|
|
280
|
-
title: 'New Feature',
|
|
281
|
-
components: [{ file: 'src/new.ts', name: 'NewComponent', is_primary: true }],
|
|
282
|
-
}, db);
|
|
283
|
-
expect(result.content[0].text).toContain('Feature registered');
|
|
284
|
-
expect(result.content[0].text).toContain('new.feature');
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it('requires feature_key, domain, and title', () => {
|
|
288
|
-
const result = handleSentinelToolCall('massu_sentinel_register', {}, db);
|
|
289
|
-
expect(result.content[0].text).toContain('Error');
|
|
290
|
-
expect(result.content[0].text).toContain('required');
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
describe('handleSentinelToolCall - sentinel_parity', () => {
|
|
295
|
-
it('compares old and new file sets', () => {
|
|
296
|
-
const featureId = upsertFeature(db, {
|
|
297
|
-
feature_key: 'test.feature',
|
|
298
|
-
domain: 'test',
|
|
299
|
-
title: 'Test Feature',
|
|
300
|
-
});
|
|
301
|
-
linkComponent(db, featureId, 'src/old.ts', null, 'implementation', true);
|
|
302
|
-
linkComponent(db, featureId, 'src/new.ts', null, 'implementation', true);
|
|
303
|
-
|
|
304
|
-
const result = handleSentinelToolCall('massu_sentinel_parity', {
|
|
305
|
-
old_files: ['src/old.ts'],
|
|
306
|
-
new_files: ['src/new.ts'],
|
|
307
|
-
}, db);
|
|
308
|
-
expect(result.content[0].text).toContain('Parity Report');
|
|
309
|
-
expect(result.content[0].text).toContain('Parity:');
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
it('returns error for missing arguments', () => {
|
|
313
|
-
const result = handleSentinelToolCall('massu_sentinel_parity', {}, db);
|
|
314
|
-
expect(result.content[0].text).toContain('Error');
|
|
315
|
-
});
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
describe('handleSentinelToolCall - unknown tool', () => {
|
|
319
|
-
it('returns error for unknown tool', () => {
|
|
320
|
-
const result = handleSentinelToolCall('massu_sentinel_unknown', {}, db);
|
|
321
|
-
expect(result.content[0].text).toContain('Unknown');
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
});
|