@massu/core 0.1.1 → 0.4.0

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 (151) hide show
  1. package/commands/_shared-preamble.md +76 -0
  2. package/commands/massu-audit-deps.md +211 -0
  3. package/commands/massu-changelog.md +174 -0
  4. package/commands/massu-cleanup.md +315 -0
  5. package/commands/massu-commit.md +481 -0
  6. package/commands/massu-create-plan.md +752 -0
  7. package/commands/massu-dead-code.md +131 -0
  8. package/commands/massu-debug.md +484 -0
  9. package/commands/massu-deploy.md +91 -0
  10. package/commands/massu-deps.md +374 -0
  11. package/commands/massu-doc-gen.md +279 -0
  12. package/commands/massu-docs.md +364 -0
  13. package/commands/massu-estimate.md +313 -0
  14. package/commands/massu-golden-path.md +973 -0
  15. package/commands/massu-guide.md +167 -0
  16. package/commands/massu-hotfix.md +480 -0
  17. package/commands/massu-loop-playwright.md +837 -0
  18. package/commands/massu-loop.md +775 -0
  19. package/commands/massu-new-feature.md +511 -0
  20. package/commands/massu-parity.md +214 -0
  21. package/commands/massu-plan.md +456 -0
  22. package/commands/massu-push-light.md +207 -0
  23. package/commands/massu-push.md +434 -0
  24. package/commands/massu-refactor.md +410 -0
  25. package/commands/massu-release.md +363 -0
  26. package/commands/massu-review.md +238 -0
  27. package/commands/massu-simplify.md +281 -0
  28. package/commands/massu-status.md +278 -0
  29. package/commands/massu-tdd.md +201 -0
  30. package/commands/massu-test.md +516 -0
  31. package/commands/massu-verify-playwright.md +281 -0
  32. package/commands/massu-verify.md +667 -0
  33. package/dist/cli.js +7772 -3140
  34. package/dist/hooks/cost-tracker.js +103 -40
  35. package/dist/hooks/post-edit-context.js +74 -8
  36. package/dist/hooks/post-tool-use.js +268 -106
  37. package/dist/hooks/pre-compact.js +167 -43
  38. package/dist/hooks/pre-delete-check.js +159 -42
  39. package/dist/hooks/quality-event.js +103 -40
  40. package/dist/hooks/security-gate.js +29 -0
  41. package/dist/hooks/session-end.js +143 -84
  42. package/dist/hooks/session-start.js +186 -49
  43. package/dist/hooks/user-prompt.js +189 -43
  44. package/package.json +10 -15
  45. package/src/adr-generator.ts +9 -2
  46. package/src/analytics.ts +9 -3
  47. package/src/audit-trail.ts +10 -3
  48. package/src/backfill-sessions.ts +5 -4
  49. package/src/cli.ts +6 -0
  50. package/src/cloud-sync.ts +14 -18
  51. package/src/commands/doctor.ts +193 -6
  52. package/src/commands/init.ts +230 -5
  53. package/src/commands/install-commands.ts +137 -0
  54. package/src/config.ts +68 -2
  55. package/src/cost-tracker.ts +11 -6
  56. package/src/db.ts +115 -2
  57. package/src/dependency-scorer.ts +9 -2
  58. package/src/docs-tools.ts +21 -16
  59. package/src/hooks/post-edit-context.ts +4 -4
  60. package/src/hooks/post-tool-use.ts +130 -0
  61. package/src/hooks/pre-compact.ts +23 -1
  62. package/src/hooks/pre-delete-check.ts +92 -4
  63. package/src/hooks/security-gate.ts +32 -0
  64. package/src/hooks/session-end.ts +3 -3
  65. package/src/hooks/session-start.ts +99 -6
  66. package/src/hooks/user-prompt.ts +46 -1
  67. package/src/import-resolver.ts +2 -1
  68. package/src/knowledge-db.ts +169 -0
  69. package/src/knowledge-indexer.ts +704 -0
  70. package/src/knowledge-tools.ts +1413 -0
  71. package/src/license.ts +482 -0
  72. package/src/memory-db.ts +1364 -23
  73. package/src/memory-tools.ts +14 -15
  74. package/src/observability-tools.ts +13 -2
  75. package/src/observation-extractor.ts +11 -4
  76. package/src/page-deps.ts +3 -2
  77. package/src/prompt-analyzer.ts +9 -2
  78. package/src/python/coupling-detector.ts +124 -0
  79. package/src/python/domain-enforcer.ts +83 -0
  80. package/src/python/impact-analyzer.ts +95 -0
  81. package/src/python/import-parser.ts +244 -0
  82. package/src/python/import-resolver.ts +135 -0
  83. package/src/python/migration-indexer.ts +115 -0
  84. package/src/python/migration-parser.ts +332 -0
  85. package/src/python/model-indexer.ts +70 -0
  86. package/src/python/model-parser.ts +279 -0
  87. package/src/python/route-indexer.ts +58 -0
  88. package/src/python/route-parser.ts +317 -0
  89. package/src/python-tools.ts +629 -0
  90. package/src/regression-detector.ts +9 -3
  91. package/src/security-scorer.ts +9 -2
  92. package/src/sentinel-db.ts +45 -89
  93. package/src/sentinel-tools.ts +8 -11
  94. package/src/server.ts +29 -7
  95. package/src/session-archiver.ts +4 -5
  96. package/src/team-knowledge.ts +9 -2
  97. package/src/tools.ts +1032 -44
  98. package/src/validate-features-runner.ts +0 -1
  99. package/src/validation-engine.ts +9 -2
  100. package/README.md +0 -40
  101. package/dist/server.js +0 -7008
  102. package/src/__tests__/adr-generator.test.ts +0 -260
  103. package/src/__tests__/analytics.test.ts +0 -282
  104. package/src/__tests__/audit-trail.test.ts +0 -382
  105. package/src/__tests__/backfill-sessions.test.ts +0 -690
  106. package/src/__tests__/cli.test.ts +0 -290
  107. package/src/__tests__/cloud-sync.test.ts +0 -261
  108. package/src/__tests__/config-sections.test.ts +0 -359
  109. package/src/__tests__/config.test.ts +0 -732
  110. package/src/__tests__/cost-tracker.test.ts +0 -348
  111. package/src/__tests__/db.test.ts +0 -177
  112. package/src/__tests__/dependency-scorer.test.ts +0 -325
  113. package/src/__tests__/docs-integration.test.ts +0 -178
  114. package/src/__tests__/docs-tools.test.ts +0 -199
  115. package/src/__tests__/domains.test.ts +0 -236
  116. package/src/__tests__/hooks.test.ts +0 -221
  117. package/src/__tests__/import-resolver.test.ts +0 -95
  118. package/src/__tests__/integration/path-traversal.test.ts +0 -134
  119. package/src/__tests__/integration/pricing-consistency.test.ts +0 -88
  120. package/src/__tests__/integration/tool-registration.test.ts +0 -146
  121. package/src/__tests__/memory-db.test.ts +0 -404
  122. package/src/__tests__/memory-enhancements.test.ts +0 -316
  123. package/src/__tests__/memory-tools.test.ts +0 -199
  124. package/src/__tests__/middleware-tree.test.ts +0 -177
  125. package/src/__tests__/observability-tools.test.ts +0 -595
  126. package/src/__tests__/observability.test.ts +0 -437
  127. package/src/__tests__/observation-extractor.test.ts +0 -167
  128. package/src/__tests__/page-deps.test.ts +0 -60
  129. package/src/__tests__/prompt-analyzer.test.ts +0 -298
  130. package/src/__tests__/regression-detector.test.ts +0 -295
  131. package/src/__tests__/rules.test.ts +0 -87
  132. package/src/__tests__/schema-mapper.test.ts +0 -29
  133. package/src/__tests__/security-scorer.test.ts +0 -238
  134. package/src/__tests__/security-utils.test.ts +0 -175
  135. package/src/__tests__/sentinel-db.test.ts +0 -491
  136. package/src/__tests__/sentinel-scanner.test.ts +0 -750
  137. package/src/__tests__/sentinel-tools.test.ts +0 -324
  138. package/src/__tests__/sentinel-types.test.ts +0 -750
  139. package/src/__tests__/server.test.ts +0 -452
  140. package/src/__tests__/session-archiver.test.ts +0 -524
  141. package/src/__tests__/session-state-generator.test.ts +0 -900
  142. package/src/__tests__/team-knowledge.test.ts +0 -327
  143. package/src/__tests__/tools.test.ts +0 -340
  144. package/src/__tests__/transcript-parser.test.ts +0 -195
  145. package/src/__tests__/trpc-index.test.ts +0 -25
  146. package/src/__tests__/validate-features-runner.test.ts +0 -517
  147. package/src/__tests__/validation-engine.test.ts +0 -300
  148. package/src/core-tools.ts +0 -685
  149. package/src/memory-queries.ts +0 -804
  150. package/src/memory-schema.ts +0 -546
  151. package/src/tool-helpers.ts +0 -41
