@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
|
@@ -3,7 +3,7 @@ import{createRequire as __cr}from"module";const require=__cr(import.meta.url);
|
|
|
3
3
|
|
|
4
4
|
// src/memory-db.ts
|
|
5
5
|
import Database from "better-sqlite3";
|
|
6
|
-
import { dirname as dirname2 } from "path";
|
|
6
|
+
import { dirname as dirname2, basename } from "path";
|
|
7
7
|
import { existsSync as existsSync2, mkdirSync } from "fs";
|
|
8
8
|
|
|
9
9
|
// src/config.ts
|
|
@@ -268,14 +268,26 @@ function getResolvedPaths() {
|
|
|
268
268
|
};
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
-
// src/memory-
|
|
271
|
+
// src/memory-db.ts
|
|
272
|
+
function getMemoryDb() {
|
|
273
|
+
const dbPath = getResolvedPaths().memoryDbPath;
|
|
274
|
+
const dir = dirname2(dbPath);
|
|
275
|
+
if (!existsSync2(dir)) {
|
|
276
|
+
mkdirSync(dir, { recursive: true });
|
|
277
|
+
}
|
|
278
|
+
const db = new Database(dbPath);
|
|
279
|
+
db.pragma("journal_mode = WAL");
|
|
280
|
+
db.pragma("foreign_keys = ON");
|
|
281
|
+
initMemorySchema(db);
|
|
282
|
+
return db;
|
|
283
|
+
}
|
|
272
284
|
function initMemorySchema(db) {
|
|
273
285
|
db.exec(`
|
|
274
286
|
-- Sessions table (linked to Claude Code session IDs)
|
|
275
287
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
276
288
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
277
289
|
session_id TEXT UNIQUE NOT NULL,
|
|
278
|
-
project TEXT NOT NULL DEFAULT '
|
|
290
|
+
project TEXT NOT NULL DEFAULT 'my-project',
|
|
279
291
|
git_branch TEXT,
|
|
280
292
|
started_at TEXT NOT NULL,
|
|
281
293
|
started_at_epoch INTEGER NOT NULL,
|
|
@@ -330,9 +342,7 @@ function initMemorySchema(db) {
|
|
|
330
342
|
content_rowid='id'
|
|
331
343
|
);
|
|
332
344
|
`);
|
|
333
|
-
} catch (
|
|
334
|
-
process.stderr.write(`FTS5 setup warning: ${e instanceof Error ? e.message : String(e)}
|
|
335
|
-
`);
|
|
345
|
+
} catch (_e) {
|
|
336
346
|
}
|
|
337
347
|
db.exec(`
|
|
338
348
|
CREATE TRIGGER IF NOT EXISTS observations_ai AFTER INSERT ON observations BEGIN
|
|
@@ -392,9 +402,7 @@ function initMemorySchema(db) {
|
|
|
392
402
|
content_rowid='id'
|
|
393
403
|
);
|
|
394
404
|
`);
|
|
395
|
-
} catch (
|
|
396
|
-
process.stderr.write(`FTS5 setup warning: ${e instanceof Error ? e.message : String(e)}
|
|
397
|
-
`);
|
|
405
|
+
} catch (_e) {
|
|
398
406
|
}
|
|
399
407
|
db.exec(`
|
|
400
408
|
CREATE TRIGGER IF NOT EXISTS prompts_ai AFTER INSERT ON user_prompts BEGIN
|
|
@@ -464,9 +472,7 @@ function initMemorySchema(db) {
|
|
|
464
472
|
content_rowid=id
|
|
465
473
|
);
|
|
466
474
|
`);
|
|
467
|
-
} catch (
|
|
468
|
-
process.stderr.write(`FTS5 setup warning: ${e instanceof Error ? e.message : String(e)}
|
|
469
|
-
`);
|
|
475
|
+
} catch (_e) {
|
|
470
476
|
}
|
|
471
477
|
db.exec(`
|
|
472
478
|
CREATE TRIGGER IF NOT EXISTS ct_fts_insert AFTER INSERT ON conversation_turns BEGIN
|
|
@@ -490,7 +496,7 @@ function initMemorySchema(db) {
|
|
|
490
496
|
CREATE TABLE IF NOT EXISTS session_quality_scores (
|
|
491
497
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
492
498
|
session_id TEXT NOT NULL UNIQUE,
|
|
493
|
-
project TEXT NOT NULL DEFAULT '
|
|
499
|
+
project TEXT NOT NULL DEFAULT 'my-project',
|
|
494
500
|
score INTEGER NOT NULL DEFAULT 100,
|
|
495
501
|
security_score INTEGER NOT NULL DEFAULT 100,
|
|
496
502
|
architecture_score INTEGER NOT NULL DEFAULT 100,
|
|
@@ -513,7 +519,7 @@ function initMemorySchema(db) {
|
|
|
513
519
|
CREATE TABLE IF NOT EXISTS session_costs (
|
|
514
520
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
515
521
|
session_id TEXT NOT NULL UNIQUE,
|
|
516
|
-
project TEXT NOT NULL DEFAULT '
|
|
522
|
+
project TEXT NOT NULL DEFAULT 'my-project',
|
|
517
523
|
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
518
524
|
output_tokens INTEGER NOT NULL DEFAULT 0,
|
|
519
525
|
cache_read_tokens INTEGER NOT NULL DEFAULT 0,
|
|
@@ -742,24 +748,6 @@ function initMemorySchema(db) {
|
|
|
742
748
|
`);
|
|
743
749
|
}
|
|
744
750
|
|
|
745
|
-
// src/memory-db.ts
|
|
746
|
-
var initializedPaths = /* @__PURE__ */ new Set();
|
|
747
|
-
function getMemoryDb() {
|
|
748
|
-
const dbPath = getResolvedPaths().memoryDbPath;
|
|
749
|
-
const dir = dirname2(dbPath);
|
|
750
|
-
if (!existsSync2(dir)) {
|
|
751
|
-
mkdirSync(dir, { recursive: true });
|
|
752
|
-
}
|
|
753
|
-
const db = new Database(dbPath);
|
|
754
|
-
db.pragma("journal_mode = WAL");
|
|
755
|
-
db.pragma("foreign_keys = ON");
|
|
756
|
-
if (!initializedPaths.has(dbPath)) {
|
|
757
|
-
initMemorySchema(db);
|
|
758
|
-
initializedPaths.add(dbPath);
|
|
759
|
-
}
|
|
760
|
-
return db;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
751
|
// src/hooks/quality-event.ts
|
|
764
752
|
var TEST_FAILURE_PATTERNS = [
|
|
765
753
|
/\bFAIL\b/,
|
|
@@ -838,14 +826,14 @@ async function main() {
|
|
|
838
826
|
process.exit(0);
|
|
839
827
|
}
|
|
840
828
|
function readStdin() {
|
|
841
|
-
return new Promise((
|
|
829
|
+
return new Promise((resolve3) => {
|
|
842
830
|
let data = "";
|
|
843
831
|
process.stdin.setEncoding("utf-8");
|
|
844
832
|
process.stdin.on("data", (chunk) => {
|
|
845
833
|
data += chunk;
|
|
846
834
|
});
|
|
847
|
-
process.stdin.on("end", () =>
|
|
848
|
-
setTimeout(() =>
|
|
835
|
+
process.stdin.on("end", () => resolve3(data));
|
|
836
|
+
setTimeout(() => resolve3(data), 3e3);
|
|
849
837
|
});
|
|
850
838
|
}
|
|
851
839
|
main();
|
|
@@ -3,7 +3,7 @@ import{createRequire as __cr}from"module";const require=__cr(import.meta.url);
|
|
|
3
3
|
|
|
4
4
|
// src/memory-db.ts
|
|
5
5
|
import Database from "better-sqlite3";
|
|
6
|
-
import { dirname as dirname2 } from "path";
|
|
6
|
+
import { dirname as dirname2, basename } from "path";
|
|
7
7
|
import { existsSync as existsSync2, mkdirSync } from "fs";
|
|
8
8
|
|
|
9
9
|
// src/config.ts
|
|
@@ -268,14 +268,26 @@ function getResolvedPaths() {
|
|
|
268
268
|
};
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
-
// src/memory-
|
|
271
|
+
// src/memory-db.ts
|
|
272
|
+
function getMemoryDb() {
|
|
273
|
+
const dbPath = getResolvedPaths().memoryDbPath;
|
|
274
|
+
const dir = dirname2(dbPath);
|
|
275
|
+
if (!existsSync2(dir)) {
|
|
276
|
+
mkdirSync(dir, { recursive: true });
|
|
277
|
+
}
|
|
278
|
+
const db = new Database(dbPath);
|
|
279
|
+
db.pragma("journal_mode = WAL");
|
|
280
|
+
db.pragma("foreign_keys = ON");
|
|
281
|
+
initMemorySchema(db);
|
|
282
|
+
return db;
|
|
283
|
+
}
|
|
272
284
|
function initMemorySchema(db) {
|
|
273
285
|
db.exec(`
|
|
274
286
|
-- Sessions table (linked to Claude Code session IDs)
|
|
275
287
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
276
288
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
277
289
|
session_id TEXT UNIQUE NOT NULL,
|
|
278
|
-
project TEXT NOT NULL DEFAULT '
|
|
290
|
+
project TEXT NOT NULL DEFAULT 'my-project',
|
|
279
291
|
git_branch TEXT,
|
|
280
292
|
started_at TEXT NOT NULL,
|
|
281
293
|
started_at_epoch INTEGER NOT NULL,
|
|
@@ -330,9 +342,7 @@ function initMemorySchema(db) {
|
|
|
330
342
|
content_rowid='id'
|
|
331
343
|
);
|
|
332
344
|
`);
|
|
333
|
-
} catch (
|
|
334
|
-
process.stderr.write(`FTS5 setup warning: ${e instanceof Error ? e.message : String(e)}
|
|
335
|
-
`);
|
|
345
|
+
} catch (_e) {
|
|
336
346
|
}
|
|
337
347
|
db.exec(`
|
|
338
348
|
CREATE TRIGGER IF NOT EXISTS observations_ai AFTER INSERT ON observations BEGIN
|
|
@@ -392,9 +402,7 @@ function initMemorySchema(db) {
|
|
|
392
402
|
content_rowid='id'
|
|
393
403
|
);
|
|
394
404
|
`);
|
|
395
|
-
} catch (
|
|
396
|
-
process.stderr.write(`FTS5 setup warning: ${e instanceof Error ? e.message : String(e)}
|
|
397
|
-
`);
|
|
405
|
+
} catch (_e) {
|
|
398
406
|
}
|
|
399
407
|
db.exec(`
|
|
400
408
|
CREATE TRIGGER IF NOT EXISTS prompts_ai AFTER INSERT ON user_prompts BEGIN
|
|
@@ -464,9 +472,7 @@ function initMemorySchema(db) {
|
|
|
464
472
|
content_rowid=id
|
|
465
473
|
);
|
|
466
474
|
`);
|
|
467
|
-
} catch (
|
|
468
|
-
process.stderr.write(`FTS5 setup warning: ${e instanceof Error ? e.message : String(e)}
|
|
469
|
-
`);
|
|
475
|
+
} catch (_e) {
|
|
470
476
|
}
|
|
471
477
|
db.exec(`
|
|
472
478
|
CREATE TRIGGER IF NOT EXISTS ct_fts_insert AFTER INSERT ON conversation_turns BEGIN
|
|
@@ -490,7 +496,7 @@ function initMemorySchema(db) {
|
|
|
490
496
|
CREATE TABLE IF NOT EXISTS session_quality_scores (
|
|
491
497
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
492
498
|
session_id TEXT NOT NULL UNIQUE,
|
|
493
|
-
project TEXT NOT NULL DEFAULT '
|
|
499
|
+
project TEXT NOT NULL DEFAULT 'my-project',
|
|
494
500
|
score INTEGER NOT NULL DEFAULT 100,
|
|
495
501
|
security_score INTEGER NOT NULL DEFAULT 100,
|
|
496
502
|
architecture_score INTEGER NOT NULL DEFAULT 100,
|
|
@@ -513,7 +519,7 @@ function initMemorySchema(db) {
|
|
|
513
519
|
CREATE TABLE IF NOT EXISTS session_costs (
|
|
514
520
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
515
521
|
session_id TEXT NOT NULL UNIQUE,
|
|
516
|
-
project TEXT NOT NULL DEFAULT '
|
|
522
|
+
project TEXT NOT NULL DEFAULT 'my-project',
|
|
517
523
|
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
518
524
|
output_tokens INTEGER NOT NULL DEFAULT 0,
|
|
519
525
|
cache_read_tokens INTEGER NOT NULL DEFAULT 0,
|
|
@@ -741,14 +747,17 @@ function initMemorySchema(db) {
|
|
|
741
747
|
CREATE INDEX IF NOT EXISTS idx_pending_sync_created ON pending_sync(created_at ASC);
|
|
742
748
|
`);
|
|
743
749
|
}
|
|
744
|
-
|
|
745
|
-
// src/memory-queries.ts
|
|
746
|
-
import { basename } from "path";
|
|
747
750
|
function enqueueSyncPayload(db, payload) {
|
|
748
751
|
db.prepare("INSERT INTO pending_sync (payload) VALUES (?)").run(payload);
|
|
749
752
|
}
|
|
750
753
|
function dequeuePendingSync(db, limit = 10) {
|
|
751
|
-
|
|
754
|
+
const stale = db.prepare(
|
|
755
|
+
"SELECT id FROM pending_sync WHERE retry_count >= 10"
|
|
756
|
+
).all();
|
|
757
|
+
if (stale.length > 0) {
|
|
758
|
+
const ids = stale.map((s) => s.id);
|
|
759
|
+
db.prepare(`DELETE FROM pending_sync WHERE id IN (${ids.map(() => "?").join(",")})`).run(...ids);
|
|
760
|
+
}
|
|
752
761
|
return db.prepare(
|
|
753
762
|
"SELECT id, payload, retry_count FROM pending_sync ORDER BY created_at ASC LIMIT ?"
|
|
754
763
|
).all(limit);
|
|
@@ -840,27 +849,9 @@ function setLastProcessedLine(db, sessionId, lineNumber) {
|
|
|
840
849
|
db.prepare("INSERT OR REPLACE INTO memory_meta (key, value) VALUES (?, ?)").run(`last_processed_line:${sessionId}`, String(lineNumber));
|
|
841
850
|
}
|
|
842
851
|
|
|
843
|
-
// src/memory-db.ts
|
|
844
|
-
var initializedPaths = /* @__PURE__ */ new Set();
|
|
845
|
-
function getMemoryDb() {
|
|
846
|
-
const dbPath = getResolvedPaths().memoryDbPath;
|
|
847
|
-
const dir = dirname2(dbPath);
|
|
848
|
-
if (!existsSync2(dir)) {
|
|
849
|
-
mkdirSync(dir, { recursive: true });
|
|
850
|
-
}
|
|
851
|
-
const db = new Database(dbPath);
|
|
852
|
-
db.pragma("journal_mode = WAL");
|
|
853
|
-
db.pragma("foreign_keys = ON");
|
|
854
|
-
if (!initializedPaths.has(dbPath)) {
|
|
855
|
-
initMemorySchema(db);
|
|
856
|
-
initializedPaths.add(dbPath);
|
|
857
|
-
}
|
|
858
|
-
return db;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
852
|
// src/session-archiver.ts
|
|
862
853
|
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync, mkdirSync as mkdirSync2, renameSync } from "fs";
|
|
863
|
-
import { resolve as
|
|
854
|
+
import { resolve as resolve3, dirname as dirname3 } from "path";
|
|
864
855
|
|
|
865
856
|
// src/session-state-generator.ts
|
|
866
857
|
function generateCurrentMd(db, sessionId) {
|
|
@@ -1007,15 +998,15 @@ function safeParseJson(json, fallback) {
|
|
|
1007
998
|
// src/session-archiver.ts
|
|
1008
999
|
var PROJECT_ROOT = getProjectRoot();
|
|
1009
1000
|
function archiveAndRegenerate(db, sessionId) {
|
|
1010
|
-
const currentMdPath =
|
|
1011
|
-
const archiveDir =
|
|
1001
|
+
const currentMdPath = resolve3(PROJECT_ROOT, ".claude/session-state/CURRENT.md");
|
|
1002
|
+
const archiveDir = resolve3(PROJECT_ROOT, ".claude/session-state/archive");
|
|
1012
1003
|
let archived = false;
|
|
1013
1004
|
let archivePath;
|
|
1014
1005
|
if (existsSync3(currentMdPath)) {
|
|
1015
1006
|
const existingContent = readFileSync2(currentMdPath, "utf-8");
|
|
1016
1007
|
if (existingContent.trim().length > 10) {
|
|
1017
1008
|
const { date, slug } = extractArchiveInfo(existingContent);
|
|
1018
|
-
archivePath =
|
|
1009
|
+
archivePath = resolve3(archiveDir, `${date}-${slug}.md`);
|
|
1019
1010
|
if (!existsSync3(archiveDir)) {
|
|
1020
1011
|
mkdirSync2(archiveDir, { recursive: true });
|
|
1021
1012
|
}
|
|
@@ -1129,8 +1120,8 @@ async function parseTranscriptFrom(filePath, startLine) {
|
|
|
1129
1120
|
}
|
|
1130
1121
|
return { entries, totalLines: lineNumber };
|
|
1131
1122
|
}
|
|
1132
|
-
function estimateTokens(
|
|
1133
|
-
return Math.ceil(
|
|
1123
|
+
function estimateTokens(text) {
|
|
1124
|
+
return Math.ceil(text.length / 4);
|
|
1134
1125
|
}
|
|
1135
1126
|
|
|
1136
1127
|
// src/cloud-sync.ts
|
|
@@ -1211,30 +1202,25 @@ async function syncToCloud(db, payload) {
|
|
|
1211
1202
|
};
|
|
1212
1203
|
}
|
|
1213
1204
|
async function drainSyncQueue(db) {
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
incrementRetryCount(db, item.id, result.error ?? "Unknown error");
|
|
1226
|
-
}
|
|
1227
|
-
} catch (err) {
|
|
1228
|
-
incrementRetryCount(db, item.id, err instanceof Error ? err.message : String(err));
|
|
1205
|
+
const config = getConfig();
|
|
1206
|
+
if (!config.cloud?.enabled || !config.cloud?.apiKey) return;
|
|
1207
|
+
const pending = dequeuePendingSync(db, 10);
|
|
1208
|
+
for (const item of pending) {
|
|
1209
|
+
try {
|
|
1210
|
+
const payload = JSON.parse(item.payload);
|
|
1211
|
+
const result = await syncToCloud(db, payload);
|
|
1212
|
+
if (result.success) {
|
|
1213
|
+
removePendingSync(db, item.id);
|
|
1214
|
+
} else {
|
|
1215
|
+
incrementRetryCount(db, item.id, result.error ?? "Unknown error");
|
|
1229
1216
|
}
|
|
1217
|
+
} catch (err) {
|
|
1218
|
+
incrementRetryCount(db, item.id, err instanceof Error ? err.message : String(err));
|
|
1230
1219
|
}
|
|
1231
|
-
} catch (err) {
|
|
1232
|
-
process.stderr.write(`massu: drainSyncQueue failed: ${err instanceof Error ? err.message : String(err)}
|
|
1233
|
-
`);
|
|
1234
1220
|
}
|
|
1235
1221
|
}
|
|
1236
1222
|
function sleep(ms) {
|
|
1237
|
-
return new Promise((
|
|
1223
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
1238
1224
|
}
|
|
1239
1225
|
|
|
1240
1226
|
// src/analytics.ts
|
|
@@ -1300,7 +1286,6 @@ function backfillQualityScores(db) {
|
|
|
1300
1286
|
FROM sessions s
|
|
1301
1287
|
LEFT JOIN session_quality_scores q ON s.session_id = q.session_id
|
|
1302
1288
|
WHERE q.session_id IS NULL
|
|
1303
|
-
LIMIT 1000
|
|
1304
1289
|
`).all();
|
|
1305
1290
|
let backfilled = 0;
|
|
1306
1291
|
for (const session of sessions) {
|
|
@@ -1313,11 +1298,10 @@ function backfillQualityScores(db) {
|
|
|
1313
1298
|
|
|
1314
1299
|
// src/cost-tracker.ts
|
|
1315
1300
|
var DEFAULT_MODEL_PRICING = {
|
|
1316
|
-
"claude-opus-4-6": { input_per_million:
|
|
1301
|
+
"claude-opus-4-6": { input_per_million: 15, output_per_million: 75, cache_read_per_million: 1.5, cache_write_per_million: 18.75 },
|
|
1317
1302
|
"claude-sonnet-4-6": { input_per_million: 3, output_per_million: 15, cache_read_per_million: 0.3, cache_write_per_million: 3.75 },
|
|
1318
1303
|
"claude-sonnet-4-5": { input_per_million: 3, output_per_million: 15, cache_read_per_million: 0.3, cache_write_per_million: 3.75 },
|
|
1319
|
-
"claude-
|
|
1320
|
-
"claude-haiku-4-5-20251001": { input_per_million: 1, output_per_million: 5, cache_read_per_million: 0.1, cache_write_per_million: 1.25 },
|
|
1304
|
+
"claude-haiku-4-5-20251001": { input_per_million: 0.8, output_per_million: 4, cache_read_per_million: 0.08, cache_write_per_million: 1 },
|
|
1321
1305
|
"default": { input_per_million: 3, output_per_million: 15, cache_read_per_million: 0.3, cache_write_per_million: 3.75 }
|
|
1322
1306
|
};
|
|
1323
1307
|
function getModelPricing() {
|
|
@@ -1389,8 +1373,8 @@ import { createHash } from "crypto";
|
|
|
1389
1373
|
function escapeRegex(str) {
|
|
1390
1374
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1391
1375
|
}
|
|
1392
|
-
function redactSensitiveContent(
|
|
1393
|
-
return
|
|
1376
|
+
function redactSensitiveContent(text) {
|
|
1377
|
+
return text.replace(/\b(sk-|ghp_|gho_|xoxb-|xoxp-|AKIA)[A-Za-z0-9_-]{10,}\b/g, "[REDACTED_KEY]").replace(/Bearer\s+[A-Za-z0-9._~+/=-]{20,}/gi, "Bearer [REDACTED_TOKEN]").replace(/:\/\/[^:]+:[^@\s]+@/g, "://[REDACTED_CREDENTIALS]@").replace(/(https?:\/\/[^\s]+[?&](?:token|key|secret|password|auth)=)[^\s&]*/gi, "$1[REDACTED]").replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, "[REDACTED_EMAIL]").replace(/(?:\/Users\/|\/home\/|C:\\Users\\)[^\s"'`]+/g, "[REDACTED_PATH]");
|
|
1394
1378
|
}
|
|
1395
1379
|
|
|
1396
1380
|
// src/prompt-analyzer.ts
|
|
@@ -1458,7 +1442,7 @@ function analyzeSessionPrompts(db, sessionId) {
|
|
|
1458
1442
|
let stored = 0;
|
|
1459
1443
|
for (let i = 0; i < prompts.length; i++) {
|
|
1460
1444
|
const prompt = prompts[i];
|
|
1461
|
-
const followUps = prompts.slice(i + 1, i + 4).map((
|
|
1445
|
+
const followUps = prompts.slice(i + 1, i + 4).map((p) => p.prompt_text);
|
|
1462
1446
|
const category = categorizePrompt(prompt.prompt_text);
|
|
1463
1447
|
const hash = hashPrompt(prompt.prompt_text);
|
|
1464
1448
|
const { outcome, correctionsNeeded, followUpCount } = detectOutcome(followUps, []);
|
|
@@ -1510,7 +1494,7 @@ async function main() {
|
|
|
1510
1494
|
}
|
|
1511
1495
|
try {
|
|
1512
1496
|
const { score, breakdown } = calculateQualityScore(db, session_id);
|
|
1513
|
-
if (score
|
|
1497
|
+
if (score !== 50) {
|
|
1514
1498
|
storeQualityScore(db, session_id, score, breakdown);
|
|
1515
1499
|
}
|
|
1516
1500
|
backfillQualityScores(db);
|
|
@@ -1683,18 +1667,18 @@ function groupEntriesIntoTurns(entries) {
|
|
|
1683
1667
|
if (currentTurn) {
|
|
1684
1668
|
turns.push(currentTurn);
|
|
1685
1669
|
}
|
|
1686
|
-
const
|
|
1687
|
-
if (
|
|
1670
|
+
const text = getTextFromBlocks(entry.message.content);
|
|
1671
|
+
if (text.trim()) {
|
|
1688
1672
|
currentTurn = {
|
|
1689
|
-
userPrompt:
|
|
1673
|
+
userPrompt: text.trim(),
|
|
1690
1674
|
assistantText: null,
|
|
1691
1675
|
toolCalls: []
|
|
1692
1676
|
};
|
|
1693
1677
|
}
|
|
1694
1678
|
} else if (entry.type === "assistant" && entry.message && currentTurn) {
|
|
1695
|
-
const
|
|
1696
|
-
if (
|
|
1697
|
-
currentTurn.assistantText = currentTurn.assistantText ? currentTurn.assistantText + "\n" +
|
|
1679
|
+
const text = getTextFromBlocks(entry.message.content);
|
|
1680
|
+
if (text.trim()) {
|
|
1681
|
+
currentTurn.assistantText = currentTurn.assistantText ? currentTurn.assistantText + "\n" + text.trim() : text.trim();
|
|
1698
1682
|
}
|
|
1699
1683
|
for (const block of entry.message.content) {
|
|
1700
1684
|
if (block.type === "tool_use") {
|
|
@@ -1764,14 +1748,14 @@ function extractFilesFromToolCall(toolName, input) {
|
|
|
1764
1748
|
return [];
|
|
1765
1749
|
}
|
|
1766
1750
|
function readStdin() {
|
|
1767
|
-
return new Promise((
|
|
1751
|
+
return new Promise((resolve4) => {
|
|
1768
1752
|
let data = "";
|
|
1769
1753
|
process.stdin.setEncoding("utf-8");
|
|
1770
1754
|
process.stdin.on("data", (chunk) => {
|
|
1771
1755
|
data += chunk;
|
|
1772
1756
|
});
|
|
1773
|
-
process.stdin.on("end", () =>
|
|
1774
|
-
setTimeout(() =>
|
|
1757
|
+
process.stdin.on("end", () => resolve4(data));
|
|
1758
|
+
setTimeout(() => resolve4(data), 5e3);
|
|
1775
1759
|
});
|
|
1776
1760
|
}
|
|
1777
1761
|
main();
|
|
@@ -3,7 +3,7 @@ import{createRequire as __cr}from"module";const require=__cr(import.meta.url);
|
|
|
3
3
|
|
|
4
4
|
// src/memory-db.ts
|
|
5
5
|
import Database from "better-sqlite3";
|
|
6
|
-
import { dirname as dirname2 } from "path";
|
|
6
|
+
import { dirname as dirname2, basename } from "path";
|
|
7
7
|
import { existsSync as existsSync2, mkdirSync } from "fs";
|
|
8
8
|
|
|
9
9
|
// src/config.ts
|
|
@@ -268,14 +268,32 @@ function getResolvedPaths() {
|
|
|
268
268
|
};
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
-
// src/memory-
|
|
271
|
+
// src/memory-db.ts
|
|
272
|
+
function sanitizeFts5Query(raw) {
|
|
273
|
+
const trimmed = raw.trim();
|
|
274
|
+
if (!trimmed) return '""';
|
|
275
|
+
const tokens = trimmed.replace(/"/g, "").split(/\s+/).filter(Boolean);
|
|
276
|
+
return tokens.map((t) => `"${t}"`).join(" ");
|
|
277
|
+
}
|
|
278
|
+
function getMemoryDb() {
|
|
279
|
+
const dbPath = getResolvedPaths().memoryDbPath;
|
|
280
|
+
const dir = dirname2(dbPath);
|
|
281
|
+
if (!existsSync2(dir)) {
|
|
282
|
+
mkdirSync(dir, { recursive: true });
|
|
283
|
+
}
|
|
284
|
+
const db = new Database(dbPath);
|
|
285
|
+
db.pragma("journal_mode = WAL");
|
|
286
|
+
db.pragma("foreign_keys = ON");
|
|
287
|
+
initMemorySchema(db);
|
|
288
|
+
return db;
|
|
289
|
+
}
|
|
272
290
|
function initMemorySchema(db) {
|
|
273
291
|
db.exec(`
|
|
274
292
|
-- Sessions table (linked to Claude Code session IDs)
|
|
275
293
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
276
294
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
277
295
|
session_id TEXT UNIQUE NOT NULL,
|
|
278
|
-
project TEXT NOT NULL DEFAULT '
|
|
296
|
+
project TEXT NOT NULL DEFAULT 'my-project',
|
|
279
297
|
git_branch TEXT,
|
|
280
298
|
started_at TEXT NOT NULL,
|
|
281
299
|
started_at_epoch INTEGER NOT NULL,
|
|
@@ -330,9 +348,7 @@ function initMemorySchema(db) {
|
|
|
330
348
|
content_rowid='id'
|
|
331
349
|
);
|
|
332
350
|
`);
|
|
333
|
-
} catch (
|
|
334
|
-
process.stderr.write(`FTS5 setup warning: ${e instanceof Error ? e.message : String(e)}
|
|
335
|
-
`);
|
|
351
|
+
} catch (_e) {
|
|
336
352
|
}
|
|
337
353
|
db.exec(`
|
|
338
354
|
CREATE TRIGGER IF NOT EXISTS observations_ai AFTER INSERT ON observations BEGIN
|
|
@@ -392,9 +408,7 @@ function initMemorySchema(db) {
|
|
|
392
408
|
content_rowid='id'
|
|
393
409
|
);
|
|
394
410
|
`);
|
|
395
|
-
} catch (
|
|
396
|
-
process.stderr.write(`FTS5 setup warning: ${e instanceof Error ? e.message : String(e)}
|
|
397
|
-
`);
|
|
411
|
+
} catch (_e) {
|
|
398
412
|
}
|
|
399
413
|
db.exec(`
|
|
400
414
|
CREATE TRIGGER IF NOT EXISTS prompts_ai AFTER INSERT ON user_prompts BEGIN
|
|
@@ -464,9 +478,7 @@ function initMemorySchema(db) {
|
|
|
464
478
|
content_rowid=id
|
|
465
479
|
);
|
|
466
480
|
`);
|
|
467
|
-
} catch (
|
|
468
|
-
process.stderr.write(`FTS5 setup warning: ${e instanceof Error ? e.message : String(e)}
|
|
469
|
-
`);
|
|
481
|
+
} catch (_e) {
|
|
470
482
|
}
|
|
471
483
|
db.exec(`
|
|
472
484
|
CREATE TRIGGER IF NOT EXISTS ct_fts_insert AFTER INSERT ON conversation_turns BEGIN
|
|
@@ -490,7 +502,7 @@ function initMemorySchema(db) {
|
|
|
490
502
|
CREATE TABLE IF NOT EXISTS session_quality_scores (
|
|
491
503
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
492
504
|
session_id TEXT NOT NULL UNIQUE,
|
|
493
|
-
project TEXT NOT NULL DEFAULT '
|
|
505
|
+
project TEXT NOT NULL DEFAULT 'my-project',
|
|
494
506
|
score INTEGER NOT NULL DEFAULT 100,
|
|
495
507
|
security_score INTEGER NOT NULL DEFAULT 100,
|
|
496
508
|
architecture_score INTEGER NOT NULL DEFAULT 100,
|
|
@@ -513,7 +525,7 @@ function initMemorySchema(db) {
|
|
|
513
525
|
CREATE TABLE IF NOT EXISTS session_costs (
|
|
514
526
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
515
527
|
session_id TEXT NOT NULL UNIQUE,
|
|
516
|
-
project TEXT NOT NULL DEFAULT '
|
|
528
|
+
project TEXT NOT NULL DEFAULT 'my-project',
|
|
517
529
|
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
518
530
|
output_tokens INTEGER NOT NULL DEFAULT 0,
|
|
519
531
|
cache_read_tokens INTEGER NOT NULL DEFAULT 0,
|
|
@@ -741,9 +753,6 @@ function initMemorySchema(db) {
|
|
|
741
753
|
CREATE INDEX IF NOT EXISTS idx_pending_sync_created ON pending_sync(created_at ASC);
|
|
742
754
|
`);
|
|
743
755
|
}
|
|
744
|
-
|
|
745
|
-
// src/memory-queries.ts
|
|
746
|
-
import { basename } from "path";
|
|
747
756
|
function autoDetectTaskId(planFile) {
|
|
748
757
|
if (!planFile) return null;
|
|
749
758
|
const base = basename(planFile);
|
|
@@ -786,7 +795,7 @@ function getFailedAttempts(db, query, limit = 20) {
|
|
|
786
795
|
JOIN observations o ON observations_fts.rowid = o.id
|
|
787
796
|
WHERE observations_fts MATCH ? AND o.type = 'failed_attempt'
|
|
788
797
|
ORDER BY o.recurrence_count DESC, rank LIMIT ?
|
|
789
|
-
`).all(query, limit);
|
|
798
|
+
`).all(sanitizeFts5Query(query), limit);
|
|
790
799
|
}
|
|
791
800
|
return db.prepare(`
|
|
792
801
|
SELECT id, title, detail, session_id, recurrence_count, created_at
|
|
@@ -821,24 +830,6 @@ function linkSessionToTask(db, sessionId, taskId) {
|
|
|
821
830
|
db.prepare("UPDATE sessions SET task_id = ? WHERE session_id = ?").run(taskId, sessionId);
|
|
822
831
|
}
|
|
823
832
|
|
|
824
|
-
// src/memory-db.ts
|
|
825
|
-
var initializedPaths = /* @__PURE__ */ new Set();
|
|
826
|
-
function getMemoryDb() {
|
|
827
|
-
const dbPath = getResolvedPaths().memoryDbPath;
|
|
828
|
-
const dir = dirname2(dbPath);
|
|
829
|
-
if (!existsSync2(dir)) {
|
|
830
|
-
mkdirSync(dir, { recursive: true });
|
|
831
|
-
}
|
|
832
|
-
const db = new Database(dbPath);
|
|
833
|
-
db.pragma("journal_mode = WAL");
|
|
834
|
-
db.pragma("foreign_keys = ON");
|
|
835
|
-
if (!initializedPaths.has(dbPath)) {
|
|
836
|
-
initMemorySchema(db);
|
|
837
|
-
initializedPaths.add(dbPath);
|
|
838
|
-
}
|
|
839
|
-
return db;
|
|
840
|
-
}
|
|
841
|
-
|
|
842
833
|
// src/hooks/session-start.ts
|
|
843
834
|
async function main() {
|
|
844
835
|
try {
|
|
@@ -960,7 +951,7 @@ function buildContext(db, sessionId, source, tokenBudget, taskId) {
|
|
|
960
951
|
}
|
|
961
952
|
sections.sort((a, b) => b.importance - a.importance);
|
|
962
953
|
let usedTokens = 0;
|
|
963
|
-
const headerTokens = estimateTokens("===
|
|
954
|
+
const headerTokens = estimateTokens("=== Massu Memory: Previous Session Context ===\n\n=== END Massu Memory ===\n");
|
|
964
955
|
usedTokens += headerTokens;
|
|
965
956
|
const includedSections = [];
|
|
966
957
|
for (const section of sections) {
|
|
@@ -971,10 +962,10 @@ function buildContext(db, sessionId, source, tokenBudget, taskId) {
|
|
|
971
962
|
}
|
|
972
963
|
}
|
|
973
964
|
if (includedSections.length === 0) return "";
|
|
974
|
-
return `===
|
|
965
|
+
return `=== Massu Memory: Previous Session Context ===
|
|
975
966
|
|
|
976
967
|
${includedSections.join("\n")}
|
|
977
|
-
=== END
|
|
968
|
+
=== END Massu Memory ===
|
|
978
969
|
`;
|
|
979
970
|
}
|
|
980
971
|
function estimateTokens(text) {
|
|
@@ -994,14 +985,14 @@ async function getGitBranch() {
|
|
|
994
985
|
}
|
|
995
986
|
}
|
|
996
987
|
function readStdin() {
|
|
997
|
-
return new Promise((
|
|
988
|
+
return new Promise((resolve3) => {
|
|
998
989
|
let data = "";
|
|
999
990
|
process.stdin.setEncoding("utf-8");
|
|
1000
991
|
process.stdin.on("data", (chunk) => {
|
|
1001
992
|
data += chunk;
|
|
1002
993
|
});
|
|
1003
|
-
process.stdin.on("end", () =>
|
|
1004
|
-
setTimeout(() =>
|
|
994
|
+
process.stdin.on("end", () => resolve3(data));
|
|
995
|
+
setTimeout(() => resolve3(data), 3e3);
|
|
1005
996
|
});
|
|
1006
997
|
}
|
|
1007
998
|
function safeParseJson(json) {
|