@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.
Files changed (87) hide show
  1. package/README.md +2 -2
  2. package/dist/hooks/cost-tracker.js +23 -35
  3. package/dist/hooks/post-edit-context.js +2 -2
  4. package/dist/hooks/post-tool-use.js +43 -58
  5. package/dist/hooks/pre-compact.js +23 -38
  6. package/dist/hooks/pre-delete-check.js +18 -31
  7. package/dist/hooks/quality-event.js +23 -35
  8. package/dist/hooks/session-end.js +62 -78
  9. package/dist/hooks/session-start.js +33 -42
  10. package/dist/hooks/user-prompt.js +23 -38
  11. package/package.json +8 -14
  12. package/src/adr-generator.ts +9 -2
  13. package/src/analytics.ts +9 -3
  14. package/src/audit-trail.ts +10 -3
  15. package/src/cloud-sync.ts +14 -18
  16. package/src/commands/init.ts +1 -5
  17. package/src/cost-tracker.ts +11 -6
  18. package/src/dependency-scorer.ts +9 -2
  19. package/src/docs-tools.ts +13 -10
  20. package/src/hooks/post-edit-context.ts +3 -3
  21. package/src/hooks/session-end.ts +3 -3
  22. package/src/hooks/session-start.ts +2 -2
  23. package/src/memory-db.ts +1351 -23
  24. package/src/memory-tools.ts +14 -15
  25. package/src/observability-tools.ts +13 -2
  26. package/src/prompt-analyzer.ts +9 -2
  27. package/src/regression-detector.ts +9 -3
  28. package/src/security-scorer.ts +9 -2
  29. package/src/sentinel-db.ts +43 -88
  30. package/src/sentinel-tools.ts +8 -11
  31. package/src/server.ts +1 -2
  32. package/src/team-knowledge.ts +9 -2
  33. package/src/tools.ts +771 -35
  34. package/src/validate-features-runner.ts +0 -1
  35. package/src/validation-engine.ts +9 -2
  36. package/dist/cli.js +0 -7890
  37. package/dist/server.js +0 -7008
  38. package/src/__tests__/adr-generator.test.ts +0 -260
  39. package/src/__tests__/analytics.test.ts +0 -282
  40. package/src/__tests__/audit-trail.test.ts +0 -382
  41. package/src/__tests__/backfill-sessions.test.ts +0 -690
  42. package/src/__tests__/cli.test.ts +0 -290
  43. package/src/__tests__/cloud-sync.test.ts +0 -261
  44. package/src/__tests__/config-sections.test.ts +0 -359
  45. package/src/__tests__/config.test.ts +0 -732
  46. package/src/__tests__/cost-tracker.test.ts +0 -348
  47. package/src/__tests__/db.test.ts +0 -177
  48. package/src/__tests__/dependency-scorer.test.ts +0 -325
  49. package/src/__tests__/docs-integration.test.ts +0 -178
  50. package/src/__tests__/docs-tools.test.ts +0 -199
  51. package/src/__tests__/domains.test.ts +0 -236
  52. package/src/__tests__/hooks.test.ts +0 -221
  53. package/src/__tests__/import-resolver.test.ts +0 -95
  54. package/src/__tests__/integration/path-traversal.test.ts +0 -134
  55. package/src/__tests__/integration/pricing-consistency.test.ts +0 -88
  56. package/src/__tests__/integration/tool-registration.test.ts +0 -146
  57. package/src/__tests__/memory-db.test.ts +0 -404
  58. package/src/__tests__/memory-enhancements.test.ts +0 -316
  59. package/src/__tests__/memory-tools.test.ts +0 -199
  60. package/src/__tests__/middleware-tree.test.ts +0 -177
  61. package/src/__tests__/observability-tools.test.ts +0 -595
  62. package/src/__tests__/observability.test.ts +0 -437
  63. package/src/__tests__/observation-extractor.test.ts +0 -167
  64. package/src/__tests__/page-deps.test.ts +0 -60
  65. package/src/__tests__/prompt-analyzer.test.ts +0 -298
  66. package/src/__tests__/regression-detector.test.ts +0 -295
  67. package/src/__tests__/rules.test.ts +0 -87
  68. package/src/__tests__/schema-mapper.test.ts +0 -29
  69. package/src/__tests__/security-scorer.test.ts +0 -238
  70. package/src/__tests__/security-utils.test.ts +0 -175
  71. package/src/__tests__/sentinel-db.test.ts +0 -491
  72. package/src/__tests__/sentinel-scanner.test.ts +0 -750
  73. package/src/__tests__/sentinel-tools.test.ts +0 -324
  74. package/src/__tests__/sentinel-types.test.ts +0 -750
  75. package/src/__tests__/server.test.ts +0 -452
  76. package/src/__tests__/session-archiver.test.ts +0 -524
  77. package/src/__tests__/session-state-generator.test.ts +0 -900
  78. package/src/__tests__/team-knowledge.test.ts +0 -327
  79. package/src/__tests__/tools.test.ts +0 -340
  80. package/src/__tests__/transcript-parser.test.ts +0 -195
  81. package/src/__tests__/trpc-index.test.ts +0 -25
  82. package/src/__tests__/validate-features-runner.test.ts +0 -517
  83. package/src/__tests__/validation-engine.test.ts +0 -300
  84. package/src/core-tools.ts +0 -685
  85. package/src/memory-queries.ts +0 -804
  86. package/src/memory-schema.ts +0 -546
  87. 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-schema.ts
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 'unknown',
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 (e) {
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 (e) {
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 (e) {
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 'unknown',
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 'unknown',
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((resolve2) => {
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", () => resolve2(data));
848
- setTimeout(() => resolve2(data), 3e3);
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-schema.ts
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 'unknown',
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 (e) {
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 (e) {
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 (e) {
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 'unknown',
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 'unknown',
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
- db.prepare("DELETE FROM pending_sync WHERE retry_count >= 10").run();
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 resolve2, dirname as dirname3 } from "path";
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 = resolve2(PROJECT_ROOT, ".claude/session-state/CURRENT.md");
1011
- const archiveDir = resolve2(PROJECT_ROOT, ".claude/session-state/archive");
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 = resolve2(archiveDir, `${date}-${slug}.md`);
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(text2) {
1133
- return Math.ceil(text2.length / 4);
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
- try {
1215
- const config = getConfig();
1216
- if (!config.cloud?.enabled || !config.cloud?.apiKey) return;
1217
- const pending = dequeuePendingSync(db, 10);
1218
- for (const item of pending) {
1219
- try {
1220
- const payload = JSON.parse(item.payload);
1221
- const result = await syncToCloud(db, payload);
1222
- if (result.success) {
1223
- removePendingSync(db, item.id);
1224
- } else {
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((resolve3) => setTimeout(resolve3, ms));
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: 5, output_per_million: 25, cache_read_per_million: 0.5, cache_write_per_million: 6.25 },
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-3-5-haiku-20241022": { input_per_million: 0.8, output_per_million: 4, cache_read_per_million: 0.08, cache_write_per_million: 1 },
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(text2) {
1393
- return text2.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]");
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((p2) => p2.prompt_text);
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 > 0) {
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 text2 = getTextFromBlocks(entry.message.content);
1687
- if (text2.trim()) {
1670
+ const text = getTextFromBlocks(entry.message.content);
1671
+ if (text.trim()) {
1688
1672
  currentTurn = {
1689
- userPrompt: text2.trim(),
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 text2 = getTextFromBlocks(entry.message.content);
1696
- if (text2.trim()) {
1697
- currentTurn.assistantText = currentTurn.assistantText ? currentTurn.assistantText + "\n" + text2.trim() : text2.trim();
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((resolve3) => {
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", () => resolve3(data));
1774
- setTimeout(() => resolve3(data), 5e3);
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-schema.ts
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 'unknown',
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 (e) {
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 (e) {
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 (e) {
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 'unknown',
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 'unknown',
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("=== CS MEMORY: Previous Session Context ===\n\n=== END CS MEMORY ===\n");
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 `=== CS MEMORY: Previous Session Context ===
965
+ return `=== Massu Memory: Previous Session Context ===
975
966
 
976
967
  ${includedSections.join("\n")}
977
- === END CS MEMORY ===
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((resolve2) => {
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", () => resolve2(data));
1004
- setTimeout(() => resolve2(data), 3e3);
994
+ process.stdin.on("end", () => resolve3(data));
995
+ setTimeout(() => resolve3(data), 3e3);
1005
996
  });
1006
997
  }
1007
998
  function safeParseJson(json) {