@@ -3,12 +3,13 @@ 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
10
10
  import { resolve, dirname } from "path";
11
11
  import { existsSync, readFileSync } from "fs";
12
+ import { homedir } from "os";
12
13
  import { parse as parseYaml } from "yaml";
13
14
  import { z } from "zod";
14
15
  var DomainConfigSchema = z.object({
@@ -140,6 +141,49 @@ var CloudConfigSchema = z.object({
140
141
  audit: z.boolean().default(true)
141
142
  }).default({ memory: true, analytics: true, audit: true })
142
143
  }).optional();
144
+ var ConventionsConfigSchema = z.object({
145
+ claudeDirName: z.string().default(".claude").refine(
146
+ (s) => !s.includes("..") && !s.startsWith("/"),
147
+ { message: 'claudeDirName must not contain ".." or start with "/"' }
148
+ ),
149
+ sessionStatePath: z.string().default(".claude/session-state/CURRENT.md").refine(
150
+ (s) => !s.includes("..") && !s.startsWith("/"),
151
+ { message: 'sessionStatePath must not contain ".." or start with "/"' }
152
+ ),
153
+ sessionArchivePath: z.string().default(".claude/session-state/archive").refine(
154
+ (s) => !s.includes("..") && !s.startsWith("/"),
155
+ { message: 'sessionArchivePath must not contain ".." or start with "/"' }
156
+ ),
157
+ knowledgeCategories: z.array(z.string()).default([
158
+ "patterns",
159
+ "commands",
160
+ "incidents",
161
+ "reference",
162
+ "protocols",
163
+ "checklists",
164
+ "playbooks",
165
+ "critical",
166
+ "scripts",
167
+ "status",
168
+ "templates",
169
+ "loop-state",
170
+ "session-state",
171
+ "agents"
172
+ ]),
173
+ knowledgeSourceFiles: z.array(z.string()).default(["CLAUDE.md", "MEMORY.md", "corrections.md"]),
174
+ excludePatterns: z.array(z.string()).default(["/ARCHIVE/", "/SESSION-HISTORY/"])
175
+ }).optional();
176
+ var PythonDomainConfigSchema = z.object({
177
+ name: z.string(),
178
+ packages: z.array(z.string()),
179
+ allowed_imports_from: z.array(z.string()).default([])
180
+ });
181
+ var PythonConfigSchema = z.object({
182
+ root: z.string(),
183
+ alembic_dir: z.string().optional(),
184
+ domains: z.array(PythonDomainConfigSchema).default([]),
185
+ exclude_dirs: z.array(z.string()).default(["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"])
186
+ }).optional();
143
187
  var PathsConfigSchema = z.object({
144
188
  source: z.string().default("src"),
145
189
  aliases: z.record(z.string(), z.string()).default({ "@": "src" }),
@@ -174,7 +218,9 @@ var RawConfigSchema = z.object({
174
218
  security: SecurityConfigSchema,
175
219
  team: TeamConfigSchema,
176
220
  regression: RegressionConfigSchema,
177
- cloud: CloudConfigSchema
221
+ cloud: CloudConfigSchema,
222
+ conventions: ConventionsConfigSchema,
223
+ python: PythonConfigSchema
178
224
  }).passthrough();
179
225
  var _config = null;
180
226
  var _projectRoot = null;
@@ -238,13 +284,24 @@ function getConfig() {
238
284
  security: parsed.security,
239
285
  team: parsed.team,
240
286
  regression: parsed.regression,
241
- cloud: parsed.cloud
287
+ cloud: parsed.cloud,
288
+ conventions: parsed.conventions,
289
+ python: parsed.python
242
290
  };
291
+ if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
292
+ _config.cloud = {
293
+ enabled: true,
294
+ sync: { memory: true, analytics: true, audit: true },
295
+ ..._config.cloud,
296
+ apiKey: process.env.MASSU_API_KEY
297
+ };
298
+ }
243
299
  return _config;
244
300
  }
