@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,43 @@ 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 getMemoryDb() {
|
|
339
|
+
const dbPath = getResolvedPaths().memoryDbPath;
|
|
340
|
+
const dir = dirname2(dbPath);
|
|
341
|
+
if (!existsSync2(dir)) {
|
|
342
|
+
mkdirSync(dir, { recursive: true });
|
|
343
|
+
}
|
|
344
|
+
const db = new Database(dbPath);
|
|
345
|
+
db.pragma("journal_mode = WAL");
|
|
346
|
+
db.pragma("foreign_keys = ON");
|
|
347
|
+
initMemorySchema(db);
|
|
348
|
+
return db;
|
|
349
|
+
}
|
|
272
350
|
function initMemorySchema(db) {
|
|
273
351
|
db.exec(`
|
|
274
352
|
-- Sessions table (linked to Claude Code session IDs)
|
|
275
353
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
276
354
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
277
355
|
session_id TEXT UNIQUE NOT NULL,
|
|
278
|
-
project TEXT NOT NULL DEFAULT '
|
|
356
|
+
project TEXT NOT NULL DEFAULT 'my-project',
|
|
279
357
|
git_branch TEXT,
|
|
280
358
|
started_at TEXT NOT NULL,
|
|
281
359
|
started_at_epoch INTEGER NOT NULL,
|
|
@@ -330,9 +408,7 @@ function initMemorySchema(db) {
|
|
|
330
408
|
content_rowid='id'
|
|
331
409
|
);
|
|
332
410
|
`);
|
|
333
|
-
} catch (
|
|
334
|
-
process.stderr.write(`FTS5 setup warning: ${e instanceof Error ? e.message : String(e)}
|
|
335
|
-
`);
|
|
411
|
+
} catch (_e) {
|
|
336
412
|
}
|
|
337
413
|
db.exec(`
|
|
338
414
|
CREATE TRIGGER IF NOT EXISTS observations_ai AFTER INSERT ON observations BEGIN
|
|
@@ -392,9 +468,7 @@ function initMemorySchema(db) {
|
|
|
392
468
|
content_rowid='id'
|
|
393
469
|
);
|
|
394
470
|
`);
|
|
395
|
-
} catch (
|
|
396
|
-
process.stderr.write(`FTS5 setup warning: ${e instanceof Error ? e.message : String(e)}
|
|
397
|
-
`);
|
|
471
|
+
} catch (_e) {
|
|
398
472
|
}
|
|
399
473
|
db.exec(`
|
|
400
474
|
CREATE TRIGGER IF NOT EXISTS prompts_ai AFTER INSERT ON user_prompts BEGIN
|
|
@@ -464,9 +538,7 @@ function initMemorySchema(db) {
|
|
|
464
538
|
content_rowid=id
|
|
465
539
|
);
|
|
466
540
|
`);
|
|
467
|
-
} catch (
|
|
468
|
-
process.stderr.write(`FTS5 setup warning: ${e instanceof Error ? e.message : String(e)}
|
|
469
|
-
`);
|
|
541
|
+
} catch (_e) {
|
|
470
542
|
}
|
|
471
543
|
db.exec(`
|
|
472
544
|
CREATE TRIGGER IF NOT EXISTS ct_fts_insert AFTER INSERT ON conversation_turns BEGIN
|
|
@@ -490,7 +562,7 @@ function initMemorySchema(db) {
|
|
|
490
562
|
CREATE TABLE IF NOT EXISTS session_quality_scores (
|
|
491
563
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
492
564
|
session_id TEXT NOT NULL UNIQUE,
|
|
493
|
-
project TEXT NOT NULL DEFAULT '
|
|
565
|
+
project TEXT NOT NULL DEFAULT 'my-project',
|
|
494
566
|
score INTEGER NOT NULL DEFAULT 100,
|
|
495
567
|
security_score INTEGER NOT NULL DEFAULT 100,
|
|
496
568
|
architecture_score INTEGER NOT NULL DEFAULT 100,
|
|
@@ -513,7 +585,7 @@ function initMemorySchema(db) {
|
|
|
513
585
|
CREATE TABLE IF NOT EXISTS session_costs (
|
|
514
586
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
515
587
|
session_id TEXT NOT NULL UNIQUE,
|
|
516
|
-
project TEXT NOT NULL DEFAULT '
|
|
588
|
+
project TEXT NOT NULL DEFAULT 'my-project',
|
|
517
589
|
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
518
590
|
output_tokens INTEGER NOT NULL DEFAULT 0,
|
|
519
591
|
cache_read_tokens INTEGER NOT NULL DEFAULT 0,
|
|
@@ -740,15 +812,27 @@ function initMemorySchema(db) {
|
|
|
740
812
|
);
|
|
741
813
|
CREATE INDEX IF NOT EXISTS idx_pending_sync_created ON pending_sync(created_at ASC);
|
|
742
814
|
`);
|
|
815
|
+
db.exec(`
|
|
816
|
+
CREATE TABLE IF NOT EXISTS license_cache (
|
|
817
|
+
api_key_hash TEXT PRIMARY KEY,
|
|
818
|
+
tier TEXT NOT NULL,
|
|
819
|
+
valid_until TEXT NOT NULL,
|
|
820
|
+
last_validated TEXT NOT NULL,
|
|
821
|
+
features TEXT DEFAULT '[]'
|
|
822
|
+
);
|
|
823
|
+
`);
|
|
743
824
|
}
|
|
744
|
-
|
|
745
|
-
// src/memory-queries.ts
|
|
746
|
-
import { basename } from "path";
|
|
747
825
|
function enqueueSyncPayload(db, payload) {
|
|
748
826
|
db.prepare("INSERT INTO pending_sync (payload) VALUES (?)").run(payload);
|
|
749
827
|
}
|
|
750
828
|
function dequeuePendingSync(db, limit = 10) {
|
|
751
|
-
|
|
829
|
+
const stale = db.prepare(
|
|
830
|
+
"SELECT id FROM pending_sync WHERE retry_count >= 10"
|
|
831
|
+
).all();
|
|
832
|
+
if (stale.length > 0) {
|
|
833
|
+
const ids = stale.map((s) => s.id);
|
|
834
|
+
db.prepare(`DELETE FROM pending_sync WHERE id IN (${ids.map(() => "?").join(",")})`).run(...ids);
|
|
835
|
+
}
|
|
752
836
|
return db.prepare(
|
|
753
837
|
"SELECT id, payload, retry_count FROM pending_sync ORDER BY created_at ASC LIMIT ?"
|
|
754
838
|
).all(limit);
|
|
@@ -840,27 +924,9 @@ function setLastProcessedLine(db, sessionId, lineNumber) {
|
|
|
840
924
|
db.prepare("INSERT OR REPLACE INTO memory_meta (key, value) VALUES (?, ?)").run(`last_processed_line:${sessionId}`, String(lineNumber));
|
|
841
925
|
}
|
|
842
926
|
|
|
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
927
|
// src/session-archiver.ts
|
|
862
928
|
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync, mkdirSync as mkdirSync2, renameSync } from "fs";
|
|
863
|
-
import { resolve as
|
|
929
|
+
import { resolve as resolve3, dirname as dirname3 } from "path";
|
|
864
930
|
|
|
865
931
|
// src/session-state-generator.ts
|
|
866
932
|
function generateCurrentMd(db, sessionId) {
|
|
@@ -1005,17 +1071,17 @@ function safeParseJson(json, fallback) {
|
|
|
1005
1071
|
}
|
|
1006
1072
|
|
|
1007
1073
|
// src/session-archiver.ts
|
|
1008
|
-
var PROJECT_ROOT = getProjectRoot();
|
|
1009
1074
|
function archiveAndRegenerate(db, sessionId) {
|
|
1010
|
-
const
|
|
1011
|
-
const
|
|
1075
|
+
const resolved = getResolvedPaths();
|
|
1076
|
+
const currentMdPath = resolved.sessionStatePath;
|
|
1077
|
+
const archiveDir = resolved.sessionArchivePath;
|
|
1012
1078
|
let archived = false;
|
|
1013
1079
|
let archivePath;
|
|
1014
1080
|
if (existsSync3(currentMdPath)) {
|
|
1015
1081
|
const existingContent = readFileSync2(currentMdPath, "utf-8");
|
|
1016
1082
|
if (existingContent.trim().length > 10) {
|
|
1017
1083
|
const { date, slug } = extractArchiveInfo(existingContent);
|
|
1018
|
-
archivePath =
|
|
1084
|
+
archivePath = resolve3(archiveDir, `${date}-${slug}.md`);
|
|
1019
1085
|
if (!existsSync3(archiveDir)) {
|
|
1020
1086
|
mkdirSync2(archiveDir, { recursive: true });
|
|
1021
1087
|
}
|
|
@@ -1129,8 +1195,8 @@ async function parseTranscriptFrom(filePath, startLine) {
|
|
|
1129
1195
|
}
|
|
1130
1196
|
return { entries, totalLines: lineNumber };
|
|
1131
1197
|
}
|
|
1132
|
-
function estimateTokens(
|
|
1133
|
-
return Math.ceil(
|
|
1198
|
+
function estimateTokens(text) {
|
|
1199
|
+
return Math.ceil(text.length / 4);
|
|
1134
1200
|
}
|
|
1135
1201
|
|
|
1136
1202
|
// src/cloud-sync.ts
|
|
@@ -1211,30 +1277,25 @@ async function syncToCloud(db, payload) {
|
|
|
1211
1277
|
};
|
|
1212
1278
|
}
|
|
1213
1279
|
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));
|
|
1280
|
+
const config = getConfig();
|
|
1281
|
+
if (!config.cloud?.enabled || !config.cloud?.apiKey) return;
|
|
1282
|
+
const pending = dequeuePendingSync(db, 10);
|
|
1283
|
+
for (const item of pending) {
|
|
1284
|
+
try {
|
|
1285
|
+
const payload = JSON.parse(item.payload);
|
|
1286
|
+
const result = await syncToCloud(db, payload);
|
|
1287
|
+
if (result.success) {
|
|
1288
|
+
removePendingSync(db, item.id);
|
|
1289
|
+
} else {
|
|
1290
|
+
incrementRetryCount(db, item.id, result.error ?? "Unknown error");
|
|
1229
1291
|
}
|
|
1292
|
+
} catch (err) {
|
|
1293
|
+
incrementRetryCount(db, item.id, err instanceof Error ? err.message : String(err));
|
|
1230
1294
|
}
|
|
1231
|
-
} catch (err) {
|
|
1232
|
-
process.stderr.write(`massu: drainSyncQueue failed: ${err instanceof Error ? err.message : String(err)}
|
|
1233
|
-
`);
|
|
1234
1295
|
}
|
|
1235
1296
|
}
|
|
1236
1297
|
function sleep(ms) {
|
|
1237
|
-
return new Promise((
|
|
1298
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
1238
1299
|
}
|
|
1239
1300
|
|
|
1240
1301
|
// src/analytics.ts
|
|
@@ -1300,7 +1361,6 @@ function backfillQualityScores(db) {
|
|
|
1300
1361
|
FROM sessions s
|
|
1301
1362
|
LEFT JOIN session_quality_scores q ON s.session_id = q.session_id
|
|
1302
1363
|
WHERE q.session_id IS NULL
|
|
1303
|
-
LIMIT 1000
|
|
1304
1364
|
`).all();
|
|
1305
1365
|
let backfilled = 0;
|
|
1306
1366
|
for (const session of sessions) {
|
|
@@ -1313,11 +1373,10 @@ function backfillQualityScores(db) {
|
|
|
1313
1373
|
|
|
1314
1374
|
// src/cost-tracker.ts
|
|
1315
1375
|
var DEFAULT_MODEL_PRICING = {
|
|
1316
|
-
"claude-opus-4-6": { input_per_million:
|
|
1376
|
+
"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
1377
|
"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
1378
|
"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 },
|
|
1379
|
+
"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
1380
|
"default": { input_per_million: 3, output_per_million: 15, cache_read_per_million: 0.3, cache_write_per_million: 3.75 }
|
|
1322
1381
|
};
|
|
1323
1382
|
function getModelPricing() {
|
|
@@ -1389,8 +1448,8 @@ import { createHash } from "crypto";
|
|
|
1389
1448
|
function escapeRegex(str) {
|
|
1390
1449
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1391
1450
|
}
|
|
1392
|
-
function redactSensitiveContent(
|
|
1393
|
-
return
|
|
1451
|
+
function redactSensitiveContent(text) {
|
|
1452
|
+
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
1453
|
}
|
|
1395
1454
|
|
|
1396
1455
|
// src/prompt-analyzer.ts
|
|
@@ -1458,7 +1517,7 @@ function analyzeSessionPrompts(db, sessionId) {
|
|
|
1458
1517
|
let stored = 0;
|
|
1459
1518
|
for (let i = 0; i < prompts.length; i++) {
|
|
1460
1519
|
const prompt = prompts[i];
|
|
1461
|
-
const followUps = prompts.slice(i + 1, i + 4).map((
|
|
1520
|
+
const followUps = prompts.slice(i + 1, i + 4).map((p) => p.prompt_text);
|
|
1462
1521
|
const category = categorizePrompt(prompt.prompt_text);
|
|
1463
1522
|
const hash = hashPrompt(prompt.prompt_text);
|
|
1464
1523
|
const { outcome, correctionsNeeded, followUpCount } = detectOutcome(followUps, []);
|
|
@@ -1510,7 +1569,7 @@ async function main() {
|
|
|
1510
1569
|
}
|
|
1511
1570
|
try {
|
|
1512
1571
|
const { score, breakdown } = calculateQualityScore(db, session_id);
|
|
1513
|
-
if (score
|
|
1572
|
+
if (score !== 50) {
|
|
1514
1573
|
storeQualityScore(db, session_id, score, breakdown);
|
|
1515
1574
|
}
|
|
1516
1575
|
backfillQualityScores(db);
|
|
@@ -1683,18 +1742,18 @@ function groupEntriesIntoTurns(entries) {
|
|
|
1683
1742
|
if (currentTurn) {
|
|
1684
1743
|
turns.push(currentTurn);
|
|
1685
1744
|
}
|
|
1686
|
-
const
|
|
1687
|
-
if (
|
|
1745
|
+
const text = getTextFromBlocks(entry.message.content);
|
|
1746
|
+
if (text.trim()) {
|
|
1688
1747
|
currentTurn = {
|
|
1689
|
-
userPrompt:
|
|
1748
|
+
userPrompt: text.trim(),
|
|
1690
1749
|
assistantText: null,
|
|
1691
1750
|
toolCalls: []
|
|
1692
1751
|
};
|
|
1693
1752
|
}
|
|
1694
1753
|
} else if (entry.type === "assistant" && entry.message && currentTurn) {
|
|
1695
|
-
const
|
|
1696
|
-
if (
|
|
1697
|
-
currentTurn.assistantText = currentTurn.assistantText ? currentTurn.assistantText + "\n" +
|
|
1754
|
+
const text = getTextFromBlocks(entry.message.content);
|
|
1755
|
+
if (text.trim()) {
|
|
1756
|
+
currentTurn.assistantText = currentTurn.assistantText ? currentTurn.assistantText + "\n" + text.trim() : text.trim();
|
|
1698
1757
|
}
|
|
1699
1758
|
for (const block of entry.message.content) {
|
|
1700
1759
|
if (block.type === "tool_use") {
|
|
@@ -1764,14 +1823,14 @@ function extractFilesFromToolCall(toolName, input) {
|
|
|
1764
1823
|
return [];
|
|
1765
1824
|
}
|
|
1766
1825
|
function readStdin() {
|
|
1767
|
-
return new Promise((
|
|
1826
|
+
return new Promise((resolve4) => {
|
|
1768
1827
|
let data = "";
|
|
1769
1828
|
process.stdin.setEncoding("utf-8");
|
|
1770
1829
|
process.stdin.on("data", (chunk) => {
|
|
1771
1830
|
data += chunk;
|
|
1772
1831
|
});
|
|
1773
|
-
process.stdin.on("end", () =>
|
|
1774
|
-
setTimeout(() =>
|
|
1832
|
+
process.stdin.on("end", () => resolve4(data));
|
|
1833
|
+
setTimeout(() => resolve4(data), 5e3);
|
|
1775
1834
|
});
|
|
1776
1835
|
}
|
|
1777
1836
|
main();
|