@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.
- package/commands/_shared-preamble.md +76 -0
- package/commands/massu-audit-deps.md +211 -0
- package/commands/massu-changelog.md +174 -0
- package/commands/massu-cleanup.md +315 -0
- package/commands/massu-commit.md +481 -0
- package/commands/massu-create-plan.md +752 -0
- package/commands/massu-dead-code.md +131 -0
- package/commands/massu-debug.md +484 -0
- package/commands/massu-deploy.md +91 -0
- package/commands/massu-deps.md +374 -0
- package/commands/massu-doc-gen.md +279 -0
- package/commands/massu-docs.md +364 -0
- package/commands/massu-estimate.md +313 -0
- package/commands/massu-golden-path.md +973 -0
- package/commands/massu-guide.md +167 -0
- package/commands/massu-hotfix.md +480 -0
- package/commands/massu-loop-playwright.md +837 -0
- package/commands/massu-loop.md +775 -0
- package/commands/massu-new-feature.md +511 -0
- package/commands/massu-parity.md +214 -0
- package/commands/massu-plan.md +456 -0
- package/commands/massu-push-light.md +207 -0
- package/commands/massu-push.md +434 -0
- package/commands/massu-refactor.md +410 -0
- package/commands/massu-release.md +363 -0
- package/commands/massu-review.md +238 -0
- package/commands/massu-simplify.md +281 -0
- package/commands/massu-status.md +278 -0
- package/commands/massu-tdd.md +201 -0
- package/commands/massu-test.md +516 -0
- package/commands/massu-verify-playwright.md +281 -0
- package/commands/massu-verify.md +667 -0
- package/dist/cli.js +7772 -3140
- package/dist/hooks/cost-tracker.js +103 -40
- package/dist/hooks/post-edit-context.js +74 -8
- package/dist/hooks/post-tool-use.js +268 -106
- package/dist/hooks/pre-compact.js +167 -43
- package/dist/hooks/pre-delete-check.js +159 -42
- package/dist/hooks/quality-event.js +103 -40
- package/dist/hooks/security-gate.js +29 -0
- package/dist/hooks/session-end.js +143 -84
- package/dist/hooks/session-start.js +186 -49
- package/dist/hooks/user-prompt.js +189 -43
- package/package.json +10 -15
- package/src/adr-generator.ts +9 -2
- package/src/analytics.ts +9 -3
- package/src/audit-trail.ts +10 -3
- package/src/backfill-sessions.ts +5 -4
- package/src/cli.ts +6 -0
- package/src/cloud-sync.ts +14 -18
- package/src/commands/doctor.ts +193 -6
- package/src/commands/init.ts +230 -5
- package/src/commands/install-commands.ts +137 -0
- package/src/config.ts +68 -2
- package/src/cost-tracker.ts +11 -6
- package/src/db.ts +115 -2
- package/src/dependency-scorer.ts +9 -2
- package/src/docs-tools.ts +21 -16
- package/src/hooks/post-edit-context.ts +4 -4
- package/src/hooks/post-tool-use.ts +130 -0
- package/src/hooks/pre-compact.ts +23 -1
- package/src/hooks/pre-delete-check.ts +92 -4
- package/src/hooks/security-gate.ts +32 -0
- package/src/hooks/session-end.ts +3 -3
- package/src/hooks/session-start.ts +99 -6
- package/src/hooks/user-prompt.ts +46 -1
- package/src/import-resolver.ts +2 -1
- package/src/knowledge-db.ts +169 -0
- package/src/knowledge-indexer.ts +704 -0
- package/src/knowledge-tools.ts +1413 -0
- package/src/license.ts +482 -0
- package/src/memory-db.ts +1364 -23
- package/src/memory-tools.ts +14 -15
- package/src/observability-tools.ts +13 -2
- package/src/observation-extractor.ts +11 -4
- package/src/page-deps.ts +3 -2
- package/src/prompt-analyzer.ts +9 -2
- package/src/python/coupling-detector.ts +124 -0
- package/src/python/domain-enforcer.ts +83 -0
- package/src/python/impact-analyzer.ts +95 -0
- package/src/python/import-parser.ts +244 -0
- package/src/python/import-resolver.ts +135 -0
- package/src/python/migration-indexer.ts +115 -0
- package/src/python/migration-parser.ts +332 -0
- package/src/python/model-indexer.ts +70 -0
- package/src/python/model-parser.ts +279 -0
- package/src/python/route-indexer.ts +58 -0
- package/src/python/route-parser.ts +317 -0
- package/src/python-tools.ts +629 -0
- package/src/regression-detector.ts +9 -3
- package/src/security-scorer.ts +9 -2
- package/src/sentinel-db.ts +45 -89
- package/src/sentinel-tools.ts +8 -11
- package/src/server.ts +29 -7
- package/src/session-archiver.ts +4 -5
- package/src/team-knowledge.ts +9 -2
- package/src/tools.ts +1032 -44
- package/src/validate-features-runner.ts +0 -1
- package/src/validation-engine.ts +9 -2
- package/README.md +0 -40
- 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,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, "
|
|
264
|
-
claudeMdPath: resolve(root, "
|
|
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-
|
|
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 '
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 '
|
|
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 '
|
|
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("===
|
|
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 `===
|
|
1083
|
+
return `=== Massu Memory: Previous Session Context ===
|
|
975
1084
|
|
|
976
1085
|
${includedSections.join("\n")}
|
|
977
|
-
=== END
|
|
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((
|
|
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", () =>
|
|
1004
|
-
setTimeout(() =>
|
|
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();
|