245
301
  function getResolvedPaths() {
246
302
  const config = getConfig();
247
303
  const root = getProjectRoot();
304
+ const claudeDirName = config.conventions?.claudeDirName ?? ".claude";
248
305
  return {
249
306
  codegraphDbPath: resolve(root, ".codegraph/codegraph.db"),
250
307
  dataDbPath: resolve(root, ".massu/data.db"),
@@ -260,22 +317,49 @@ function getResolvedPaths() {
260
317
  ),
261
318
  extensions: [".ts", ".tsx", ".js", ".jsx"],
262
319
  indexFiles: ["index.ts", "index.tsx", "index.js", "index.jsx"],
263
- patternsDir: resolve(root, ".claude/patterns"),
264
- claudeMdPath: resolve(root, ".claude/CLAUDE.md"),
320
+ patternsDir: resolve(root, claudeDirName, "patterns"),
321
+ claudeMdPath: resolve(root, claudeDirName, "CLAUDE.md"),
265
322
  docsMapPath: resolve(root, ".massu/docs-map.json"),
266
323
  helpSitePath: resolve(root, "../" + config.project.name + "-help"),
267
- memoryDbPath: resolve(root, ".massu/memory.db")
324
+ memoryDbPath: resolve(root, ".massu/memory.db"),
325
+ knowledgeDbPath: resolve(root, ".massu/knowledge.db"),
326
+ plansDir: resolve(root, "docs/plans"),
327
+ docsDir: resolve(root, "docs"),
328
+ claudeDir: resolve(root, claudeDirName),
329
+ memoryDir: resolve(homedir(), claudeDirName, "projects", root.replace(/\//g, "-"), "memory"),
330
+ sessionStatePath: resolve(root, config.conventions?.sessionStatePath ?? `${claudeDirName}/session-state/CURRENT.md`),
331
+ sessionArchivePath: resolve(root, config.conventions?.sessionArchivePath ?? `${claudeDirName}/session-state/archive`),
332
+ mcpJsonPath: resolve(root, ".mcp.json"),
333
+ settingsLocalPath: resolve(root, claudeDirName, "settings.local.json")
268
334
  };
269
335
  }
270
336
 
271
- // src/memory-schema.ts
337
+ // src/memory-db.ts
338
+ function sanitizeFts5Query(raw) {
339
+ const trimmed = raw.trim();
340
+ if (!trimmed) return '""';
341
+ const tokens = trimmed.replace(/"/g, "").split(/\s+/).filter(Boolean);
342
+ return tokens.map((t) => `"${t}"`).join(" ");
343
+ }
344
+ function getMemoryDb() {
345
+ const dbPath = getResolvedPaths().memoryDbPath;
346
+ const dir = dirname2(dbPath);
347
+ if (!existsSync2(dir)) {
348
+ mkdirSync(dir, { recursive: true });
349
+ }
350
+ const db = new Database(dbPath);
351
+ db.pragma("journal_mode = WAL");
352
+ db.pragma("foreign_keys = ON");
353
+ initMemorySchema(db);
354
+ return db;
355
+ }
272
356
  function initMemorySchema(db) {
273
357
  db.exec(`
274
358
  -- Sessions table (linked to Claude Code session IDs)
275
359
  CREATE TABLE IF NOT EXISTS sessions (
276
360
  id INTEGER PRIMARY KEY AUTOINCREMENT,
277
361
  session_id TEXT UNIQUE NOT NULL,
278
- project TEXT NOT NULL DEFAULT 'unknown',
362
+ project TEXT NOT NULL DEFAULT 'my-project',
279
363
  git_branch TEXT,
280
364
  started_at TEXT NOT NULL,
281
365
  started_at_epoch INTEGER NOT NULL,
@@ -330,9 +414,7 @@ function initMemorySchema(db) {
330
414
  content_rowid='id'
331
415
  );
332
416
  `);
333
- } catch (e) {
334
- process.stderr.write(`FTS5 setup warning: ${e instanceof Error ? e.message : String(e)}
335
- `);
417
+ } catch (_e) {
336
418
  }
337
419
  db.exec(`
338
420
  CREATE TRIGGER IF NOT EXISTS observations_ai AFTER INSERT ON observations BEGIN
@@ -392,9 +474,7 @@ function initMemorySchema(db) {
392
474
  content_rowid='id'
393
475
  );
394
476
  `);
395
- } catch (e) {
396
- process.stderr.write(`FTS5 setup warning: ${e instanceof Error ? e.message : String(e)}
397
- `);
477
+ } catch (_e) {
398
478
  }
399
479
  db.exec(`
400
480
  CREATE TRIGGER IF NOT EXISTS prompts_ai AFTER INSERT ON user_prompts BEGIN
@@ -464,9 +544,7 @@ function initMemorySchema(db) {
464
544
  content_rowid=id
465
545
  );
466
546
  `);
467
- } catch (e) {
468
- process.stderr.write(`FTS5 setup warning: ${e instanceof Error ? e.message : String(e)}
469
- `);
547
+ } catch (_e) {
470
548
  }
471
549
  db.exec(`
472
550
  CREATE TRIGGER IF NOT EXISTS ct_fts_insert AFTER INSERT ON conversation_turns BEGIN
@@ -490,7 +568,7 @@ function initMemorySchema(db) {
490
568
  CREATE TABLE IF NOT EXISTS session_quality_scores (
491
569
  id INTEGER PRIMARY KEY AUTOINCREMENT,
492
570
  session_id TEXT NOT NULL UNIQUE,
493
- project TEXT NOT NULL DEFAULT 'unknown',
571
+ project TEXT NOT NULL DEFAULT 'my-project',
494
572
  score INTEGER NOT NULL DEFAULT 100,
495
573
  security_score INTEGER NOT NULL DEFAULT 100,
496
574
  architecture_score INTEGER NOT NULL DEFAULT 100,
@@ -513,7 +591,7 @@ function initMemorySchema(db) {
513
591
  CREATE TABLE IF NOT EXISTS session_costs (
514
592
  id INTEGER PRIMARY KEY AUTOINCREMENT,
515
593
  session_id TEXT NOT NULL UNIQUE,
516
- project TEXT NOT NULL DEFAULT 'unknown',
594
+ project TEXT NOT NULL DEFAULT 'my-project',
517
595
  input_tokens INTEGER NOT NULL DEFAULT 0,
518
596
  output_tokens INTEGER NOT NULL DEFAULT 0,
519
597
  cache_read_tokens INTEGER NOT NULL DEFAULT 0,
@@ -740,10 +818,16 @@ function initMemorySchema(db) {
740
818
  );
741
819
  CREATE INDEX IF NOT EXISTS idx_pending_sync_created ON pending_sync(created_at ASC);
742
820
  `);
821
+ db.exec(`
822
+ CREATE TABLE IF NOT EXISTS license_cache (
823
+ api_key_hash TEXT PRIMARY KEY,
824
+ tier TEXT NOT NULL,
825
+ valid_until TEXT NOT NULL,
826
+ last_validated TEXT NOT NULL,
827
+ features TEXT DEFAULT '[]'
828
+ );
829
+ `);
743
830
  }
