@massu/core 1.15.1 → 1.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/cli.js +900 -572
- package/dist/hooks/auto-learning-pipeline.js +90 -19
- package/dist/hooks/classify-failure.js +112 -43
- package/dist/hooks/cost-tracker.js +108 -39
- package/dist/hooks/fix-detector.js +95 -24
- package/dist/hooks/incident-pipeline.js +114 -45
- package/dist/hooks/post-edit-context.js +105 -36
- package/dist/hooks/post-tool-use.js +137 -68
- package/dist/hooks/pre-compact.js +108 -39
- package/dist/hooks/pre-delete-check.js +107 -38
- package/dist/hooks/pre-tool-use-gate.js +113 -44
- package/dist/hooks/quality-event.js +108 -39
- package/dist/hooks/rule-enforcement-pipeline.js +92 -21
- package/dist/hooks/session-end.js +126 -57
- package/dist/hooks/session-start.js +165 -96
- package/dist/hooks/user-prompt.js +124 -55
- package/package.json +1 -1
|
@@ -4,12 +4,12 @@ import{createRequire as __cr}from"module";const require=__cr(import.meta.url);
|
|
|
4
4
|
// src/memory-db.ts
|
|
5
5
|
import Database from "better-sqlite3";
|
|
6
6
|
import { dirname as dirname2, basename } from "path";
|
|
7
|
-
import { existsSync as
|
|
7
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
8
8
|
|
|
9
9
|
// src/config.ts
|
|
10
|
-
import { resolve, dirname } from "path";
|
|
11
|
-
import { existsSync, readFileSync } from "fs";
|
|
12
|
-
import { homedir } from "os";
|
|
10
|
+
import { resolve as resolve2, dirname } from "path";
|
|
11
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
12
|
+
import { homedir as homedir2 } from "os";
|
|
13
13
|
import { parse as parseYaml } from "yaml";
|
|
14
14
|
import { z } from "zod";
|
|
15
15
|
|
|
@@ -18,6 +18,68 @@ function encodeMemoryDirName(projectRoot) {
|
|
|
18
18
|
return projectRoot.replace(/\//g, "-");
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// src/credentials.ts
|
|
22
|
+
import { homedir } from "os";
|
|
23
|
+
import { resolve } from "path";
|
|
24
|
+
import {
|
|
25
|
+
existsSync,
|
|
26
|
+
readFileSync,
|
|
27
|
+
writeFileSync,
|
|
28
|
+
mkdirSync,
|
|
29
|
+
rmSync,
|
|
30
|
+
chmodSync
|
|
31
|
+
} from "fs";
|
|
32
|
+
var MASSU_ENV_API_KEY = "MASSU_API_KEY";
|
|
33
|
+
var MASSU_ENV_CLOUD_ENDPOINT = "MASSU_CLOUD_ENDPOINT";
|
|
34
|
+
var DEFAULT_CLOUD_ENDPOINT = "https://api.massu.ai/v1";
|
|
35
|
+
function credentialsDir(home = homedir()) {
|
|
36
|
+
return resolve(home, ".massu");
|
|
37
|
+
}
|
|
38
|
+
function credentialsPath(home = homedir()) {
|
|
39
|
+
return resolve(credentialsDir(home), "credentials");
|
|
40
|
+
}
|
|
41
|
+
function readUserCredentials(home = homedir()) {
|
|
42
|
+
try {
|
|
43
|
+
const p = credentialsPath(home);
|
|
44
|
+
if (!existsSync(p)) return void 0;
|
|
45
|
+
const parsed = JSON.parse(readFileSync(p, "utf-8"));
|
|
46
|
+
const key = typeof parsed?.apiKey === "string" ? parsed.apiKey.trim() : "";
|
|
47
|
+
return key.length > 0 ? key : void 0;
|
|
48
|
+
} catch {
|
|
49
|
+
return void 0;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function isUnresolvedLiteral(v) {
|
|
53
|
+
return /^\$\{[^}]+\}$/.test(v);
|
|
54
|
+
}
|
|
55
|
+
function resolveApiKey(opts = {}) {
|
|
56
|
+
const env = opts.env ?? process.env;
|
|
57
|
+
const home = opts.home ?? homedir();
|
|
58
|
+
const cfg = typeof opts.configApiKey === "string" ? opts.configApiKey.trim() : "";
|
|
59
|
+
if (cfg.length > 0 && !isUnresolvedLiteral(cfg)) {
|
|
60
|
+
return { apiKey: cfg, source: "config" };
|
|
61
|
+
}
|
|
62
|
+
const envRaw = env[MASSU_ENV_API_KEY];
|
|
63
|
+
const envKey = typeof envRaw === "string" ? envRaw.trim() : "";
|
|
64
|
+
if (envKey.length > 0) {
|
|
65
|
+
return { apiKey: envKey, source: "env" };
|
|
66
|
+
}
|
|
67
|
+
const fileKey = readUserCredentials(home);
|
|
68
|
+
if (fileKey) {
|
|
69
|
+
return { apiKey: fileKey, source: "user-file" };
|
|
70
|
+
}
|
|
71
|
+
return { source: "none" };
|
|
72
|
+
}
|
|
73
|
+
function resolveEndpoint(opts = {}) {
|
|
74
|
+
const env = opts.env ?? process.env;
|
|
75
|
+
const cfg = typeof opts.configEndpoint === "string" ? opts.configEndpoint.trim() : "";
|
|
76
|
+
if (cfg.length > 0) return cfg;
|
|
77
|
+
const envRaw = env[MASSU_ENV_CLOUD_ENDPOINT];
|
|
78
|
+
const envEp = typeof envRaw === "string" ? envRaw.trim() : "";
|
|
79
|
+
if (envEp.length > 0) return envEp;
|
|
80
|
+
return DEFAULT_CLOUD_ENDPOINT;
|
|
81
|
+
}
|
|
82
|
+
|
|
21
83
|
// src/config.ts
|
|
22
84
|
var DomainConfigSchema = z.object({
|
|
23
85
|
name: z.string().default("Unknown"),
|
|
@@ -400,12 +462,13 @@ var RawConfigSchema = z.object({
|
|
|
400
462
|
lsp: LSPConfigSchema.optional()
|
|
401
463
|
}).passthrough();
|
|
402
464
|
var _config = null;
|
|
465
|
+
var _resolvedApiKeySource = "none";
|
|
403
466
|
var _projectRoot = null;
|
|
404
467
|
function findProjectRoot() {
|
|
405
468
|
const cwd = process.cwd();
|
|
406
469
|
let dir = cwd;
|
|
407
470
|
while (true) {
|
|
408
|
-
if (
|
|
471
|
+
if (existsSync2(resolve2(dir, "massu.config.yaml"))) {
|
|
409
472
|
return dir;
|
|
410
473
|
}
|
|
411
474
|
const parent = dirname(dir);
|
|
@@ -414,10 +477,10 @@ function findProjectRoot() {
|
|
|
414
477
|
}
|
|
415
478
|
dir = cwd;
|
|
416
479
|
while (true) {
|
|
417
|
-
if (
|
|
480
|
+
if (existsSync2(resolve2(dir, "package.json"))) {
|
|
418
481
|
return dir;
|
|
419
482
|
}
|
|
420
|
-
if (
|
|
483
|
+
if (existsSync2(resolve2(dir, ".git"))) {
|
|
421
484
|
return dir;
|
|
422
485
|
}
|
|
423
486
|
const parent = dirname(dir);
|
|
@@ -435,10 +498,10 @@ function getProjectRoot() {
|
|
|
435
498
|
function getConfig() {
|
|
436
499
|
if (_config) return _config;
|
|
437
500
|
const root = getProjectRoot();
|
|
438
|
-
const configPath =
|
|
501
|
+
const configPath = resolve2(root, "massu.config.yaml");
|
|
439
502
|
let rawYaml = {};
|
|
440
|
-
if (
|
|
441
|
-
const content =
|
|
503
|
+
if (existsSync2(configPath)) {
|
|
504
|
+
const content = readFileSync2(configPath, "utf-8");
|
|
442
505
|
rawYaml = parseYaml(content) ?? {};
|
|
443
506
|
}
|
|
444
507
|
const result = RawConfigSchema.safeParse(rawYaml);
|
|
@@ -455,7 +518,7 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
|
|
|
455
518
|
);
|
|
456
519
|
}
|
|
457
520
|
const parsed = result.data;
|
|
458
|
-
const projectRoot = parsed.project.root === "auto" || !parsed.project.root ? root :
|
|
521
|
+
const projectRoot = parsed.project.root === "auto" || !parsed.project.root ? root : resolve2(root, parsed.project.root);
|
|
459
522
|
const fw = parsed.framework;
|
|
460
523
|
let router = fw.router;
|
|
461
524
|
let orm = fw.orm;
|
|
@@ -513,13 +576,19 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
|
|
|
513
576
|
telemetry: parsed.telemetry,
|
|
514
577
|
lsp: parsed.lsp
|
|
515
578
|
};
|
|
516
|
-
|
|
579
|
+
const resolvedKey = resolveApiKey({ configApiKey: _config.cloud?.apiKey });
|
|
580
|
+
_resolvedApiKeySource = resolvedKey.source;
|
|
581
|
+
const resolvedEndpoint = resolveEndpoint({ configEndpoint: _config.cloud?.endpoint });
|
|
582
|
+
if (resolvedKey.apiKey) {
|
|
517
583
|
_config.cloud = {
|
|
518
584
|
enabled: true,
|
|
519
585
|
sync: { memory: true, analytics: true, audit: true },
|
|
520
586
|
..._config.cloud,
|
|
521
|
-
apiKey:
|
|
587
|
+
apiKey: resolvedKey.apiKey,
|
|
588
|
+
endpoint: resolvedEndpoint
|
|
522
589
|
};
|
|
590
|
+
} else if (_config.cloud) {
|
|
591
|
+
_config.cloud = { ..._config.cloud, endpoint: _config.cloud.endpoint ?? resolvedEndpoint };
|
|
523
592
|
}
|
|
524
593
|
return _config;
|
|
525
594
|
}
|
|
@@ -528,34 +597,34 @@ function getResolvedPaths() {
|
|
|
528
597
|
const root = getProjectRoot();
|
|
529
598
|
const claudeDirName = config.conventions?.claudeDirName ?? ".claude";
|
|
530
599
|
return {
|
|
531
|
-
codegraphDbPath:
|
|
532
|
-
dataDbPath:
|
|
533
|
-
prismaSchemaPath:
|
|
534
|
-
rootRouterPath:
|
|
535
|
-
routersDir:
|
|
536
|
-
srcDir:
|
|
600
|
+
codegraphDbPath: resolve2(root, ".codegraph/codegraph.db"),
|
|
601
|
+
dataDbPath: resolve2(root, ".massu/data.db"),
|
|
602
|
+
prismaSchemaPath: resolve2(root, config.paths.schema ?? "prisma/schema.prisma"),
|
|
603
|
+
rootRouterPath: resolve2(root, config.paths.routerRoot ?? "src/server/api/root.ts"),
|
|
604
|
+
routersDir: resolve2(root, config.paths.routers ?? "src/server/api/routers"),
|
|
605
|
+
srcDir: resolve2(root, config.paths.source),
|
|
537
606
|
pathAlias: Object.fromEntries(
|
|
538
607
|
Object.entries(config.paths.aliases).map(([alias, target]) => [
|
|
539
608
|
alias,
|
|
540
|
-
|
|
609
|
+
resolve2(root, target)
|
|
541
610
|
])
|
|
542
611
|
),
|
|
543
612
|
extensions: [".ts", ".tsx", ".js", ".jsx"],
|
|
544
613
|
indexFiles: ["index.ts", "index.tsx", "index.js", "index.jsx"],
|
|
545
|
-
patternsDir:
|
|
546
|
-
claudeMdPath:
|
|
547
|
-
docsMapPath:
|
|
548
|
-
helpSitePath:
|
|
549
|
-
memoryDbPath:
|
|
550
|
-
knowledgeDbPath:
|
|
551
|
-
plansDir:
|
|
552
|
-
docsDir:
|
|
553
|
-
claudeDir:
|
|
554
|
-
memoryDir:
|
|
555
|
-
sessionStatePath:
|
|
556
|
-
sessionArchivePath:
|
|
557
|
-
mcpJsonPath:
|
|
558
|
-
settingsLocalPath:
|
|
614
|
+
patternsDir: resolve2(root, claudeDirName, "patterns"),
|
|
615
|
+
claudeMdPath: resolve2(root, claudeDirName, "CLAUDE.md"),
|
|
616
|
+
docsMapPath: resolve2(root, ".massu/docs-map.json"),
|
|
617
|
+
helpSitePath: resolve2(root, "../" + config.project.name + "-help"),
|
|
618
|
+
memoryDbPath: resolve2(root, ".massu/memory.db"),
|
|
619
|
+
knowledgeDbPath: resolve2(root, ".massu/knowledge.db"),
|
|
620
|
+
plansDir: resolve2(root, "docs/plans"),
|
|
621
|
+
docsDir: resolve2(root, "docs"),
|
|
622
|
+
claudeDir: resolve2(root, claudeDirName),
|
|
623
|
+
memoryDir: resolve2(homedir2(), claudeDirName, "projects", encodeMemoryDirName(root), "memory"),
|
|
624
|
+
sessionStatePath: resolve2(root, config.conventions?.sessionStatePath ?? `${claudeDirName}/session-state/CURRENT.md`),
|
|
625
|
+
sessionArchivePath: resolve2(root, config.conventions?.sessionArchivePath ?? `${claudeDirName}/session-state/archive`),
|
|
626
|
+
mcpJsonPath: resolve2(root, ".mcp.json"),
|
|
627
|
+
settingsLocalPath: resolve2(root, claudeDirName, "settings.local.json")
|
|
559
628
|
};
|
|
560
629
|
}
|
|
561
630
|
|
|
@@ -563,8 +632,8 @@ function getResolvedPaths() {
|
|
|
563
632
|
function getMemoryDb() {
|
|
564
633
|
const dbPath = getResolvedPaths().memoryDbPath;
|
|
565
634
|
const dir = dirname2(dbPath);
|
|
566
|
-
if (!
|
|
567
|
-
|
|
635
|
+
if (!existsSync3(dir)) {
|
|
636
|
+
mkdirSync2(dir, { recursive: true });
|
|
568
637
|
}
|
|
569
638
|
const db = new Database(dbPath);
|
|
570
639
|
db.pragma("journal_mode = WAL");
|
|
@@ -1223,14 +1292,14 @@ async function main() {
|
|
|
1223
1292
|
process.exit(0);
|
|
1224
1293
|
}
|
|
1225
1294
|
function readStdin() {
|
|
1226
|
-
return new Promise((
|
|
1295
|
+
return new Promise((resolve4) => {
|
|
1227
1296
|
let data = "";
|
|
1228
1297
|
process.stdin.setEncoding("utf-8");
|
|
1229
1298
|
process.stdin.on("data", (chunk) => {
|
|
1230
1299
|
data += chunk;
|
|
1231
1300
|
});
|
|
1232
|
-
process.stdin.on("end", () =>
|
|
1233
|
-
setTimeout(() =>
|
|
1301
|
+
process.stdin.on("end", () => resolve4(data));
|
|
1302
|
+
setTimeout(() => resolve4(data), 3e3);
|
|
1234
1303
|
});
|
|
1235
1304
|
}
|
|
1236
1305
|
main();
|
|
@@ -9,15 +9,79 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
9
9
|
|
|
10
10
|
// src/hooks/fix-detector.ts
|
|
11
11
|
import { execFileSync } from "child_process";
|
|
12
|
-
import { existsSync as
|
|
12
|
+
import { existsSync as existsSync3, appendFileSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3 } from "fs";
|
|
13
13
|
import { tmpdir } from "os";
|
|
14
14
|
import { join } from "path";
|
|
15
15
|
|
|
16
16
|
// src/config.ts
|
|
17
|
-
import { resolve, dirname } from "path";
|
|
18
|
-
import { existsSync, readFileSync } from "fs";
|
|
17
|
+
import { resolve as resolve2, dirname } from "path";
|
|
18
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
19
19
|
import { parse as parseYaml } from "yaml";
|
|
20
20
|
import { z } from "zod";
|
|
21
|
+
|
|
22
|
+
// src/credentials.ts
|
|
23
|
+
import { homedir } from "os";
|
|
24
|
+
import { resolve } from "path";
|
|
25
|
+
import {
|
|
26
|
+
existsSync,
|
|
27
|
+
readFileSync,
|
|
28
|
+
writeFileSync,
|
|
29
|
+
mkdirSync,
|
|
30
|
+
rmSync,
|
|
31
|
+
chmodSync
|
|
32
|
+
} from "fs";
|
|
33
|
+
var MASSU_ENV_API_KEY = "MASSU_API_KEY";
|
|
34
|
+
var MASSU_ENV_CLOUD_ENDPOINT = "MASSU_CLOUD_ENDPOINT";
|
|
35
|
+
var DEFAULT_CLOUD_ENDPOINT = "https://api.massu.ai/v1";
|
|
36
|
+
function credentialsDir(home = homedir()) {
|
|
37
|
+
return resolve(home, ".massu");
|
|
38
|
+
}
|
|
39
|
+
function credentialsPath(home = homedir()) {
|
|
40
|
+
return resolve(credentialsDir(home), "credentials");
|
|
41
|
+
}
|
|
42
|
+
function readUserCredentials(home = homedir()) {
|
|
43
|
+
try {
|
|
44
|
+
const p = credentialsPath(home);
|
|
45
|
+
if (!existsSync(p)) return void 0;
|
|
46
|
+
const parsed = JSON.parse(readFileSync(p, "utf-8"));
|
|
47
|
+
const key = typeof parsed?.apiKey === "string" ? parsed.apiKey.trim() : "";
|
|
48
|
+
return key.length > 0 ? key : void 0;
|
|
49
|
+
} catch {
|
|
50
|
+
return void 0;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function isUnresolvedLiteral(v) {
|
|
54
|
+
return /^\$\{[^}]+\}$/.test(v);
|
|
55
|
+
}
|
|
56
|
+
function resolveApiKey(opts = {}) {
|
|
57
|
+
const env = opts.env ?? process.env;
|
|
58
|
+
const home = opts.home ?? homedir();
|
|
59
|
+
const cfg = typeof opts.configApiKey === "string" ? opts.configApiKey.trim() : "";
|
|
60
|
+
if (cfg.length > 0 && !isUnresolvedLiteral(cfg)) {
|
|
61
|
+
return { apiKey: cfg, source: "config" };
|
|
62
|
+
}
|
|
63
|
+
const envRaw = env[MASSU_ENV_API_KEY];
|
|
64
|
+
const envKey = typeof envRaw === "string" ? envRaw.trim() : "";
|
|
65
|
+
if (envKey.length > 0) {
|
|
66
|
+
return { apiKey: envKey, source: "env" };
|
|
67
|
+
}
|
|
68
|
+
const fileKey = readUserCredentials(home);
|
|
69
|
+
if (fileKey) {
|
|
70
|
+
return { apiKey: fileKey, source: "user-file" };
|
|
71
|
+
}
|
|
72
|
+
return { source: "none" };
|
|
73
|
+
}
|
|
74
|
+
function resolveEndpoint(opts = {}) {
|
|
75
|
+
const env = opts.env ?? process.env;
|
|
76
|
+
const cfg = typeof opts.configEndpoint === "string" ? opts.configEndpoint.trim() : "";
|
|
77
|
+
if (cfg.length > 0) return cfg;
|
|
78
|
+
const envRaw = env[MASSU_ENV_CLOUD_ENDPOINT];
|
|
79
|
+
const envEp = typeof envRaw === "string" ? envRaw.trim() : "";
|
|
80
|
+
if (envEp.length > 0) return envEp;
|
|
81
|
+
return DEFAULT_CLOUD_ENDPOINT;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/config.ts
|
|
21
85
|
var DomainConfigSchema = z.object({
|
|
22
86
|
name: z.string().default("Unknown"),
|
|
23
87
|
routers: z.array(z.string()).default([]),
|
|
@@ -399,12 +463,13 @@ var RawConfigSchema = z.object({
|
|
|
399
463
|
lsp: LSPConfigSchema.optional()
|
|
400
464
|
}).passthrough();
|
|
401
465
|
var _config = null;
|
|
466
|
+
var _resolvedApiKeySource = "none";
|
|
402
467
|
var _projectRoot = null;
|
|
403
468
|
function findProjectRoot() {
|
|
404
469
|
const cwd = process.cwd();
|
|
405
470
|
let dir = cwd;
|
|
406
471
|
while (true) {
|
|
407
|
-
if (
|
|
472
|
+
if (existsSync2(resolve2(dir, "massu.config.yaml"))) {
|
|
408
473
|
return dir;
|
|
409
474
|
}
|
|
410
475
|
const parent = dirname(dir);
|
|
@@ -413,10 +478,10 @@ function findProjectRoot() {
|
|
|
413
478
|
}
|
|
414
479
|
dir = cwd;
|
|
415
480
|
while (true) {
|
|
416
|
-
if (
|
|
481
|
+
if (existsSync2(resolve2(dir, "package.json"))) {
|
|
417
482
|
return dir;
|
|
418
483
|
}
|
|
419
|
-
if (
|
|
484
|
+
if (existsSync2(resolve2(dir, ".git"))) {
|
|
420
485
|
return dir;
|
|
421
486
|
}
|
|
422
487
|
const parent = dirname(dir);
|
|
@@ -434,10 +499,10 @@ function getProjectRoot() {
|
|
|
434
499
|
function getConfig() {
|
|
435
500
|
if (_config) return _config;
|
|
436
501
|
const root = getProjectRoot();
|
|
437
|
-
const configPath =
|
|
502
|
+
const configPath = resolve2(root, "massu.config.yaml");
|
|
438
503
|
let rawYaml = {};
|
|
439
|
-
if (
|
|
440
|
-
const content =
|
|
504
|
+
if (existsSync2(configPath)) {
|
|
505
|
+
const content = readFileSync2(configPath, "utf-8");
|
|
441
506
|
rawYaml = parseYaml(content) ?? {};
|
|
442
507
|
}
|
|
443
508
|
const result = RawConfigSchema.safeParse(rawYaml);
|
|
@@ -454,7 +519,7 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
|
|
|
454
519
|
);
|
|
455
520
|
}
|
|
456
521
|
const parsed = result.data;
|
|
457
|
-
const projectRoot = parsed.project.root === "auto" || !parsed.project.root ? root :
|
|
522
|
+
const projectRoot = parsed.project.root === "auto" || !parsed.project.root ? root : resolve2(root, parsed.project.root);
|
|
458
523
|
const fw = parsed.framework;
|
|
459
524
|
let router = fw.router;
|
|
460
525
|
let orm = fw.orm;
|
|
@@ -512,13 +577,19 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
|
|
|
512
577
|
telemetry: parsed.telemetry,
|
|
513
578
|
lsp: parsed.lsp
|
|
514
579
|
};
|
|
515
|
-
|
|
580
|
+
const resolvedKey = resolveApiKey({ configApiKey: _config.cloud?.apiKey });
|
|
581
|
+
_resolvedApiKeySource = resolvedKey.source;
|
|
582
|
+
const resolvedEndpoint = resolveEndpoint({ configEndpoint: _config.cloud?.endpoint });
|
|
583
|
+
if (resolvedKey.apiKey) {
|
|
516
584
|
_config.cloud = {
|
|
517
585
|
enabled: true,
|
|
518
586
|
sync: { memory: true, analytics: true, audit: true },
|
|
519
587
|
..._config.cloud,
|
|
520
|
-
apiKey:
|
|
588
|
+
apiKey: resolvedKey.apiKey,
|
|
589
|
+
endpoint: resolvedEndpoint
|
|
521
590
|
};
|
|
591
|
+
} else if (_config.cloud) {
|
|
592
|
+
_config.cloud = { ..._config.cloud, endpoint: _config.cloud.endpoint ?? resolvedEndpoint };
|
|
522
593
|
}
|
|
523
594
|
return _config;
|
|
524
595
|
}
|
|
@@ -572,15 +643,15 @@ var FIX_HEURISTICS = [
|
|
|
572
643
|
];
|
|
573
644
|
function getSessionFlagPath(sessionId) {
|
|
574
645
|
const dir = join(tmpdir(), "massu-auto-learning");
|
|
575
|
-
if (!
|
|
576
|
-
|
|
646
|
+
if (!existsSync3(dir)) {
|
|
647
|
+
mkdirSync2(dir, { recursive: true });
|
|
577
648
|
}
|
|
578
649
|
return join(dir, `fixes-${sessionId.slice(0, 12)}.jsonl`);
|
|
579
650
|
}
|
|
580
651
|
function _stateDir() {
|
|
581
652
|
const dir = join(tmpdir(), "massu-fix-detector-state");
|
|
582
|
-
if (!
|
|
583
|
-
|
|
653
|
+
if (!existsSync3(dir)) {
|
|
654
|
+
mkdirSync2(dir, { recursive: true });
|
|
584
655
|
}
|
|
585
656
|
return dir;
|
|
586
657
|
}
|
|
@@ -594,8 +665,8 @@ function _cwdHash(cwd) {
|
|
|
594
665
|
function isGitWorkTreeFast(cwd) {
|
|
595
666
|
try {
|
|
596
667
|
const cachePath = join(_stateDir(), `worktree-${_cwdHash(cwd)}.json`);
|
|
597
|
-
if (
|
|
598
|
-
const cached = JSON.parse(
|
|
668
|
+
if (existsSync3(cachePath)) {
|
|
669
|
+
const cached = JSON.parse(readFileSync3(cachePath, "utf-8"));
|
|
599
670
|
const ageMs = Date.now() - cached.ts;
|
|
600
671
|
if (ageMs < 36e5) return cached.isWorkTree;
|
|
601
672
|
}
|
|
@@ -622,7 +693,7 @@ function _disabledFlagPath(sessionId) {
|
|
|
622
693
|
}
|
|
623
694
|
function isSessionAutoDisabled(sessionId) {
|
|
624
695
|
try {
|
|
625
|
-
return
|
|
696
|
+
return existsSync3(_disabledFlagPath(sessionId));
|
|
626
697
|
} catch {
|
|
627
698
|
return false;
|
|
628
699
|
}
|
|
@@ -640,7 +711,7 @@ async function main() {
|
|
|
640
711
|
const input = await readStdin();
|
|
641
712
|
const hookInput = JSON.parse(input);
|
|
642
713
|
const filePath = hookInput.tool_input?.file_path;
|
|
643
|
-
if (!filePath || !
|
|
714
|
+
if (!filePath || !existsSync3(filePath)) {
|
|
644
715
|
process.exit(0);
|
|
645
716
|
return;
|
|
646
717
|
}
|
|
@@ -701,7 +772,7 @@ async function main() {
|
|
|
701
772
|
};
|
|
702
773
|
const flagPath = getSessionFlagPath(hookInput.session_id);
|
|
703
774
|
appendFileSync(flagPath, JSON.stringify(signal) + "\n");
|
|
704
|
-
const lines =
|
|
775
|
+
const lines = readFileSync3(flagPath, "utf-8").split("\n").filter(Boolean);
|
|
705
776
|
if (lines.length === 1) {
|
|
706
777
|
writeHookMessage(
|
|
707
778
|
`[Massu Auto-Learning] Bug fix detected in ${filePath} (signals: ${detected.join(", ")}). The auto-learning pipeline will prompt you at session end to create an incident report, derive a prevention rule, and add enforcement.`
|
|
@@ -712,14 +783,14 @@ async function main() {
|
|
|
712
783
|
process.exit(0);
|
|
713
784
|
}
|
|
714
785
|
function readStdin() {
|
|
715
|
-
return new Promise((
|
|
786
|
+
return new Promise((resolve3) => {
|
|
716
787
|
let data = "";
|
|
717
788
|
process.stdin.setEncoding("utf-8");
|
|
718
789
|
process.stdin.on("data", (chunk) => {
|
|
719
790
|
data += chunk;
|
|
720
791
|
});
|
|
721
|
-
process.stdin.on("end", () =>
|
|
722
|
-
setTimeout(() =>
|
|
792
|
+
process.stdin.on("end", () => resolve3(data));
|
|
793
|
+
setTimeout(() => resolve3(data), 3e3);
|
|
723
794
|
});
|
|
724
795
|
}
|
|
725
796
|
main();
|