744
-
745
- // src/memory-queries.ts
746
- import { basename } from "path";
747
831
  function autoDetectTaskId(planFile) {
748
832
  if (!planFile) return null;
749
833
  const base = basename(planFile);
@@ -786,7 +870,7 @@ function getFailedAttempts(db, query, limit = 20) {
786
870
  JOIN observations o ON observations_fts.rowid = o.id
787
871
  WHERE observations_fts MATCH ? AND o.type = 'failed_attempt'
788
872
  ORDER BY o.recurrence_count DESC, rank LIMIT ?
789
- `).all(query, limit);
873
+ `).all(sanitizeFts5Query(query), limit);
790
874
  }
791
875
  return db.prepare(`
792
876
  SELECT id, title, detail, session_id, recurrence_count, created_at
@@ -821,25 +905,9 @@ function linkSessionToTask(db, sessionId, taskId) {
821
905
  db.prepare("UPDATE sessions SET task_id = ? WHERE session_id = ?").run(taskId, sessionId);
822
906
  }
823
907
 
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
908
  // src/hooks/session-start.ts
909
+ import { readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
910
+ import { join } from "path";
843
911
  async function main() {
844
912
  try {
845
913
  const input = await readStdin();
@@ -866,7 +934,7 @@ Session memory, code intelligence, and governance are now active.
866
934
  `
867
935
  );
868
936
  }
869
- const context = buildContext(db, session_id, source ?? "startup", tokenBudget, session?.task_id ?? null);
937
+ const context = await buildContext(db, session_id, source ?? "startup", tokenBudget, session?.task_id ?? null);
870
938
  if (context.trim()) {
871
939
  process.stdout.write(context);
872
940
  }
@@ -891,7 +959,7 @@ function getTokenBudget(source) {
891
959
  return 2e3;
892
960
  }
893
961
  }
894
- function buildContext(db, sessionId, source, tokenBudget, taskId) {
962
+ async function buildContext(db, sessionId, source, tokenBudget, taskId) {
895
963
  const sections = [];
896
964
  const failures = getFailedAttempts(db, void 0, 10);
897
965
  if (failures.length > 0) {
@@ -948,6 +1016,47 @@ function buildContext(db, sessionId, source, tokenBudget, taskId) {
948
1016
  sections.push({ text: progressText, importance: 8 });
949
1017
  }
950
1018
  }
1019
+ const preventionRules = loadCorrectionsPreventionRules();
1020
+ if (preventionRules.length > 0) {
1021
+ let rulesText = "### Active Prevention Rules (from corrections.md)\n";
1022
+ for (const rule of preventionRules) {
1023
+ rulesText += `- ${rule}
1024
+ `;
1025
+ }
1026
+ sections.push({ text: rulesText, importance: 9 });
1027
+ }
1028
+ try {
1029
+ const knowledgeDbPath = getResolvedPaths().knowledgeDbPath;
1030
+ if (existsSync3(knowledgeDbPath)) {
1031
+ const Database2 = (await import("better-sqlite3")).default;
1032
+ const kdb = new Database2(knowledgeDbPath, { readonly: true });
1033
+ try {
1034
+ const stats = kdb.prepare(
1035
+ "SELECT COUNT(*) as doc_count, MAX(indexed_at) as last_indexed FROM knowledge_documents"
1036
+ ).get();
1037
+ if (stats.doc_count > 0 && stats.last_indexed) {
1038
+ const ageMs = Date.now() - new Date(stats.last_indexed).getTime();
1039
+ const ageHours = Math.round(ageMs / 36e5);
1040
+ if (ageHours > 24) {
1041
+ sections.push({
1042
+ text: `### Knowledge Index Status
1043
+ Index has ${stats.doc_count} documents, last indexed ${ageHours}h ago. Consider re-indexing.
1044
+ `,
1045
+ importance: 3
1046
+ });
1047
+ }
1048
+ } else if (stats.doc_count === 0) {
1049
+ sections.push({
1050
+ text: "### Knowledge Index Status\nKnowledge index is empty. Run knowledge indexing to populate it.\n",
1051
+ importance: 2
1052
+ });
1053
+ }
1054
+ } finally {
1055
+ kdb.close();
1056
+ }
1057
+ }
1058
+ } catch (_knowledgeErr) {
1059
+ }
951
1060
  const recentObs = getRecentObservations(db, 20);
952
1061
  if (recentObs.length > 0) {
953
1062
  let obsText = "### Recent Observations\n";
@@ -960,7 +1069,7 @@ function buildContext(db, sessionId, source, tokenBudget, taskId) {
960
1069
  }
961
1070
  sections.sort((a, b) => b.importance - a.importance);
962
1071
  let usedTokens = 0;
963
- const headerTokens = estimateTokens("=== CS MEMORY: Previous Session Context ===\n\n=== END CS MEMORY ===\n");
1072
+ const headerTokens = estimateTokens("=== Massu Memory: Previous Session Context ===\n\n=== END Massu Memory ===\n");
964
1073
  usedTokens += headerTokens;
965
1074
  const includedSections = [];
966
1075
  for (const section of sections) {
@@ -971,10 +1080,10 @@ function buildContext(db, sessionId, source, tokenBudget, taskId) {
971
1080
  }
972
1081
  }
973
1082
  if (includedSections.length === 0) return "";
974
- return `=== CS MEMORY: Previous Session Context ===
1083
+ return `=== Massu Memory: Previous Session Context ===
975
1084
 
976
1085
  ${includedSections.join("\n")}
977
- === END CS MEMORY ===
1086
+ === END Massu Memory ===
978
1087
  `;
979
1088
  }
980
1089
  function estimateTokens(text) {
@@ -994,14 +1103,14 @@ async function getGitBranch() {
994
1103
  }
995
1104
  }
996
1105
  function readStdin() {
997
- return new Promise((resolve2) => {
1106
+ return new Promise((resolve3) => {
998
1107
  let data = "";
999
1108
  process.stdin.setEncoding("utf-8");
1000
1109
  process.stdin.on("data", (chunk) => {
1001
1110
  data += chunk;
1002
1111
  });
1003
- process.stdin.on("end", () => resolve2(data));
1004
- setTimeout(() => resolve2(data), 3e3);
1112
+ process.stdin.on("end", () => resolve3(data));
1113
+ setTimeout(() => resolve3(data), 3e3);
1005
1114
  });
1006
1115
  }
1007
1116
  function safeParseJson(json) {
@@ -1011,4 +1120,32 @@ function safeParseJson(json) {
1011
1120
  return null;
1012
1121
  }
1013
1122
  }
1123
+ function loadCorrectionsPreventionRules() {
1124
+ try {
1125
+ const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "";
1126
+ const cwd = process.cwd();
1127
+ const config = getConfig();
1128
+ const claudeDirName = config.conventions?.claudeDirName ?? ".claude";
1129
+ const projectDirName = cwd.replace(/\//g, "-").replace(/^-/, "");
1130
+ const correctionsPath = join(homeDir, claudeDirName, "projects", projectDirName, "memory", "corrections.md");
1131
+ if (!existsSync3(correctionsPath)) return [];
1132
+ const content = readFileSync2(correctionsPath, "utf-8");
1133
+ const lines = content.split("\n");
1134
+ const rules = [];
1135
+ for (const line of lines) {
1136
+ const trimmed = line.trim();
1137
+ if (!trimmed.startsWith("|") || !trimmed.endsWith("|")) continue;
1138
+ const cells = trimmed.split("|").map((c) => c.trim()).filter((c) => c.length > 0);
1139
+ if (cells.length < 4) continue;
1140
+ if (cells[0] === "Date" || cells[0].startsWith("-")) continue;
1141
+ const preventionRule = cells[3];
1142
+ if (preventionRule && !preventionRule.startsWith("-") && !preventionRule.startsWith("<!--")) {
1143
+ rules.push(preventionRule);
1144
+ }
1145
+ }
1146
+ return rules;
1147
+ } catch (_e) {
1148
+ return [];
1149
+ }
1150
+ }
1014
1151
  main();