finitemonkey 0.1.0-next.15 → 0.1.0-next.17
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/README.es.md +2 -1
- package/README.md +2 -1
- package/README.pt-BR.md +2 -1
- package/dist/finitemonkey.mjs +1705 -771
- package/package.json +1 -1
package/dist/finitemonkey.mjs
CHANGED
|
@@ -40,8 +40,8 @@ var parseJsonObject = (text6) => {
|
|
|
40
40
|
if (!parsed.ok) return parsed;
|
|
41
41
|
return isRecord(parsed.value) ? accept(parsed.value) : reject("expected a JSON object");
|
|
42
42
|
};
|
|
43
|
-
var optionalString = (
|
|
44
|
-
const value =
|
|
43
|
+
var optionalString = (record3, key) => {
|
|
44
|
+
const value = record3[key];
|
|
45
45
|
return typeof value === "string" ? value : void 0;
|
|
46
46
|
};
|
|
47
47
|
|
|
@@ -69,7 +69,7 @@ var resolveWindowsCommand = (command, env, exists) => {
|
|
|
69
69
|
}
|
|
70
70
|
return { command, shell: false };
|
|
71
71
|
};
|
|
72
|
-
var windowsCommandLine = (resolved,
|
|
72
|
+
var windowsCommandLine = (resolved, args2) => [quoteForCmd(resolved.command), ...args2.map(quoteForCmd)].join(" ");
|
|
73
73
|
|
|
74
74
|
// src/core/node-exec.ts
|
|
75
75
|
var SHELL_NOT_FOUND = /* @__PURE__ */ new Set([127, 9009]);
|
|
@@ -117,9 +117,9 @@ var nodeAppendText = (path, content) => {
|
|
|
117
117
|
return reject(error instanceof Error ? error.message : String(error));
|
|
118
118
|
}
|
|
119
119
|
};
|
|
120
|
-
var nodeSpawnSelf = (selfPath) => (
|
|
120
|
+
var nodeSpawnSelf = (selfPath) => (args2, cwd) => {
|
|
121
121
|
try {
|
|
122
|
-
const child2 = spawn(process.execPath, [selfPath, ...
|
|
122
|
+
const child2 = spawn(process.execPath, [selfPath, ...args2], {
|
|
123
123
|
cwd,
|
|
124
124
|
detached: true,
|
|
125
125
|
stdio: "ignore",
|
|
@@ -290,8 +290,8 @@ import { createInterface } from "node:readline";
|
|
|
290
290
|
var MAX_BUFFER2 = 256 * 1024 * 1024;
|
|
291
291
|
var GIT_ENV = { ...process.env, GIT_PAGER: "cat", GIT_TERMINAL_PROMPT: "0", LC_ALL: "C" };
|
|
292
292
|
var notFound = (error) => error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
293
|
-
var nodeGit = (
|
|
294
|
-
const result = spawnSync2("git", [...
|
|
293
|
+
var nodeGit = (args2, cwd) => {
|
|
294
|
+
const result = spawnSync2("git", [...args2], {
|
|
295
295
|
cwd,
|
|
296
296
|
encoding: "utf8",
|
|
297
297
|
maxBuffer: MAX_BUFFER2,
|
|
@@ -305,8 +305,8 @@ var nodeGit = (args, cwd) => {
|
|
|
305
305
|
stdout: typeof stdout === "string" ? stdout : ""
|
|
306
306
|
};
|
|
307
307
|
};
|
|
308
|
-
var nodeGitLines = (
|
|
309
|
-
const child2 = spawn2("git", [...
|
|
308
|
+
var nodeGitLines = (args2, cwd, onLine) => new Promise((resolve2) => {
|
|
309
|
+
const child2 = spawn2("git", [...args2], {
|
|
310
310
|
cwd,
|
|
311
311
|
windowsHide: true,
|
|
312
312
|
env: GIT_ENV,
|
|
@@ -323,8 +323,8 @@ var nodeGitLines = (args, cwd, onLine) => new Promise((resolve2) => {
|
|
|
323
323
|
});
|
|
324
324
|
});
|
|
325
325
|
var textOf = (value) => typeof value === "string" ? value : "";
|
|
326
|
-
var nodeGitWith = (
|
|
327
|
-
const result = spawnSync2("git", [...
|
|
326
|
+
var nodeGitWith = (args2, cwd, options) => {
|
|
327
|
+
const result = spawnSync2("git", [...args2], {
|
|
328
328
|
cwd,
|
|
329
329
|
encoding: "utf8",
|
|
330
330
|
maxBuffer: MAX_BUFFER2,
|
|
@@ -350,15 +350,15 @@ var errnoCode2 = (error) => {
|
|
|
350
350
|
if (!(error instanceof Error) || !("code" in error)) return void 0;
|
|
351
351
|
return typeof error.code === "string" ? error.code : void 0;
|
|
352
352
|
};
|
|
353
|
-
var spawnProbe = (command,
|
|
353
|
+
var spawnProbe = (command, args2, timeout) => {
|
|
354
354
|
const options = { encoding: "utf8", timeout, windowsHide: true };
|
|
355
|
-
return process.platform === "win32" ? spawnSync3([command, ...
|
|
355
|
+
return process.platform === "win32" ? spawnSync3([command, ...args2].join(" "), { ...options, shell: true }) : spawnSync3(command, [...args2], options);
|
|
356
356
|
};
|
|
357
|
-
var nodeProbe = (command,
|
|
358
|
-
if (![command, ...
|
|
357
|
+
var nodeProbe = (command, args2, timeoutMs) => {
|
|
358
|
+
if (![command, ...args2].every((token) => SAFE_TOKEN.test(token))) {
|
|
359
359
|
return { found: false, ok: false, stdout: "", timedOut: false };
|
|
360
360
|
}
|
|
361
|
-
const result = spawnProbe(command,
|
|
361
|
+
const result = spawnProbe(command, args2, timeoutMs);
|
|
362
362
|
const code = errnoCode2(result.error);
|
|
363
363
|
const stdout = result.stdout;
|
|
364
364
|
return {
|
|
@@ -409,14 +409,155 @@ var nodeRuntime = () => ({
|
|
|
409
409
|
exec: nodeExec,
|
|
410
410
|
appendText: nodeAppendText,
|
|
411
411
|
spawnSelf: nodeSpawnSelf(SELF_PATH),
|
|
412
|
+
execSelf: (args2, cwd, timeoutMs) => nodeExec({ command: process.execPath, args: [SELF_PATH, ...args2], cwd, timeoutMs }),
|
|
412
413
|
gitWith: nodeGitWith,
|
|
413
414
|
realpath: nodeRealpath,
|
|
414
415
|
homeDir: homedir,
|
|
415
416
|
systemLocale: () => Intl.DateTimeFormat().resolvedOptions().locale
|
|
416
417
|
});
|
|
417
418
|
|
|
418
|
-
// src/
|
|
419
|
-
import {
|
|
419
|
+
// src/app/node-process.ts
|
|
420
|
+
import { spawn as spawn3, spawnSync as spawnSync4 } from "node:child_process";
|
|
421
|
+
import { connect } from "node:net";
|
|
422
|
+
var MAX_OUTPUT = 64 * 1024;
|
|
423
|
+
var POLL_MS = 150;
|
|
424
|
+
var STOP_GRACE_MS = 3e3;
|
|
425
|
+
var sleep = (ms) => new Promise((resolve2) => {
|
|
426
|
+
setTimeout(resolve2, ms);
|
|
427
|
+
});
|
|
428
|
+
var portOpen = (host, port) => new Promise((resolve2) => {
|
|
429
|
+
const socket = connect({ host, port });
|
|
430
|
+
const done = (open) => {
|
|
431
|
+
socket.destroy();
|
|
432
|
+
resolve2(open);
|
|
433
|
+
};
|
|
434
|
+
socket.setTimeout(1e3, () => {
|
|
435
|
+
done(false);
|
|
436
|
+
});
|
|
437
|
+
socket.once("connect", () => {
|
|
438
|
+
done(true);
|
|
439
|
+
});
|
|
440
|
+
socket.once("error", () => {
|
|
441
|
+
done(false);
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
var startApp = (command, cwd, port) => {
|
|
445
|
+
const child2 = spawn3(command, {
|
|
446
|
+
cwd,
|
|
447
|
+
shell: true,
|
|
448
|
+
detached: process.platform !== "win32",
|
|
449
|
+
windowsHide: true,
|
|
450
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
451
|
+
env: { ...process.env, PORT: String(port) }
|
|
452
|
+
});
|
|
453
|
+
let output = "";
|
|
454
|
+
let exit;
|
|
455
|
+
const collect4 = (chunk) => {
|
|
456
|
+
output = `${output}${chunk.toString("utf8")}`.slice(-MAX_OUTPUT);
|
|
457
|
+
};
|
|
458
|
+
child2.stdout.on("data", collect4);
|
|
459
|
+
child2.stderr.on("data", collect4);
|
|
460
|
+
child2.once("error", (error) => {
|
|
461
|
+
output = `${output}${error.message}`;
|
|
462
|
+
exit = { code: null };
|
|
463
|
+
});
|
|
464
|
+
child2.once("exit", (code) => {
|
|
465
|
+
exit = { code };
|
|
466
|
+
});
|
|
467
|
+
return { child: child2, output: () => output, exited: () => exit };
|
|
468
|
+
};
|
|
469
|
+
var waitForPort = async (app, host, port, deadline) => {
|
|
470
|
+
while (Date.now() < deadline) {
|
|
471
|
+
if (await portOpen(host, port)) return "open";
|
|
472
|
+
if (app.exited() !== void 0) return "exited";
|
|
473
|
+
await sleep(POLL_MS);
|
|
474
|
+
}
|
|
475
|
+
return "timeout";
|
|
476
|
+
};
|
|
477
|
+
var signalGroup = (pid, signal) => {
|
|
478
|
+
try {
|
|
479
|
+
process.kill(-pid, signal);
|
|
480
|
+
} catch {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
var alive = (pid) => {
|
|
485
|
+
try {
|
|
486
|
+
process.kill(-pid, 0);
|
|
487
|
+
return true;
|
|
488
|
+
} catch {
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
var stopApp = async (app) => {
|
|
493
|
+
const pid = app.child.pid;
|
|
494
|
+
if (pid === void 0) return true;
|
|
495
|
+
if (process.platform === "win32") {
|
|
496
|
+
spawnSync4("taskkill", ["/pid", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore" });
|
|
497
|
+
return true;
|
|
498
|
+
}
|
|
499
|
+
signalGroup(pid, "SIGTERM");
|
|
500
|
+
const deadline = Date.now() + STOP_GRACE_MS;
|
|
501
|
+
while (alive(pid) && Date.now() < deadline) await sleep(POLL_MS);
|
|
502
|
+
if (alive(pid)) signalGroup(pid, "SIGKILL");
|
|
503
|
+
return !alive(pid);
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
// src/app/node-probe.ts
|
|
507
|
+
var BODY_EXCERPT = 200;
|
|
508
|
+
var MAX_BODY = 256 * 1024;
|
|
509
|
+
var url = (request) => {
|
|
510
|
+
const host = request.host.includes(":") ? `[${request.host}]` : request.host;
|
|
511
|
+
return `http://${host}:${String(request.port)}${request.route.startsWith("/") ? "" : "/"}${request.route}`;
|
|
512
|
+
};
|
|
513
|
+
var ask = async (request, deadline) => {
|
|
514
|
+
try {
|
|
515
|
+
const response = await fetch(url(request), {
|
|
516
|
+
redirect: "manual",
|
|
517
|
+
signal: AbortSignal.timeout(Math.max(1e3, deadline - Date.now()))
|
|
518
|
+
});
|
|
519
|
+
const body2 = (await response.text()).slice(0, MAX_BODY);
|
|
520
|
+
const excerpt = body2.slice(0, BODY_EXCERPT);
|
|
521
|
+
if (response.status !== request.status)
|
|
522
|
+
return { kind: "wrong_status", status: response.status, bodyExcerpt: excerpt };
|
|
523
|
+
if (request.body !== void 0 && !body2.includes(request.body)) {
|
|
524
|
+
return { kind: "body_mismatch", status: response.status, bodyExcerpt: excerpt };
|
|
525
|
+
}
|
|
526
|
+
return { kind: "ok", status: response.status };
|
|
527
|
+
} catch (error) {
|
|
528
|
+
return { kind: "request_failed", bodyExcerpt: error instanceof Error ? error.message : String(error) };
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
var finish = async (app, answer, started) => {
|
|
532
|
+
const stopped = app === void 0 ? true : await stopApp(app);
|
|
533
|
+
return {
|
|
534
|
+
...answer,
|
|
535
|
+
reused: app === void 0,
|
|
536
|
+
output: app?.output() ?? "",
|
|
537
|
+
stopped,
|
|
538
|
+
durationMs: Date.now() - started
|
|
539
|
+
};
|
|
540
|
+
};
|
|
541
|
+
var probeApp = async (request) => {
|
|
542
|
+
const started = Date.now();
|
|
543
|
+
const deadline = started + request.timeoutMs;
|
|
544
|
+
if (await portOpen(request.host, request.port)) return finish(void 0, await ask(request, deadline), started);
|
|
545
|
+
if (request.start === void 0) return finish(void 0, { kind: "no_start" }, started);
|
|
546
|
+
const app = startApp(request.start, request.cwd, request.port);
|
|
547
|
+
const port = await waitForPort(app, request.host, request.port, deadline);
|
|
548
|
+
if (port === "exited") return finish(app, { kind: "crashed", exitCode: app.exited()?.code ?? null }, started);
|
|
549
|
+
if (port === "timeout") return finish(app, { kind: "no_port" }, started);
|
|
550
|
+
return finish(app, await ask(request, deadline), started);
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
// src/app/types.ts
|
|
554
|
+
var DEFAULT_HOST = "127.0.0.1";
|
|
555
|
+
var DEFAULT_ROUTE = "/";
|
|
556
|
+
var DEFAULT_STATUS = 200;
|
|
557
|
+
var DEFAULT_APP_TIMEOUT_S = 60;
|
|
558
|
+
|
|
559
|
+
// src/core/args.ts
|
|
560
|
+
import { parseArgs } from "node:util";
|
|
420
561
|
|
|
421
562
|
// src/exit-codes.ts
|
|
422
563
|
var ExitCode = {
|
|
@@ -444,6 +585,89 @@ var exitCodeDocs = {
|
|
|
444
585
|
5: "exit.internal"
|
|
445
586
|
};
|
|
446
587
|
|
|
588
|
+
// src/core/problem.ts
|
|
589
|
+
var usageProblem = (code, messageKey, fix, params2 = {}) => ({
|
|
590
|
+
code,
|
|
591
|
+
messageKey,
|
|
592
|
+
params: params2,
|
|
593
|
+
fix,
|
|
594
|
+
exitCode: ExitCode.Usage
|
|
595
|
+
});
|
|
596
|
+
var needsPersonProblem = (messageKey, fix, params2 = {}) => ({
|
|
597
|
+
code: "needs_confirmation",
|
|
598
|
+
messageKey,
|
|
599
|
+
params: params2,
|
|
600
|
+
fix,
|
|
601
|
+
exitCode: ExitCode.NeedsPerson
|
|
602
|
+
});
|
|
603
|
+
var FmError = class extends Error {
|
|
604
|
+
problem;
|
|
605
|
+
constructor(problem4) {
|
|
606
|
+
super(problem4.code);
|
|
607
|
+
this.name = "FmError";
|
|
608
|
+
this.problem = problem4;
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
// src/core/args.ts
|
|
613
|
+
var GLOBAL_OPTIONS = {
|
|
614
|
+
json: { type: "boolean" },
|
|
615
|
+
lang: { type: "string" },
|
|
616
|
+
yes: { type: "boolean", short: "y" },
|
|
617
|
+
"dry-run": { type: "boolean" },
|
|
618
|
+
version: { type: "boolean", short: "v" },
|
|
619
|
+
help: { type: "boolean", short: "h" }
|
|
620
|
+
};
|
|
621
|
+
var flag = (values, name) => values[name] === true;
|
|
622
|
+
var looseArgs = (argv) => {
|
|
623
|
+
const { values, positionals } = parseArgs({
|
|
624
|
+
args: [...argv],
|
|
625
|
+
options: GLOBAL_OPTIONS,
|
|
626
|
+
strict: false,
|
|
627
|
+
allowPositionals: true
|
|
628
|
+
});
|
|
629
|
+
const lang = values.lang;
|
|
630
|
+
return {
|
|
631
|
+
positionals,
|
|
632
|
+
json: flag(values, "json"),
|
|
633
|
+
yes: flag(values, "yes"),
|
|
634
|
+
dryRun: flag(values, "dry-run"),
|
|
635
|
+
version: flag(values, "version"),
|
|
636
|
+
help: flag(values, "help"),
|
|
637
|
+
lang: typeof lang === "string" ? lang : void 0
|
|
638
|
+
};
|
|
639
|
+
};
|
|
640
|
+
var resolveCommand = (registry, positionals) => registry.filter((spec2) => spec2.path.every((part2, index) => positionals[index] === part2)).reduce(
|
|
641
|
+
(best, spec2) => best === void 0 || spec2.path.length > best.consumed ? { spec: spec2, consumed: spec2.path.length } : best,
|
|
642
|
+
void 0
|
|
643
|
+
);
|
|
644
|
+
var strictArgs = (argv, spec2) => {
|
|
645
|
+
const fix = `finitemonkey help ${spec2.path.join(" ")}`;
|
|
646
|
+
let parsed;
|
|
647
|
+
try {
|
|
648
|
+
parsed = parseArgs({
|
|
649
|
+
args: [...argv],
|
|
650
|
+
options: { ...GLOBAL_OPTIONS, ...spec2.options },
|
|
651
|
+
strict: true,
|
|
652
|
+
allowPositionals: true
|
|
653
|
+
});
|
|
654
|
+
} catch (error) {
|
|
655
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
656
|
+
return reject(usageProblem("invalid_arguments", "error.invalid_arguments", fix, { detail }));
|
|
657
|
+
}
|
|
658
|
+
const rest = parsed.positionals.slice(spec2.path.length);
|
|
659
|
+
const max = spec2.maxPositionals ?? 0;
|
|
660
|
+
const extra = rest[max];
|
|
661
|
+
if (extra !== void 0) {
|
|
662
|
+
return reject(usageProblem("unexpected_argument", "error.unexpected_argument", fix, { argument: extra }));
|
|
663
|
+
}
|
|
664
|
+
return accept({ values: parsed.values, positionals: rest });
|
|
665
|
+
};
|
|
666
|
+
var stringOption = (values, name) => {
|
|
667
|
+
const value = values[name];
|
|
668
|
+
return typeof value === "string" ? value : void 0;
|
|
669
|
+
};
|
|
670
|
+
|
|
447
671
|
// src/core/result.ts
|
|
448
672
|
var SCHEMA_VERSION = 1;
|
|
449
673
|
var success = (data, human2 = []) => ({
|
|
@@ -468,6 +692,87 @@ var toEnvelope = (command, result) => ({
|
|
|
468
692
|
...result.next === void 0 ? {} : { next: result.next }
|
|
469
693
|
});
|
|
470
694
|
|
|
695
|
+
// src/commands/app-check.ts
|
|
696
|
+
var APP_CHECK_SCHEMA = "finitemonkey/app-check@1";
|
|
697
|
+
var positive = (raw, fallback) => {
|
|
698
|
+
if (raw === void 0) return fallback;
|
|
699
|
+
const value = Number(raw);
|
|
700
|
+
return Number.isInteger(value) && value > 0 ? value : void 0;
|
|
701
|
+
};
|
|
702
|
+
var requestFrom = (ctx, values) => {
|
|
703
|
+
const port = positive(stringOption(values, "port"), 0);
|
|
704
|
+
const status = positive(stringOption(values, "status"), DEFAULT_STATUS);
|
|
705
|
+
const timeout = positive(stringOption(values, "timeout"), DEFAULT_APP_TIMEOUT_S);
|
|
706
|
+
if (port === void 0 || port === 0 || port > 65535 || status === void 0 || timeout === void 0)
|
|
707
|
+
return void 0;
|
|
708
|
+
return {
|
|
709
|
+
host: stringOption(values, "host") ?? DEFAULT_HOST,
|
|
710
|
+
port,
|
|
711
|
+
route: stringOption(values, "route") ?? DEFAULT_ROUTE,
|
|
712
|
+
status,
|
|
713
|
+
body: stringOption(values, "body"),
|
|
714
|
+
start: stringOption(values, "start"),
|
|
715
|
+
cwd: ctx.cwd,
|
|
716
|
+
timeoutMs: timeout * 1e3
|
|
717
|
+
};
|
|
718
|
+
};
|
|
719
|
+
var humanLine = (ctx, data) => ctx.t(`app.check.${data.kind}`, {
|
|
720
|
+
url: data.url,
|
|
721
|
+
status: data.status ?? 0,
|
|
722
|
+
code: data.exitCode ?? -1
|
|
723
|
+
});
|
|
724
|
+
var toResult = (ctx, request, data) => {
|
|
725
|
+
const human2 = [humanLine(ctx, data)];
|
|
726
|
+
if (data.kind === "ok") return success(data, human2);
|
|
727
|
+
return {
|
|
728
|
+
ok: false,
|
|
729
|
+
exitCode: ExitCode.CheckFailed,
|
|
730
|
+
data,
|
|
731
|
+
human: human2,
|
|
732
|
+
error: {
|
|
733
|
+
code: "app_check_failed",
|
|
734
|
+
message: human2.join(" "),
|
|
735
|
+
fix: `finitemonkey app check --port ${String(request.port)}`
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
};
|
|
739
|
+
var appCheckCommand = {
|
|
740
|
+
path: ["app", "check"],
|
|
741
|
+
summaryKey: "command.app_check.summary",
|
|
742
|
+
usage: "finitemonkey app check --port <n> [--start <command>] [--route <path>] [--status <code>] [--body <text>] [--timeout <seconds>] [--host <host>]",
|
|
743
|
+
status: "ready",
|
|
744
|
+
options: {
|
|
745
|
+
port: { type: "string" },
|
|
746
|
+
start: { type: "string" },
|
|
747
|
+
route: { type: "string" },
|
|
748
|
+
status: { type: "string" },
|
|
749
|
+
body: { type: "string" },
|
|
750
|
+
timeout: { type: "string" },
|
|
751
|
+
host: { type: "string" }
|
|
752
|
+
},
|
|
753
|
+
run: async (ctx, args2) => {
|
|
754
|
+
const request = requestFrom(ctx, args2.values);
|
|
755
|
+
if (request === void 0) {
|
|
756
|
+
return {
|
|
757
|
+
ok: false,
|
|
758
|
+
exitCode: ExitCode.Usage,
|
|
759
|
+
human: [ctx.t("app.check.usage")],
|
|
760
|
+
error: {
|
|
761
|
+
code: "app_check_failed",
|
|
762
|
+
message: ctx.t("app.check.usage"),
|
|
763
|
+
fix: "finitemonkey app check --port 3000"
|
|
764
|
+
}
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
const result = await probeApp(request);
|
|
768
|
+
const url2 = `http://${request.host}:${String(request.port)}${request.route}`;
|
|
769
|
+
return toResult(ctx, request, { schema: APP_CHECK_SCHEMA, url: url2, ...result });
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
|
|
773
|
+
// src/commands/doctor.ts
|
|
774
|
+
import { dirname as dirname7 } from "node:path";
|
|
775
|
+
|
|
471
776
|
// src/install/apply.ts
|
|
472
777
|
import { dirname as dirname3 } from "node:path";
|
|
473
778
|
|
|
@@ -512,12 +817,12 @@ var parseAdded = (value) => {
|
|
|
512
817
|
const deny2 = strings(value["deny"]);
|
|
513
818
|
const { schema, attribution } = value;
|
|
514
819
|
if (deny2 === void 0 || typeof schema !== "boolean" || typeof attribution !== "boolean") return void 0;
|
|
515
|
-
const
|
|
820
|
+
const ask2 = strings(value["ask"]) ?? [];
|
|
516
821
|
const blockReads = value["blockReads"] === true;
|
|
517
822
|
const marketplace = value["marketplace"] === true;
|
|
518
823
|
const plugin = value["plugin"] === true;
|
|
519
824
|
const language = value["language"];
|
|
520
|
-
const added = { schema, attribution, deny: deny2, ask, blockReads, marketplace, plugin };
|
|
825
|
+
const added = { schema, attribution, deny: deny2, ask: ask2, blockReads, marketplace, plugin };
|
|
521
826
|
return typeof language === "string" ? { ...added, language } : added;
|
|
522
827
|
};
|
|
523
828
|
var parseEntry = (value) => {
|
|
@@ -544,11 +849,11 @@ var parseEntries = (value) => {
|
|
|
544
849
|
const entries = value.map(parseEntry);
|
|
545
850
|
return entries.every((entry2) => entry2 !== void 0) ? entries : void 0;
|
|
546
851
|
};
|
|
547
|
-
var fromRecord = (
|
|
548
|
-
const { schema, version, preset, installedAt, updatedAt } =
|
|
852
|
+
var fromRecord = (record3) => {
|
|
853
|
+
const { schema, version, preset, installedAt, updatedAt } = record3;
|
|
549
854
|
const known2 = PRESETS.find((item) => item === preset);
|
|
550
|
-
const entries = parseEntries(
|
|
551
|
-
const createdDirs = strings(
|
|
855
|
+
const entries = parseEntries(record3["entries"]);
|
|
856
|
+
const createdDirs = strings(record3["createdDirs"]);
|
|
552
857
|
if (schema !== LOCK_SCHEMA) return reject(`schema is not ${LOCK_SCHEMA}`);
|
|
553
858
|
if (typeof version !== "string" || typeof installedAt !== "string" || typeof updatedAt !== "string") {
|
|
554
859
|
return reject("version, installedAt and updatedAt must be text");
|
|
@@ -565,6 +870,134 @@ var parseLock = (text6) => {
|
|
|
565
870
|
var renderLock = (lock) => `${JSON.stringify(lock, null, 2)}
|
|
566
871
|
`;
|
|
567
872
|
|
|
873
|
+
// src/policy/policy.ts
|
|
874
|
+
import { join as join3 } from "node:path";
|
|
875
|
+
|
|
876
|
+
// src/git/project-config.ts
|
|
877
|
+
import { join as join2 } from "node:path";
|
|
878
|
+
var CONFIG_FILE2 = ".finitemonkey/config.yml";
|
|
879
|
+
var unquote = (value) => value.trim().replace(/^(["'])(.*)\1$/s, "$2");
|
|
880
|
+
var stripComment = (line2) => {
|
|
881
|
+
let quote;
|
|
882
|
+
for (let index = 0; index < line2.length; index += 1) {
|
|
883
|
+
const char = line2.charAt(index);
|
|
884
|
+
if (quote === void 0 && (char === '"' || char === "'")) quote = char;
|
|
885
|
+
else if (char === quote) quote = void 0;
|
|
886
|
+
else if (quote === void 0 && char === "#" && (index === 0 || /\s/.test(line2.charAt(index - 1)))) {
|
|
887
|
+
return line2.slice(0, index);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return line2;
|
|
891
|
+
};
|
|
892
|
+
var flowList = (value) => {
|
|
893
|
+
const trimmed = value.trim();
|
|
894
|
+
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return void 0;
|
|
895
|
+
return trimmed.slice(1, -1).split(",").map(unquote).filter((item) => item !== "");
|
|
896
|
+
};
|
|
897
|
+
var keyValue = (body2) => {
|
|
898
|
+
const match = /^("[^"]*"|'[^']*'|[^:]+?)\s*:(?:\s+(.*))?$/.exec(body2);
|
|
899
|
+
if (match === null) return void 0;
|
|
900
|
+
return { key: unquote(match[1] ?? ""), value: match[2] ?? "" };
|
|
901
|
+
};
|
|
902
|
+
var addItem = (frame, item) => {
|
|
903
|
+
if (frame.key === void 0) return;
|
|
904
|
+
const parent = frame.tree[frame.key];
|
|
905
|
+
if (Array.isArray(parent)) parent.push(item);
|
|
906
|
+
else frame.tree[frame.key] = [item];
|
|
907
|
+
};
|
|
908
|
+
var placeLine = (stack, indent, body2) => {
|
|
909
|
+
while (stack.length > 1 && (stack[stack.length - 1]?.indent ?? 0) >= indent && !body2.startsWith("- ")) stack.pop();
|
|
910
|
+
const top = stack[stack.length - 1];
|
|
911
|
+
if (top === void 0) return;
|
|
912
|
+
if (body2.startsWith("- ")) {
|
|
913
|
+
addItem(top, unquote(body2.slice(2)));
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
const pair = keyValue(body2);
|
|
917
|
+
if (pair === void 0) return;
|
|
918
|
+
const parentTree = top.key === void 0 ? top.tree : ensureTree(top.tree, top.key);
|
|
919
|
+
if (pair.value === "") {
|
|
920
|
+
stack.push({ indent, tree: parentTree, key: pair.key });
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
parentTree[pair.key] = flowList(pair.value) ?? unquote(pair.value);
|
|
924
|
+
};
|
|
925
|
+
var ensureTree = (tree, key) => {
|
|
926
|
+
const existing = tree[key];
|
|
927
|
+
if (existing !== void 0 && typeof existing !== "string" && !Array.isArray(existing)) return existing;
|
|
928
|
+
const fresh3 = {};
|
|
929
|
+
tree[key] = fresh3;
|
|
930
|
+
return fresh3;
|
|
931
|
+
};
|
|
932
|
+
var parseConfigYaml = (text6) => {
|
|
933
|
+
const root = {};
|
|
934
|
+
const stack = [{ indent: -1, tree: root, key: void 0 }];
|
|
935
|
+
for (const raw of text6.split(/\r?\n/)) {
|
|
936
|
+
const line2 = stripComment(raw).trimEnd();
|
|
937
|
+
if (line2.trim() === "" || line2.trim() === "---") continue;
|
|
938
|
+
const indent = line2.length - line2.trimStart().length;
|
|
939
|
+
placeLine(stack, indent, line2.trim());
|
|
940
|
+
}
|
|
941
|
+
return root;
|
|
942
|
+
};
|
|
943
|
+
var isTree = (node) => typeof node === "object" && !Array.isArray(node);
|
|
944
|
+
var configAt = (tree, ...path) => path.reduce((node, key) => isTree(node) ? node[key] : void 0, tree);
|
|
945
|
+
var configList = (tree, ...path) => {
|
|
946
|
+
const node = configAt(tree, ...path);
|
|
947
|
+
if (typeof node === "string") return node === "" ? [] : [node];
|
|
948
|
+
return Array.isArray(node) ? node.filter((item) => typeof item === "string") : [];
|
|
949
|
+
};
|
|
950
|
+
var configMap = (tree, ...path) => {
|
|
951
|
+
const node = configAt(tree, ...path);
|
|
952
|
+
if (!isTree(node)) return {};
|
|
953
|
+
return Object.fromEntries(
|
|
954
|
+
Object.entries(node).filter((entry2) => typeof entry2[1] === "string")
|
|
955
|
+
);
|
|
956
|
+
};
|
|
957
|
+
var loadProjectConfig = (runtime, root) => {
|
|
958
|
+
const text6 = runtime.readText(toAbsolutePath(join2(root, CONFIG_FILE2)));
|
|
959
|
+
return text6.ok ? parseConfigYaml(text6.value) : {};
|
|
960
|
+
};
|
|
961
|
+
|
|
962
|
+
// src/policy/policy.ts
|
|
963
|
+
var POLICY_FILE = ".finitemonkey/policy.yml";
|
|
964
|
+
var GUARD_MODES = ["off", "shadow", "active"];
|
|
965
|
+
var LEVELS = ["light", "standard", "strict"];
|
|
966
|
+
var SEVERITIES = ["low", "medium", "high"];
|
|
967
|
+
var DEFAULT_PROMOTE_AFTER = 20;
|
|
968
|
+
var OPTIONAL_GUARDS = ["prose"];
|
|
969
|
+
var DEFAULT_POLICY = {
|
|
970
|
+
exists: false,
|
|
971
|
+
level: "standard",
|
|
972
|
+
severity: "medium",
|
|
973
|
+
promoteAfter: DEFAULT_PROMOTE_AFTER,
|
|
974
|
+
guards: {}
|
|
975
|
+
};
|
|
976
|
+
var pick = (options, value, fallback) => options.find((option) => option === value) ?? fallback;
|
|
977
|
+
var parsePolicy = (text6) => {
|
|
978
|
+
const tree = parseConfigYaml(text6);
|
|
979
|
+
const raw = configAt(tree, "promoteAfter");
|
|
980
|
+
const promoteAfter = typeof raw === "string" ? Number.parseInt(raw, 10) : Number.NaN;
|
|
981
|
+
const guards = Object.fromEntries(
|
|
982
|
+
Object.entries(configMap(tree, "guards")).flatMap(([id, mode]) => {
|
|
983
|
+
const known2 = GUARD_MODES.find((option) => option === mode);
|
|
984
|
+
return known2 === void 0 ? [] : [[id, known2]];
|
|
985
|
+
})
|
|
986
|
+
);
|
|
987
|
+
return {
|
|
988
|
+
exists: true,
|
|
989
|
+
level: pick(LEVELS, configAt(tree, "level"), DEFAULT_POLICY.level),
|
|
990
|
+
severity: pick(SEVERITIES, configAt(tree, "severity"), DEFAULT_POLICY.severity),
|
|
991
|
+
promoteAfter: Number.isInteger(promoteAfter) && promoteAfter > 0 ? promoteAfter : DEFAULT_PROMOTE_AFTER,
|
|
992
|
+
guards
|
|
993
|
+
};
|
|
994
|
+
};
|
|
995
|
+
var policyPath = (root) => toAbsolutePath(join3(root, POLICY_FILE));
|
|
996
|
+
var loadPolicy = (runtime, root) => {
|
|
997
|
+
const text6 = runtime.readText(policyPath(root));
|
|
998
|
+
return text6.ok ? parsePolicy(text6.value) : DEFAULT_POLICY;
|
|
999
|
+
};
|
|
1000
|
+
|
|
568
1001
|
// src/install/managed-block.ts
|
|
569
1002
|
var lineBreak = (text6) => text6.includes("\r\n") ? "\r\n" : "\n";
|
|
570
1003
|
var blockRange = (lines3, markers) => {
|
|
@@ -594,6 +1027,11 @@ var withoutManagedBlock = (text6, markers) => {
|
|
|
594
1027
|
// src/install/templates/en.ts
|
|
595
1028
|
var en = {
|
|
596
1029
|
configHeader: "Finite Monkey project settings. Commit this file. `finitemonkey update` keeps your edits.",
|
|
1030
|
+
policyHeader: [
|
|
1031
|
+
"Guard policy for this project. Modes: off, shadow (record what it would block), active.",
|
|
1032
|
+
"The guards for secrets, production, lost git work and false done stay active whatever this file says.",
|
|
1033
|
+
"A guard missing from this list starts in shadow and is promoted after promoteAfter firings without a false positive."
|
|
1034
|
+
],
|
|
597
1035
|
agentsBlock: ({ gate, doctor }) => [
|
|
598
1036
|
"## Finite Monkey",
|
|
599
1037
|
"",
|
|
@@ -616,6 +1054,11 @@ var en = {
|
|
|
616
1054
|
// src/install/templates/es.ts
|
|
617
1055
|
var es = {
|
|
618
1056
|
configHeader: "Configuración de Finite Monkey en este proyecto. Versiona este archivo. `finitemonkey update` conserva tus cambios.",
|
|
1057
|
+
policyHeader: [
|
|
1058
|
+
"Política de los guards de este proyecto. Modos: off, shadow (registra lo que bloquearía), active.",
|
|
1059
|
+
"Los guards de secretos, producción, trabajo de git perdido y falso terminado siguen activos diga lo que diga este archivo.",
|
|
1060
|
+
"Un guard que no está en esta lista empieza en shadow y se promueve tras promoteAfter disparos sin falso positivo."
|
|
1061
|
+
],
|
|
619
1062
|
agentsBlock: ({ gate, doctor }) => [
|
|
620
1063
|
"## Finite Monkey",
|
|
621
1064
|
"",
|
|
@@ -638,6 +1081,11 @@ var es = {
|
|
|
638
1081
|
// src/install/templates/pt-BR.ts
|
|
639
1082
|
var ptBR = {
|
|
640
1083
|
configHeader: "Configuração do Finite Monkey neste projeto. Versione este arquivo. O `finitemonkey update` mantém suas edições.",
|
|
1084
|
+
policyHeader: [
|
|
1085
|
+
"Política dos guards deste projeto. Modos: off, shadow (registra o que bloquearia), active.",
|
|
1086
|
+
"Os guards de segredos, produção, trabalho de git perdido e falso pronto ficam ativos, diga o que disser este arquivo.",
|
|
1087
|
+
"Um guard que não está nesta lista começa em shadow e é promovido depois de promoteAfter disparos sem falso positivo."
|
|
1088
|
+
],
|
|
641
1089
|
agentsBlock: ({ gate, doctor }) => [
|
|
642
1090
|
"## Finite Monkey",
|
|
643
1091
|
"",
|
|
@@ -694,8 +1142,8 @@ var execForm = (runner, event) => {
|
|
|
694
1142
|
};
|
|
695
1143
|
var hookHandler = (runner, spec2, failClosedSupported) => {
|
|
696
1144
|
const failure4 = spec2.guard && failClosedSupported ? { onFailure: "block" } : {};
|
|
697
|
-
const { command, args } = execForm(runner, spec2.event);
|
|
698
|
-
return { type: "command", command, args, timeout: spec2.timeout, ...failure4 };
|
|
1145
|
+
const { command, args: args2 } = execForm(runner, spec2.event);
|
|
1146
|
+
return { type: "command", command, args: args2, timeout: spec2.timeout, ...failure4 };
|
|
699
1147
|
};
|
|
700
1148
|
var OWN_SHELL = /(^|[\s/"'])finitemonkey(@[^\s"]+|\.mjs|\.cmd|\.exe)?"?\s+hook\s/;
|
|
701
1149
|
var OWN_SCRIPT = /finitemonkey(\.mjs|\.cmd|\.exe)?$/;
|
|
@@ -704,19 +1152,19 @@ var isOwnHandler = (handler) => {
|
|
|
704
1152
|
if (!isRecord(handler)) return false;
|
|
705
1153
|
const command = handler["command"];
|
|
706
1154
|
if (typeof command !== "string") return false;
|
|
707
|
-
const
|
|
708
|
-
if (
|
|
709
|
-
const hookAt =
|
|
1155
|
+
const args2 = stringsOf(handler["args"]);
|
|
1156
|
+
if (args2.length === 0) return OWN_SHELL.test(`${command} `);
|
|
1157
|
+
const hookAt = args2.indexOf("hook");
|
|
710
1158
|
if (hookAt < 0) return false;
|
|
711
|
-
const target = hookAt === 0 ? command :
|
|
1159
|
+
const target = hookAt === 0 ? command : args2[hookAt - 1] ?? "";
|
|
712
1160
|
return OWN_SCRIPT.test(target);
|
|
713
1161
|
};
|
|
714
1162
|
|
|
715
1163
|
// src/install/skill-template.ts
|
|
716
|
-
var cliCommand = (runner,
|
|
717
|
-
if (runner.kind === "local") return `node ${LOCAL_BIN} ${
|
|
718
|
-
if (runner.kind === "vendored") return `node ${VENDOR_BIN} ${
|
|
719
|
-
return isScript(runner.path) ? `node "${runner.path}" ${
|
|
1164
|
+
var cliCommand = (runner, args2) => {
|
|
1165
|
+
if (runner.kind === "local") return `node ${LOCAL_BIN} ${args2}`;
|
|
1166
|
+
if (runner.kind === "vendored") return `node ${VENDOR_BIN} ${args2}`;
|
|
1167
|
+
return isScript(runner.path) ? `node "${runner.path}" ${args2}` : `"${runner.path}" ${args2}`;
|
|
720
1168
|
};
|
|
721
1169
|
var verifySkillTemplate = (runner, lang) => {
|
|
722
1170
|
const skill = TEMPLATES[lang].verifySkill;
|
|
@@ -856,7 +1304,7 @@ var DENY_FLOOR = {
|
|
|
856
1304
|
var DENY_RULES = [...DENY_FLOOR.secrets, ...DENY_FLOOR.git, ...DENY_FLOOR.proof];
|
|
857
1305
|
|
|
858
1306
|
// src/install/detect.ts
|
|
859
|
-
import { join as
|
|
1307
|
+
import { join as join4 } from "node:path";
|
|
860
1308
|
|
|
861
1309
|
// src/install/json-text.ts
|
|
862
1310
|
var DEFAULT_STYLE = { indent: " ", finalNewline: true };
|
|
@@ -868,11 +1316,11 @@ var renderJson = (value, style) => `${JSON.stringify(value, null, style.indent)}
|
|
|
868
1316
|
var parseSettings = (text6) => text6 === void 0 || text6.trim() === "" ? { ok: true, value: {} } : parseJsonObject(text6);
|
|
869
1317
|
var asList = (value) => Array.isArray(value) ? value : [];
|
|
870
1318
|
var asRecord = (value) => isRecord(value) ? value : {};
|
|
871
|
-
var setKey = (
|
|
872
|
-
if (!(key in
|
|
1319
|
+
var setKey = (record3, key, value) => {
|
|
1320
|
+
if (!(key in record3)) return value === void 0 ? record3 : { ...record3, [key]: value };
|
|
873
1321
|
return Object.fromEntries(
|
|
874
|
-
Object.entries(
|
|
875
|
-
if (name !== key) return [[name,
|
|
1322
|
+
Object.entries(record3).flatMap(([name, current2]) => {
|
|
1323
|
+
if (name !== key) return [[name, current2]];
|
|
876
1324
|
return value === void 0 ? [] : [[name, value]];
|
|
877
1325
|
})
|
|
878
1326
|
);
|
|
@@ -949,7 +1397,7 @@ var scopeAuthorities = (text6, source) => {
|
|
|
949
1397
|
var userSettingsText = (project, env) => {
|
|
950
1398
|
const home = env["CLAUDE_CONFIG_DIR"] ?? env["HOME"] ?? env["USERPROFILE"];
|
|
951
1399
|
if (home === void 0) return void 0;
|
|
952
|
-
const file = env["CLAUDE_CONFIG_DIR"] === void 0 ?
|
|
1400
|
+
const file = env["CLAUDE_CONFIG_DIR"] === void 0 ? join4(home, ".claude", "settings.json") : join4(home, "settings.json");
|
|
953
1401
|
const text6 = project.runtime.readText(toAbsolutePath(file));
|
|
954
1402
|
return text6.ok ? text6.value : void 0;
|
|
955
1403
|
};
|
|
@@ -1041,6 +1489,18 @@ var applyMigrations = (items, previous, version, migrations) => {
|
|
|
1041
1489
|
// src/install/migrations/index.ts
|
|
1042
1490
|
var MIGRATIONS = [];
|
|
1043
1491
|
|
|
1492
|
+
// src/install/policy-template.ts
|
|
1493
|
+
var policyTemplate = (lang) => [
|
|
1494
|
+
`# ${GENERATED_MARKER}`,
|
|
1495
|
+
...TEMPLATES[lang].policyHeader.map((line2) => `# ${line2}`),
|
|
1496
|
+
"level: standard",
|
|
1497
|
+
"severity: medium",
|
|
1498
|
+
`promoteAfter: ${String(DEFAULT_PROMOTE_AFTER)}`,
|
|
1499
|
+
"guards:",
|
|
1500
|
+
...OPTIONAL_GUARDS.map((id) => ` ${id}: active`),
|
|
1501
|
+
""
|
|
1502
|
+
].join("\n");
|
|
1503
|
+
|
|
1044
1504
|
// src/install/settings-merge.ts
|
|
1045
1505
|
var SETTINGS_SCHEMA_URL = "https://json.schemastore.org/claude-code-settings.json";
|
|
1046
1506
|
var ATTRIBUTION_OFF = { commit: "", pr: "", sessionUrl: false };
|
|
@@ -1095,15 +1555,15 @@ var withPermissions = (settings, lists) => {
|
|
|
1095
1555
|
var missing = (wanted, existing) => wanted.filter((rule) => !existing.includes(rule));
|
|
1096
1556
|
var withoutAdded = (existing, added) => existing.filter((rule) => typeof rule !== "string" || !added.includes(rule));
|
|
1097
1557
|
var ownHookMatcher = (recorded) => (handler) => isOwnHandler(handler) || recorded.some((own) => JSON.stringify(own) === JSON.stringify(handler));
|
|
1098
|
-
var mergeSettings = (
|
|
1099
|
-
const schema = Object.keys(
|
|
1100
|
-
const base = schema ? { $schema: SETTINGS_SCHEMA_URL } :
|
|
1558
|
+
var mergeSettings = (current2, wanted, recorded) => {
|
|
1559
|
+
const schema = Object.keys(current2).length === 0;
|
|
1560
|
+
const base = schema ? { $schema: SETTINGS_SCHEMA_URL } : current2;
|
|
1101
1561
|
const hooks = mergeHooks(asRecord(base["hooks"]), ownHookMatcher(recorded), wanted.hooks);
|
|
1102
1562
|
const permissions = asRecord(base["permissions"]);
|
|
1103
1563
|
const existingDeny = asList(permissions["deny"]);
|
|
1104
1564
|
const existingAsk = asList(permissions["ask"]);
|
|
1105
1565
|
const deny2 = missing(wanted.deny, existingDeny);
|
|
1106
|
-
const
|
|
1566
|
+
const ask2 = missing(wanted.ask, existingAsk);
|
|
1107
1567
|
const blockReads = !(BLOCK_READS in permissions);
|
|
1108
1568
|
const attribution = wanted.attributionOff && !("attribution" in base);
|
|
1109
1569
|
const marketplace = wanted.marketplace && !hasEntry(base, "extraKnownMarketplaces", MARKETPLACE_KEY);
|
|
@@ -1111,21 +1571,21 @@ var mergeSettings = (current, wanted, recorded) => {
|
|
|
1111
1571
|
const language = "language" in base ? void 0 : wanted.language;
|
|
1112
1572
|
let settings = withPermissions(withHooks(base, hooks), {
|
|
1113
1573
|
deny: [...existingDeny, ...deny2],
|
|
1114
|
-
ask: [...existingAsk, ...
|
|
1574
|
+
ask: [...existingAsk, ...ask2],
|
|
1115
1575
|
blockReads: blockReads ? true : permissions[BLOCK_READS]
|
|
1116
1576
|
});
|
|
1117
1577
|
if (attribution) settings = { ...settings, attribution: ATTRIBUTION_OFF };
|
|
1118
1578
|
if (marketplace) settings = withEntry(settings, "extraKnownMarketplaces", MARKETPLACE_KEY, MARKETPLACE_ENTRY);
|
|
1119
1579
|
if (plugin) settings = withEntry(settings, "enabledPlugins", PLUGIN_ID, true);
|
|
1120
1580
|
if (language !== void 0) settings = { ...settings, language };
|
|
1121
|
-
const added = { schema, deny: deny2, ask, blockReads, attribution, marketplace, plugin };
|
|
1581
|
+
const added = { schema, deny: deny2, ask: ask2, blockReads, attribution, marketplace, plugin };
|
|
1122
1582
|
return { settings, added: language === void 0 ? added : { ...added, language } };
|
|
1123
1583
|
};
|
|
1124
1584
|
var sameAttribution = (value) => JSON.stringify(value) === JSON.stringify(ATTRIBUTION_OFF);
|
|
1125
|
-
var unmergeSettings = (
|
|
1126
|
-
const hooks = mergeHooks(asRecord(
|
|
1127
|
-
const permissions = asRecord(
|
|
1128
|
-
let settings = withPermissions(withHooks(
|
|
1585
|
+
var unmergeSettings = (current2, added, recorded) => {
|
|
1586
|
+
const hooks = mergeHooks(asRecord(current2["hooks"]), ownHookMatcher(recorded), {});
|
|
1587
|
+
const permissions = asRecord(current2["permissions"]);
|
|
1588
|
+
let settings = withPermissions(withHooks(current2, hooks), {
|
|
1129
1589
|
deny: withoutAdded(asList(permissions["deny"]), added.deny),
|
|
1130
1590
|
ask: withoutAdded(asList(permissions["ask"]), added.ask),
|
|
1131
1591
|
blockReads: added.blockReads && permissions[BLOCK_READS] === true ? void 0 : permissions[BLOCK_READS]
|
|
@@ -1202,8 +1662,8 @@ var wantedHooks = (detection) => Object.fromEntries(
|
|
|
1202
1662
|
);
|
|
1203
1663
|
var settingsItem = (project, detection, input, previous) => {
|
|
1204
1664
|
const before = project.read(SETTINGS_FILE);
|
|
1205
|
-
const
|
|
1206
|
-
if (!
|
|
1665
|
+
const current2 = parseSettings(before);
|
|
1666
|
+
if (!current2.ok) return reject({ kind: "settings", detail: current2.error });
|
|
1207
1667
|
const hooks = wantedHooks(detection);
|
|
1208
1668
|
const wanted = {
|
|
1209
1669
|
hooks,
|
|
@@ -1213,7 +1673,7 @@ var settingsItem = (project, detection, input, previous) => {
|
|
|
1213
1673
|
marketplace: input.marketplace,
|
|
1214
1674
|
language: input.claudeLanguage
|
|
1215
1675
|
};
|
|
1216
|
-
const merged = mergeSettings(
|
|
1676
|
+
const merged = mergeSettings(current2.value, wanted, previous?.handlers ?? []);
|
|
1217
1677
|
const after = renderJson(merged.settings, before === void 0 ? DEFAULT_STYLE : styleOf(before));
|
|
1218
1678
|
return accept({
|
|
1219
1679
|
path: SETTINGS_FILE,
|
|
@@ -1254,7 +1714,8 @@ var generatedItem = (project, file, previous) => {
|
|
|
1254
1714
|
var generatedFiles = (input, runner, vendor) => {
|
|
1255
1715
|
const own = [
|
|
1256
1716
|
{ path: CONFIG_FILE, template: configTemplate(input.preset, input.lang), isOurs: hasMarker },
|
|
1257
|
-
{ path: VERIFY_SKILL, template: verifySkillTemplate(runner, input.lang), isOurs: isOwnSkill }
|
|
1717
|
+
{ path: VERIFY_SKILL, template: verifySkillTemplate(runner, input.lang), isOurs: isOwnSkill },
|
|
1718
|
+
{ path: POLICY_FILE, template: policyTemplate(input.lang), isOurs: hasMarker }
|
|
1258
1719
|
];
|
|
1259
1720
|
if (!vendor) return accept(own);
|
|
1260
1721
|
if (input.bundle === void 0) return reject({ kind: "bundle", detail: "the running finitemonkey is not a bundle" });
|
|
@@ -1428,9 +1889,9 @@ var detectLang = (flag2, env, settings = {}) => {
|
|
|
1428
1889
|
};
|
|
1429
1890
|
|
|
1430
1891
|
// src/install/project.ts
|
|
1431
|
-
import { dirname as dirname4, join as
|
|
1892
|
+
import { dirname as dirname4, join as join5 } from "node:path";
|
|
1432
1893
|
var projectAt = (root, runtime) => {
|
|
1433
|
-
const abs = (relative9) => toAbsolutePath(
|
|
1894
|
+
const abs = (relative9) => toAbsolutePath(join5(root, ...relative9.split("/")));
|
|
1434
1895
|
return {
|
|
1435
1896
|
root,
|
|
1436
1897
|
runtime,
|
|
@@ -1444,12 +1905,12 @@ var projectAt = (root, runtime) => {
|
|
|
1444
1905
|
};
|
|
1445
1906
|
};
|
|
1446
1907
|
var findGitRoot = (start, runtime) => {
|
|
1447
|
-
let
|
|
1908
|
+
let current2 = start;
|
|
1448
1909
|
for (; ; ) {
|
|
1449
|
-
if (runtime.stat(toAbsolutePath(
|
|
1450
|
-
const parent = dirname4(
|
|
1451
|
-
if (parent ===
|
|
1452
|
-
|
|
1910
|
+
if (runtime.stat(toAbsolutePath(join5(current2, ".git"))) !== void 0) return toAbsolutePath(current2);
|
|
1911
|
+
const parent = dirname4(current2);
|
|
1912
|
+
if (parent === current2) return void 0;
|
|
1913
|
+
current2 = parent;
|
|
1453
1914
|
}
|
|
1454
1915
|
};
|
|
1455
1916
|
|
|
@@ -1547,7 +2008,7 @@ var applyFixes = (ctx, write3) => {
|
|
|
1547
2008
|
};
|
|
1548
2009
|
|
|
1549
2010
|
// src/doctor/report-file.ts
|
|
1550
|
-
import { join as
|
|
2011
|
+
import { join as join7 } from "node:path";
|
|
1551
2012
|
|
|
1552
2013
|
// src/evidence/log.ts
|
|
1553
2014
|
var logEvent = (runtime, paths, event, data) => runtime.appendText(paths.log, `${JSON.stringify({ at: runtime.now(), event, ...data })}
|
|
@@ -2014,11 +2475,11 @@ var maskDeep = (value) => {
|
|
|
2014
2475
|
};
|
|
2015
2476
|
|
|
2016
2477
|
// src/task/paths.ts
|
|
2017
|
-
import { join as
|
|
2478
|
+
import { join as join6 } from "node:path";
|
|
2018
2479
|
var FM_DIR = ".finitemonkey";
|
|
2019
2480
|
var fmPaths = (root) => {
|
|
2020
|
-
const dir = toAbsolutePath(
|
|
2021
|
-
const at = (...parts) => toAbsolutePath(
|
|
2481
|
+
const dir = toAbsolutePath(join6(root, FM_DIR));
|
|
2482
|
+
const at = (...parts) => toAbsolutePath(join6(dir, ...parts));
|
|
2022
2483
|
return {
|
|
2023
2484
|
root,
|
|
2024
2485
|
dir,
|
|
@@ -2036,8 +2497,8 @@ var fmPaths = (root) => {
|
|
|
2036
2497
|
var HOOK_FAILURE_EVENT = "hook.failed";
|
|
2037
2498
|
var FAILURES = ["failed_open", "failed_closed", "stop_released"];
|
|
2038
2499
|
var MAX_DETAIL = 500;
|
|
2039
|
-
var logHookFailure = (ctx, event,
|
|
2040
|
-
if (!FAILURES.includes(
|
|
2500
|
+
var logHookFailure = (ctx, event, outcome2) => {
|
|
2501
|
+
if (!FAILURES.includes(outcome2.verdict)) return;
|
|
2041
2502
|
const fromEnv = ctx.env["CLAUDE_PROJECT_DIR"];
|
|
2042
2503
|
const root = fromEnv === void 0 ? findGitRoot(ctx.cwd, ctx.runtime) : toAbsolutePath(fromEnv);
|
|
2043
2504
|
if (root === void 0) return;
|
|
@@ -2045,8 +2506,8 @@ var logHookFailure = (ctx, event, outcome) => {
|
|
|
2045
2506
|
if (ctx.runtime.stat(paths.dir)?.kind !== "dir") return;
|
|
2046
2507
|
logEvent(ctx.runtime, paths, HOOK_FAILURE_EVENT, {
|
|
2047
2508
|
hook: event,
|
|
2048
|
-
verdict:
|
|
2049
|
-
detail: maskText(
|
|
2509
|
+
verdict: outcome2.verdict,
|
|
2510
|
+
detail: maskText(outcome2.stderr.trim()).slice(0, MAX_DETAIL)
|
|
2050
2511
|
});
|
|
2051
2512
|
};
|
|
2052
2513
|
|
|
@@ -2254,16 +2715,16 @@ var reportMarkdown = (env, ctx, envelope) => {
|
|
|
2254
2715
|
];
|
|
2255
2716
|
return maskText(withoutHome(lines3.join("\n"), env.runtime.homeDir()));
|
|
2256
2717
|
};
|
|
2257
|
-
var reportPath = (env) => toAbsolutePath(
|
|
2718
|
+
var reportPath = (env) => toAbsolutePath(join7(env.root, ...REPORTS_DIR.split("/"), `doctor-${stamp(env.runtime.now())}.md`));
|
|
2258
2719
|
|
|
2259
2720
|
// src/install/plugin-yield.ts
|
|
2260
|
-
import { join as
|
|
2721
|
+
import { join as join8 } from "node:path";
|
|
2261
2722
|
var PLUGIN_VIA = "plugin";
|
|
2262
2723
|
var YIELD_STDOUT = `${JSON.stringify({ continue: true })}
|
|
2263
2724
|
`;
|
|
2264
2725
|
var projectOwnsHooks = (ctx) => {
|
|
2265
2726
|
const root = ctx.env["CLAUDE_PROJECT_DIR"] ?? ctx.cwd;
|
|
2266
|
-
const text6 = ctx.runtime.readText(toAbsolutePath(
|
|
2727
|
+
const text6 = ctx.runtime.readText(toAbsolutePath(join8(root, ...SETTINGS_FILE.split("/"))));
|
|
2267
2728
|
if (!text6.ok) return false;
|
|
2268
2729
|
const parsed = parseJsonObject(text6.value);
|
|
2269
2730
|
return parsed.ok && ownHandlersIn(parsed.value) > 0;
|
|
@@ -2277,7 +2738,7 @@ var STABLE_PLUGIN_ID = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
|
|
|
2277
2738
|
var NEXT_PLUGIN_ID = `${PLUGIN_NAME}@${NEXT_MARKETPLACE_NAME}`;
|
|
2278
2739
|
|
|
2279
2740
|
// src/doctor/settings.ts
|
|
2280
|
-
import { join as
|
|
2741
|
+
import { join as join9 } from "node:path";
|
|
2281
2742
|
var MANAGED = {
|
|
2282
2743
|
darwin: "/Library/Application Support/ClaudeCode/managed-settings.json",
|
|
2283
2744
|
linux: "/etc/claude-code/managed-settings.json",
|
|
@@ -2286,7 +2747,7 @@ var MANAGED = {
|
|
|
2286
2747
|
var userSettingsPath = ({ env, runtime }) => {
|
|
2287
2748
|
const configDir = env["CLAUDE_CONFIG_DIR"];
|
|
2288
2749
|
return toAbsolutePath(
|
|
2289
|
-
configDir === void 0 ?
|
|
2750
|
+
configDir === void 0 ? join9(runtime.homeDir(), ".claude", "settings.json") : join9(configDir, "settings.json")
|
|
2290
2751
|
);
|
|
2291
2752
|
};
|
|
2292
2753
|
var settingsPaths = (env) => {
|
|
@@ -2294,8 +2755,8 @@ var settingsPaths = (env) => {
|
|
|
2294
2755
|
return [
|
|
2295
2756
|
...managed === void 0 ? [] : [{ scope: "managed", path: toAbsolutePath(managed) }],
|
|
2296
2757
|
{ scope: "user", path: userSettingsPath(env) },
|
|
2297
|
-
{ scope: "project", path: toAbsolutePath(
|
|
2298
|
-
{ scope: "local", path: toAbsolutePath(
|
|
2758
|
+
{ scope: "project", path: toAbsolutePath(join9(env.root, ".claude", "settings.json")) },
|
|
2759
|
+
{ scope: "local", path: toAbsolutePath(join9(env.root, ".claude", "settings.local.json")) }
|
|
2299
2760
|
];
|
|
2300
2761
|
};
|
|
2301
2762
|
var readSettings = (env) => settingsPaths(env).flatMap(({ scope: scope2, path }) => {
|
|
@@ -2439,7 +2900,7 @@ var checkClaudeSettings = (env) => {
|
|
|
2439
2900
|
};
|
|
2440
2901
|
|
|
2441
2902
|
// src/doctor/environment.ts
|
|
2442
|
-
import { join as
|
|
2903
|
+
import { join as join10 } from "node:path";
|
|
2443
2904
|
var AUTH_VARIABLES = [
|
|
2444
2905
|
"ANTHROPIC_API_KEY",
|
|
2445
2906
|
"ANTHROPIC_AUTH_TOKEN",
|
|
@@ -2459,7 +2920,7 @@ var checkSandbox = (env) => {
|
|
|
2459
2920
|
}
|
|
2460
2921
|
if (os === "macos") return [finding("sandbox", "ok", "doctor.sandbox.ok")];
|
|
2461
2922
|
if (os === "other") return [];
|
|
2462
|
-
const missing2 = SANDBOX_TOOLS.filter(({ command, args }) => !env.runtime.probe(command,
|
|
2923
|
+
const missing2 = SANDBOX_TOOLS.filter(({ command, args: args2 }) => !env.runtime.probe(command, args2, PROBE_MS2).found).map(
|
|
2463
2924
|
({ command }) => command
|
|
2464
2925
|
);
|
|
2465
2926
|
if (missing2.length === 0) return [finding("sandbox", "ok", "doctor.sandbox.ok")];
|
|
@@ -2477,7 +2938,7 @@ var pathDirs = ({ env, platform }) => {
|
|
|
2477
2938
|
var copiesOnPath = (env) => {
|
|
2478
2939
|
const names = EXECUTABLES[env.platform] ?? ["finitemonkey"];
|
|
2479
2940
|
const found = pathDirs(env).flatMap(
|
|
2480
|
-
(dir) => names.map((name) => toAbsolutePath(
|
|
2941
|
+
(dir) => names.map((name) => toAbsolutePath(join10(dir, name))).filter((path) => env.runtime.stat(path)?.kind === "file").map((path) => env.runtime.realpath(path))
|
|
2481
2942
|
);
|
|
2482
2943
|
const byDir = new Map(found.map((path) => [path.replace(/\.(cmd|exe|ps1)$/i, ""), path]));
|
|
2483
2944
|
return [...byDir.values()];
|
|
@@ -2511,7 +2972,7 @@ var checkAuthVariables = ({ env, platform }) => {
|
|
|
2511
2972
|
};
|
|
2512
2973
|
|
|
2513
2974
|
// src/doctor/hooks.ts
|
|
2514
|
-
import { join as
|
|
2975
|
+
import { join as join31 } from "node:path";
|
|
2515
2976
|
|
|
2516
2977
|
// src/hooks/events.ts
|
|
2517
2978
|
var HOOK_EVENTS = [
|
|
@@ -2558,9 +3019,9 @@ var deadlineFor = (event, raw) => {
|
|
|
2558
3019
|
};
|
|
2559
3020
|
|
|
2560
3021
|
// src/hooks/fields.ts
|
|
2561
|
-
var textField = (
|
|
3022
|
+
var textField = (record3, ...names) => {
|
|
2562
3023
|
for (const name of names) {
|
|
2563
|
-
const value =
|
|
3024
|
+
const value = record3[name];
|
|
2564
3025
|
if (typeof value === "string") return value;
|
|
2565
3026
|
}
|
|
2566
3027
|
return void 0;
|
|
@@ -2655,8 +3116,8 @@ var closeTask = (state, id) => assemble({
|
|
|
2655
3116
|
|
|
2656
3117
|
// src/guards/release/grants.ts
|
|
2657
3118
|
var GRANT_MINUTES = 30;
|
|
2658
|
-
var text2 = (
|
|
2659
|
-
const value =
|
|
3119
|
+
var text2 = (record3, key) => {
|
|
3120
|
+
const value = record3[key];
|
|
2660
3121
|
return typeof value === "string" ? value : void 0;
|
|
2661
3122
|
};
|
|
2662
3123
|
var toGrant = (value) => {
|
|
@@ -3180,10 +3641,10 @@ var withAssignments = (stage) => {
|
|
|
3180
3641
|
}
|
|
3181
3642
|
return { ...stage, argv: stage.argv.slice(index), env };
|
|
3182
3643
|
};
|
|
3183
|
-
var skipOptions = (
|
|
3644
|
+
var skipOptions = (args2, wrapper) => {
|
|
3184
3645
|
let index = 0;
|
|
3185
|
-
while (index <
|
|
3186
|
-
const arg =
|
|
3646
|
+
while (index < args2.length) {
|
|
3647
|
+
const arg = args2[index] ?? "";
|
|
3187
3648
|
if (arg === "--") return index + 1 + wrapper.positionals;
|
|
3188
3649
|
if (!arg.startsWith("-") || arg === "-") break;
|
|
3189
3650
|
if (wrapper.queries.has(arg)) return void 0;
|
|
@@ -3213,33 +3674,33 @@ var envCommand = (stage) => {
|
|
|
3213
3674
|
return printsEnvironment ? { kind: "command", stage, extra: [] } : shift(stage, index, "env");
|
|
3214
3675
|
};
|
|
3215
3676
|
var shellScript = (stage, name, stdin) => {
|
|
3216
|
-
const
|
|
3677
|
+
const args2 = stage.argv.slice(1);
|
|
3217
3678
|
let index = 0;
|
|
3218
3679
|
let command = false;
|
|
3219
|
-
while (index <
|
|
3220
|
-
const arg =
|
|
3680
|
+
while (index < args2.length && /^[-+]/.test(args2[index] ?? "")) {
|
|
3681
|
+
const arg = args2[index] ?? "";
|
|
3221
3682
|
if (/^-[A-Za-z]*c[A-Za-z]*$/.test(arg)) command = true;
|
|
3222
3683
|
index += SHELL_WITH_VALUE.has(arg) ? 2 : 1;
|
|
3223
3684
|
}
|
|
3224
|
-
const script =
|
|
3685
|
+
const script = args2[index];
|
|
3225
3686
|
if (command && script !== void 0) return { kind: "script", script, dialect: "bash", stage: shift(stage, 0, name) };
|
|
3226
3687
|
if (script === void 0 && stdin !== void 0) return { kind: "script", script: stdin, dialect: "bash", stage };
|
|
3227
3688
|
return void 0;
|
|
3228
3689
|
};
|
|
3229
3690
|
var decodeEncoded = (value) => Buffer.from(value, "base64").toString("utf16le");
|
|
3230
3691
|
var powershellScript = (stage, name) => {
|
|
3231
|
-
const
|
|
3232
|
-
const at =
|
|
3233
|
-
const encoded =
|
|
3692
|
+
const args2 = stage.argv.slice(1);
|
|
3693
|
+
const at = args2.findIndex((arg) => /^[-/](?:c|co|com|comm|comma|comman|command)$/i.test(arg));
|
|
3694
|
+
const encoded = args2.findIndex((arg) => /^[-/](?:e|ec|enc|encodedcommand)$/i.test(arg));
|
|
3234
3695
|
if (at >= 0) {
|
|
3235
3696
|
return {
|
|
3236
3697
|
kind: "script",
|
|
3237
|
-
script:
|
|
3698
|
+
script: args2.slice(at + 1).join(" "),
|
|
3238
3699
|
dialect: "powershell",
|
|
3239
3700
|
stage: shift(stage, 0, name)
|
|
3240
3701
|
};
|
|
3241
3702
|
}
|
|
3242
|
-
const value = encoded >= 0 ?
|
|
3703
|
+
const value = encoded >= 0 ? args2[encoded + 1] : void 0;
|
|
3243
3704
|
if (value !== void 0) {
|
|
3244
3705
|
return { kind: "script", script: decodeEncoded(value), dialect: "powershell", stage: shift(stage, 0, name) };
|
|
3245
3706
|
}
|
|
@@ -3298,10 +3759,10 @@ var PS_ENV = /^\$env:([A-Za-z_][A-Za-z0-9_]*)=?(.*)$/is;
|
|
|
3298
3759
|
var CHANGES_DIR = /* @__PURE__ */ new Set(["cd", "pushd", "set-location", "push-location"]);
|
|
3299
3760
|
var lex = (text6, language) => language === "bash" ? lexBash(text6) : lexPowerShell(text6, language === "cmd");
|
|
3300
3761
|
var isWindowsAbsolute = (path) => /^[A-Za-z]:\//.test(path);
|
|
3301
|
-
var joinDir = (
|
|
3762
|
+
var joinDir = (current2, target) => {
|
|
3302
3763
|
const next2 = toPosixPath(target);
|
|
3303
3764
|
if (next2.startsWith("/") || isWindowsAbsolute(next2) || next2.startsWith("~")) return next2;
|
|
3304
|
-
return
|
|
3765
|
+
return current2 === void 0 ? posix.normalize(next2) : posix.join(current2, next2);
|
|
3305
3766
|
};
|
|
3306
3767
|
var assignments = (words, language) => {
|
|
3307
3768
|
if (language !== "bash") {
|
|
@@ -3414,24 +3875,24 @@ var COMMIT_VALUE_SHORTS = /* @__PURE__ */ new Set(["m", "F", "C", "c", "t"]);
|
|
|
3414
3875
|
var VERIFY_SUBCOMMANDS = /* @__PURE__ */ new Set(["commit", "push", "merge", "am", "rebase", "cherry-pick", "revert", "pull"]);
|
|
3415
3876
|
var isHooksPath = (value) => /^core\.hookspath(=|$)/i.test(value);
|
|
3416
3877
|
var envFindings = (command) => {
|
|
3417
|
-
const
|
|
3878
|
+
const pairs2 = Object.entries(command.env);
|
|
3418
3879
|
const exported = command.argv[0] === "export" ? command.argv.slice(1).map((arg) => arg.split("=", 2)) : [];
|
|
3419
|
-
return [...
|
|
3880
|
+
return [...pairs2, ...exported].filter(([name = ""]) => HOOK_ENV.test(name)).map(([name = "", value = ""]) => ({ kind: "hook_env", detail: `${name}=${value}` }));
|
|
3420
3881
|
};
|
|
3421
|
-
var splitGit = (
|
|
3882
|
+
var splitGit = (args2) => {
|
|
3422
3883
|
let index = 0;
|
|
3423
3884
|
const globals = [];
|
|
3424
|
-
while (index <
|
|
3425
|
-
const arg =
|
|
3885
|
+
while (index < args2.length && (args2[index] ?? "").startsWith("-")) {
|
|
3886
|
+
const arg = args2[index] ?? "";
|
|
3426
3887
|
globals.push(arg);
|
|
3427
|
-
if (GIT_VALUE_OPTIONS.has(arg)) globals.push(
|
|
3888
|
+
if (GIT_VALUE_OPTIONS.has(arg)) globals.push(args2[index + 1] ?? "");
|
|
3428
3889
|
index += GIT_VALUE_OPTIONS.has(arg) ? 2 : 1;
|
|
3429
3890
|
}
|
|
3430
|
-
return { globals, rest:
|
|
3891
|
+
return { globals, rest: args2.slice(index) };
|
|
3431
3892
|
};
|
|
3432
|
-
var commitHasShortN = (
|
|
3433
|
-
for (let index = 0; index <
|
|
3434
|
-
const arg =
|
|
3893
|
+
var commitHasShortN = (args2) => {
|
|
3894
|
+
for (let index = 0; index < args2.length; index += 1) {
|
|
3895
|
+
const arg = args2[index] ?? "";
|
|
3435
3896
|
if (arg === "--") return false;
|
|
3436
3897
|
if (!/^-[A-Za-z]/.test(arg)) continue;
|
|
3437
3898
|
for (const char of arg.slice(1)) {
|
|
@@ -3444,8 +3905,8 @@ var commitHasShortN = (args) => {
|
|
|
3444
3905
|
}
|
|
3445
3906
|
return false;
|
|
3446
3907
|
};
|
|
3447
|
-
var gitFindings = (
|
|
3448
|
-
const { globals, rest } = splitGit(
|
|
3908
|
+
var gitFindings = (args2) => {
|
|
3909
|
+
const { globals, rest } = splitGit(args2);
|
|
3449
3910
|
const found = [];
|
|
3450
3911
|
globals.forEach((arg, index) => {
|
|
3451
3912
|
if (arg === "-c" && isHooksPath(globals[index + 1] ?? "") || /^--config-env=core\.hookspath/i.test(arg)) {
|
|
@@ -3522,16 +3983,16 @@ var GIT_WRITERS = /* @__PURE__ */ new Set(["checkout", "restore", "rm", "mv", "c
|
|
|
3522
3983
|
var FM_NAMES2 = /* @__PURE__ */ new Set(["finitemonkey", "fm"]);
|
|
3523
3984
|
var inline = (argv) => argv.some((arg) => arg === "-e" || arg === "-c" || arg === "--eval");
|
|
3524
3985
|
var writtenPaths = (command) => {
|
|
3525
|
-
const [name = "", ...
|
|
3526
|
-
const operands2 =
|
|
3527
|
-
const ddTargets =
|
|
3986
|
+
const [name = "", ...args2] = command.argv;
|
|
3987
|
+
const operands2 = args2.filter((arg) => !arg.startsWith("-"));
|
|
3988
|
+
const ddTargets = args2.filter((arg) => arg.startsWith("of=")).map((arg) => arg.slice(3));
|
|
3528
3989
|
if (FM_NAMES2.has(name)) return command.redirects;
|
|
3529
3990
|
if (COPIERS.has(name)) return [...command.redirects, ...operands2.slice(-1)];
|
|
3530
3991
|
if (WRITERS.has(name)) return [...command.redirects, ...operands2, ...ddTargets];
|
|
3531
|
-
if (IN_PLACE.has(name) &&
|
|
3992
|
+
if (IN_PLACE.has(name) && args2.some((arg) => /^-[a-zA-Z]*i/.test(arg) || arg.startsWith("--in-place"))) {
|
|
3532
3993
|
return [...command.redirects, ...operands2];
|
|
3533
3994
|
}
|
|
3534
|
-
if (INTERPRETERS.has(name) && inline(
|
|
3995
|
+
if (INTERPRETERS.has(name) && inline(args2)) return [...command.redirects, ...args2];
|
|
3535
3996
|
if (name === "git" && GIT_WRITERS.has(operands2[0] ?? "")) return [...command.redirects, ...operands2.slice(1)];
|
|
3536
3997
|
return command.redirects;
|
|
3537
3998
|
};
|
|
@@ -3550,14 +4011,14 @@ var bashSelfTargets = (root, commandLine) => parseShell(commandLine).flatMap(
|
|
|
3550
4011
|
// src/guards/self/hook.ts
|
|
3551
4012
|
var targets = (input) => {
|
|
3552
4013
|
const tool = toolName(input.payload.raw);
|
|
3553
|
-
const
|
|
4014
|
+
const args2 = toolInput(input.payload.raw);
|
|
3554
4015
|
const root = guardRoot(input);
|
|
3555
4016
|
if (FILE_TOOLS.has(tool)) {
|
|
3556
|
-
const path = textField(
|
|
4017
|
+
const path = textField(args2, "file_path", "notebook_path") ?? "";
|
|
3557
4018
|
const kind = path === "" ? void 0 : selfTarget(root, path);
|
|
3558
4019
|
return kind === void 0 ? [] : [{ kind, path }];
|
|
3559
4020
|
}
|
|
3560
|
-
return SHELL_TOOLS.has(tool) ? bashSelfTargets(root, textField(
|
|
4021
|
+
return SHELL_TOOLS.has(tool) ? bashSelfTargets(root, textField(args2, "command") ?? "") : [];
|
|
3561
4022
|
};
|
|
3562
4023
|
var selfGuard = (input) => {
|
|
3563
4024
|
const found = targets(input);
|
|
@@ -3574,7 +4035,7 @@ var selfGuard = (input) => {
|
|
|
3574
4035
|
};
|
|
3575
4036
|
|
|
3576
4037
|
// src/guards/tests/hook.ts
|
|
3577
|
-
import { join as
|
|
4038
|
+
import { join as join11, relative as relative3 } from "node:path";
|
|
3578
4039
|
|
|
3579
4040
|
// src/guards/tests/patterns.ts
|
|
3580
4041
|
var JS_TEST = /(^|\/)(__tests__|__test__)\/|\.(test|spec)\.[cm]?[jt]sx?$|(^|\/)tests?\/.*\.[cm]?[jt]sx?$/;
|
|
@@ -3741,19 +4202,19 @@ var read = (input, path) => {
|
|
|
3741
4202
|
const text6 = input.runtime.readText(path);
|
|
3742
4203
|
return text6.ok ? text6.value : void 0;
|
|
3743
4204
|
};
|
|
3744
|
-
var proposed = (tool,
|
|
3745
|
-
if (tool === "Write") return textField(
|
|
3746
|
-
return before === void 0 ? void 0 : applyEdits(before, editsOf(
|
|
4205
|
+
var proposed = (tool, args2, before) => {
|
|
4206
|
+
if (tool === "Write") return textField(args2, "content") ?? "";
|
|
4207
|
+
return before === void 0 ? void 0 : applyEdits(before, editsOf(args2));
|
|
3747
4208
|
};
|
|
3748
4209
|
var fileViolations = (input, root) => {
|
|
3749
4210
|
const tool = toolName(input.payload.raw);
|
|
3750
|
-
const
|
|
3751
|
-
const file = textField(
|
|
4211
|
+
const args2 = toolInput(input.payload.raw);
|
|
4212
|
+
const file = textField(args2, "file_path") ?? "";
|
|
3752
4213
|
if (tool === "NotebookEdit" || file === "") return [];
|
|
3753
4214
|
const local = localPath(root, file);
|
|
3754
4215
|
if (!isTestFile(local) && !isProtectedConfig(local)) return [];
|
|
3755
4216
|
const before = read(input, toAbsolutePath(file, root));
|
|
3756
|
-
const after = proposed(tool,
|
|
4217
|
+
const after = proposed(tool, args2, before);
|
|
3757
4218
|
if (after === void 0) return [];
|
|
3758
4219
|
const accepted2 = readTrust(input.runtime, fmPaths(root)).configs[local];
|
|
3759
4220
|
if (before === void 0) return compareNewTestFile(local, after);
|
|
@@ -3764,7 +4225,7 @@ var shellViolations = (input, root) => {
|
|
|
3764
4225
|
return parseShell(command).flatMap(
|
|
3765
4226
|
(simple) => writtenPaths(simple).flatMap((path) => {
|
|
3766
4227
|
const local = localPath(root, path);
|
|
3767
|
-
const exists = input.runtime.stat(toAbsolutePath(
|
|
4228
|
+
const exists = input.runtime.stat(toAbsolutePath(join11(root, local))) !== void 0;
|
|
3768
4229
|
if (!exists) return [];
|
|
3769
4230
|
if (isProtectedConfig(local)) return [{ kind: "config_changed", path: local }];
|
|
3770
4231
|
return isTestFile(local) ? [{ kind: "test_removed", path: local }] : [];
|
|
@@ -3784,20 +4245,20 @@ var testsGuard = (input) => {
|
|
|
3784
4245
|
};
|
|
3785
4246
|
|
|
3786
4247
|
// src/gate/hooks/config.ts
|
|
3787
|
-
import { join as
|
|
4248
|
+
import { join as join13 } from "node:path";
|
|
3788
4249
|
|
|
3789
4250
|
// src/gate/hooks/common.ts
|
|
3790
|
-
import { dirname as dirname5, join as
|
|
4251
|
+
import { dirname as dirname5, join as join12 } from "node:path";
|
|
3791
4252
|
var DEFAULT_GATE_BUDGET_MS = 3e5;
|
|
3792
4253
|
var sessionCwd = (input) => input.payload.cwd === void 0 ? input.cwd : toAbsolutePath(input.payload.cwd, input.cwd);
|
|
3793
4254
|
var MAX_DEPTH2 = 64;
|
|
3794
4255
|
var findRepoRoot = (runtime, start) => {
|
|
3795
|
-
let
|
|
4256
|
+
let current2 = start;
|
|
3796
4257
|
for (let depth = 0; depth < MAX_DEPTH2; depth += 1) {
|
|
3797
|
-
if (runtime.stat(toAbsolutePath(
|
|
3798
|
-
const parent = toAbsolutePath(dirname5(
|
|
3799
|
-
if (parent ===
|
|
3800
|
-
|
|
4258
|
+
if (runtime.stat(toAbsolutePath(join12(current2, ".git"))) !== void 0) return current2;
|
|
4259
|
+
const parent = toAbsolutePath(dirname5(current2));
|
|
4260
|
+
if (parent === current2) return void 0;
|
|
4261
|
+
current2 = parent;
|
|
3801
4262
|
}
|
|
3802
4263
|
return void 0;
|
|
3803
4264
|
};
|
|
@@ -3817,7 +4278,7 @@ var contextOutput = (event, additionalContext, systemMessage) => ({
|
|
|
3817
4278
|
var human = (text6) => `finitemonkey: ${text6}`;
|
|
3818
4279
|
|
|
3819
4280
|
// src/gate/hooks/config.ts
|
|
3820
|
-
var
|
|
4281
|
+
var CONFIG_FILE3 = "config-hooks.json";
|
|
3821
4282
|
var GUARDED_SOURCES = /* @__PURE__ */ new Set(["user_settings", "project_settings", "local_settings"]);
|
|
3822
4283
|
var countIn = (value) => {
|
|
3823
4284
|
if (typeof value === "string") return /\bfinitemonkey\b|\bfm hook\b/.test(value) ? 1 : 0;
|
|
@@ -3831,20 +4292,20 @@ var censusOf = (text6) => {
|
|
|
3831
4292
|
return { hooks: countIn(parsed.value["hooks"]), disabled: parsed.value["disableAllHooks"] === true };
|
|
3832
4293
|
};
|
|
3833
4294
|
var settingsFiles = (paths, env) => [
|
|
3834
|
-
toAbsolutePath(
|
|
3835
|
-
toAbsolutePath(
|
|
3836
|
-
...env["HOME"] === void 0 ? [] : [toAbsolutePath(
|
|
4295
|
+
toAbsolutePath(join13(paths.root, ".claude", "settings.json")),
|
|
4296
|
+
toAbsolutePath(join13(paths.root, ".claude", "settings.local.json")),
|
|
4297
|
+
...env["HOME"] === void 0 ? [] : [toAbsolutePath(join13(env["HOME"], ".claude", "settings.json"))]
|
|
3837
4298
|
];
|
|
3838
4299
|
var read2 = (runtime, path) => {
|
|
3839
4300
|
const text6 = runtime.readText(path);
|
|
3840
4301
|
return text6.ok ? text6.value : void 0;
|
|
3841
4302
|
};
|
|
3842
4303
|
var known = (runtime, paths) => {
|
|
3843
|
-
const raw = readJsonFile(runtime, paths.cache(
|
|
4304
|
+
const raw = readJsonFile(runtime, paths.cache(CONFIG_FILE3))?.["hooks"];
|
|
3844
4305
|
return isRecord(raw) ? Object.fromEntries(Object.entries(raw).filter((entry2) => typeof entry2[1] === "number")) : {};
|
|
3845
4306
|
};
|
|
3846
4307
|
var save = (runtime, paths, counts) => {
|
|
3847
|
-
writeJsonFile(runtime, paths.cache(
|
|
4308
|
+
writeJsonFile(runtime, paths.cache(CONFIG_FILE3), { hooks: { ...counts } });
|
|
3848
4309
|
};
|
|
3849
4310
|
var recordConfigHooks = (runtime, paths, env) => {
|
|
3850
4311
|
const counts = Object.fromEntries(
|
|
@@ -3964,107 +4425,22 @@ var implicitContract = (files) => {
|
|
|
3964
4425
|
acceptance: [
|
|
3965
4426
|
...commands,
|
|
3966
4427
|
...hasTests ? [] : [{ id: "tests", kind: "tests" }],
|
|
3967
|
-
{ id: "placeholders", kind: "placeholders" },
|
|
3968
|
-
{ id: "secrets", kind: "secrets" },
|
|
3969
|
-
{ id: "prose", kind: "prose" },
|
|
3970
|
-
{ id: "worktree", kind: "worktree" },
|
|
3971
|
-
{ id: "merge", kind: "merge" },
|
|
3972
|
-
{ id: "
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
};
|
|
3978
|
-
|
|
3979
|
-
// src/gate/prepare.ts
|
|
3980
|
-
import { join as join14 } from "node:path";
|
|
3981
|
-
|
|
3982
|
-
// src/git/project-config.ts
|
|
3983
|
-
import { join as join12 } from "node:path";
|
|
3984
|
-
var CONFIG_FILE3 = ".finitemonkey/config.yml";
|
|
3985
|
-
var unquote = (value) => value.trim().replace(/^(["'])(.*)\1$/s, "$2");
|
|
3986
|
-
var stripComment = (line2) => {
|
|
3987
|
-
let quote;
|
|
3988
|
-
for (let index = 0; index < line2.length; index += 1) {
|
|
3989
|
-
const char = line2.charAt(index);
|
|
3990
|
-
if (quote === void 0 && (char === '"' || char === "'")) quote = char;
|
|
3991
|
-
else if (char === quote) quote = void 0;
|
|
3992
|
-
else if (quote === void 0 && char === "#" && (index === 0 || /\s/.test(line2.charAt(index - 1)))) {
|
|
3993
|
-
return line2.slice(0, index);
|
|
3994
|
-
}
|
|
3995
|
-
}
|
|
3996
|
-
return line2;
|
|
3997
|
-
};
|
|
3998
|
-
var flowList = (value) => {
|
|
3999
|
-
const trimmed = value.trim();
|
|
4000
|
-
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return void 0;
|
|
4001
|
-
return trimmed.slice(1, -1).split(",").map(unquote).filter((item) => item !== "");
|
|
4002
|
-
};
|
|
4003
|
-
var keyValue = (body2) => {
|
|
4004
|
-
const match = /^("[^"]*"|'[^']*'|[^:]+?)\s*:(?:\s+(.*))?$/.exec(body2);
|
|
4005
|
-
if (match === null) return void 0;
|
|
4006
|
-
return { key: unquote(match[1] ?? ""), value: match[2] ?? "" };
|
|
4007
|
-
};
|
|
4008
|
-
var addItem = (frame, item) => {
|
|
4009
|
-
if (frame.key === void 0) return;
|
|
4010
|
-
const parent = frame.tree[frame.key];
|
|
4011
|
-
if (Array.isArray(parent)) parent.push(item);
|
|
4012
|
-
else frame.tree[frame.key] = [item];
|
|
4013
|
-
};
|
|
4014
|
-
var placeLine = (stack, indent, body2) => {
|
|
4015
|
-
while (stack.length > 1 && (stack[stack.length - 1]?.indent ?? 0) >= indent && !body2.startsWith("- ")) stack.pop();
|
|
4016
|
-
const top = stack[stack.length - 1];
|
|
4017
|
-
if (top === void 0) return;
|
|
4018
|
-
if (body2.startsWith("- ")) {
|
|
4019
|
-
addItem(top, unquote(body2.slice(2)));
|
|
4020
|
-
return;
|
|
4021
|
-
}
|
|
4022
|
-
const pair = keyValue(body2);
|
|
4023
|
-
if (pair === void 0) return;
|
|
4024
|
-
const parentTree = top.key === void 0 ? top.tree : ensureTree(top.tree, top.key);
|
|
4025
|
-
if (pair.value === "") {
|
|
4026
|
-
stack.push({ indent, tree: parentTree, key: pair.key });
|
|
4027
|
-
return;
|
|
4028
|
-
}
|
|
4029
|
-
parentTree[pair.key] = flowList(pair.value) ?? unquote(pair.value);
|
|
4030
|
-
};
|
|
4031
|
-
var ensureTree = (tree, key) => {
|
|
4032
|
-
const existing = tree[key];
|
|
4033
|
-
if (existing !== void 0 && typeof existing !== "string" && !Array.isArray(existing)) return existing;
|
|
4034
|
-
const fresh3 = {};
|
|
4035
|
-
tree[key] = fresh3;
|
|
4036
|
-
return fresh3;
|
|
4037
|
-
};
|
|
4038
|
-
var parseConfigYaml = (text6) => {
|
|
4039
|
-
const root = {};
|
|
4040
|
-
const stack = [{ indent: -1, tree: root, key: void 0 }];
|
|
4041
|
-
for (const raw of text6.split(/\r?\n/)) {
|
|
4042
|
-
const line2 = stripComment(raw).trimEnd();
|
|
4043
|
-
if (line2.trim() === "" || line2.trim() === "---") continue;
|
|
4044
|
-
const indent = line2.length - line2.trimStart().length;
|
|
4045
|
-
placeLine(stack, indent, line2.trim());
|
|
4046
|
-
}
|
|
4047
|
-
return root;
|
|
4048
|
-
};
|
|
4049
|
-
var isTree = (node) => typeof node === "object" && !Array.isArray(node);
|
|
4050
|
-
var configAt = (tree, ...path) => path.reduce((node, key) => isTree(node) ? node[key] : void 0, tree);
|
|
4051
|
-
var configList = (tree, ...path) => {
|
|
4052
|
-
const node = configAt(tree, ...path);
|
|
4053
|
-
if (typeof node === "string") return node === "" ? [] : [node];
|
|
4054
|
-
return Array.isArray(node) ? node.filter((item) => typeof item === "string") : [];
|
|
4055
|
-
};
|
|
4056
|
-
var configMap = (tree, ...path) => {
|
|
4057
|
-
const node = configAt(tree, ...path);
|
|
4058
|
-
if (!isTree(node)) return {};
|
|
4059
|
-
return Object.fromEntries(
|
|
4060
|
-
Object.entries(node).filter((entry2) => typeof entry2[1] === "string")
|
|
4061
|
-
);
|
|
4062
|
-
};
|
|
4063
|
-
var loadProjectConfig = (runtime, root) => {
|
|
4064
|
-
const text6 = runtime.readText(toAbsolutePath(join12(root, CONFIG_FILE3)));
|
|
4065
|
-
return text6.ok ? parseConfigYaml(text6.value) : {};
|
|
4428
|
+
{ id: "placeholders", kind: "placeholders" },
|
|
4429
|
+
{ id: "secrets", kind: "secrets" },
|
|
4430
|
+
{ id: "prose", kind: "prose" },
|
|
4431
|
+
{ id: "worktree", kind: "worktree" },
|
|
4432
|
+
{ id: "merge", kind: "merge" },
|
|
4433
|
+
{ id: "callers", kind: "callers" },
|
|
4434
|
+
{ id: "pr", kind: "pr", requireOpen: false }
|
|
4435
|
+
],
|
|
4436
|
+
decisionsNeeded: [],
|
|
4437
|
+
implicit: true
|
|
4438
|
+
};
|
|
4066
4439
|
};
|
|
4067
4440
|
|
|
4441
|
+
// src/gate/prepare.ts
|
|
4442
|
+
import { join as join15 } from "node:path";
|
|
4443
|
+
|
|
4068
4444
|
// src/core/yaml/scalar.ts
|
|
4069
4445
|
var ESCAPES = {
|
|
4070
4446
|
'"': '"',
|
|
@@ -4343,8 +4719,8 @@ var TASK_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
|
4343
4719
|
var TASK_SCHEMA = "finitemonkey/task@1";
|
|
4344
4720
|
|
|
4345
4721
|
// src/task/validate.ts
|
|
4346
|
-
var str = (
|
|
4347
|
-
const value =
|
|
4722
|
+
var str = (record3, key, at, issues, required) => {
|
|
4723
|
+
const value = record3[key];
|
|
4348
4724
|
if (value === void 0 || value === null) {
|
|
4349
4725
|
if (required) issues.push(`${at}${key}: required`);
|
|
4350
4726
|
return void 0;
|
|
@@ -4355,8 +4731,8 @@ var str = (record2, key, at, issues, required) => {
|
|
|
4355
4731
|
}
|
|
4356
4732
|
return value;
|
|
4357
4733
|
};
|
|
4358
|
-
var
|
|
4359
|
-
const value =
|
|
4734
|
+
var positive2 = (record3, key, at, issues) => {
|
|
4735
|
+
const value = record3[key];
|
|
4360
4736
|
if (value === void 0 || value === null) return void 0;
|
|
4361
4737
|
if (typeof value === "number" && Number.isInteger(value) && value > 0) return value;
|
|
4362
4738
|
issues.push(`${at}${key}: must be a positive integer`);
|
|
@@ -4375,19 +4751,35 @@ var gitRequire = (value, at, issues) => {
|
|
|
4375
4751
|
if (known2.some((item) => item === void 0)) issues.push(`${at}: use ${GIT_REQUIREMENTS.join(", ")}`);
|
|
4376
4752
|
return known2.filter((item) => item !== void 0);
|
|
4377
4753
|
};
|
|
4378
|
-
var
|
|
4379
|
-
const
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
const
|
|
4383
|
-
const
|
|
4754
|
+
var appFields = (record3, kind, at, issues) => {
|
|
4755
|
+
const port = positive2(record3, "port", at, issues);
|
|
4756
|
+
if (kind === "runtime" && port === void 0) issues.push(`${at}port: required`);
|
|
4757
|
+
if (port !== void 0 && port > 65535) issues.push(`${at}port: must be at most 65535`);
|
|
4758
|
+
const status = positive2(record3, "status", at, issues);
|
|
4759
|
+
const route = str(record3, "route", at, issues, false);
|
|
4760
|
+
const body2 = str(record3, "body", at, issues, false);
|
|
4761
|
+
return {
|
|
4762
|
+
...port === void 0 ? {} : { port },
|
|
4763
|
+
...route === void 0 ? {} : { route },
|
|
4764
|
+
...status === void 0 ? {} : { status },
|
|
4765
|
+
...body2 === void 0 ? {} : { body: body2 }
|
|
4766
|
+
};
|
|
4767
|
+
};
|
|
4768
|
+
var checkFields = (record3, kind, at, issues) => {
|
|
4769
|
+
const run4 = str(record3, "run", at, issues, kind === "command");
|
|
4770
|
+
const timeoutS = positive2(record3, "timeout_s", at, issues);
|
|
4771
|
+
const description = str(record3, "description", at, issues, kind === "manual");
|
|
4772
|
+
const require2 = kind === "git" ? gitRequire(record3["require"] ?? ["committed"], `${at}require`, issues) : void 0;
|
|
4773
|
+
const app = appFields(record3, kind, at, issues);
|
|
4774
|
+
const open = record3["require_open"];
|
|
4384
4775
|
if (open !== void 0 && typeof open !== "boolean") issues.push(`${at}require_open: must be true or false`);
|
|
4385
4776
|
return {
|
|
4386
4777
|
...run4 === void 0 ? {} : { run: run4 },
|
|
4387
4778
|
...timeoutS === void 0 ? {} : { timeoutS },
|
|
4388
4779
|
...description === void 0 ? {} : { description },
|
|
4389
4780
|
...require2 === void 0 ? {} : { require: require2 },
|
|
4390
|
-
...typeof open === "boolean" ? { requireOpen: open } : {}
|
|
4781
|
+
...typeof open === "boolean" ? { requireOpen: open } : {},
|
|
4782
|
+
...app
|
|
4391
4783
|
};
|
|
4392
4784
|
};
|
|
4393
4785
|
var acceptanceItem = (value, index, issues) => {
|
|
@@ -4425,8 +4817,8 @@ var scope = (value, issues) => {
|
|
|
4425
4817
|
...files === void 0 ? {} : { files },
|
|
4426
4818
|
...lines3 === void 0 ? {} : { lines: lines3 }
|
|
4427
4819
|
}))({
|
|
4428
|
-
files:
|
|
4429
|
-
lines:
|
|
4820
|
+
files: positive2(budget, "files", "scope.diff_budget.", issues),
|
|
4821
|
+
lines: positive2(budget, "lines", "scope.diff_budget.", issues)
|
|
4430
4822
|
}) : void 0;
|
|
4431
4823
|
if (budget !== void 0 && budget !== null && !isRecord(budget)) issues.push("scope.diff_budget: must be a mapping");
|
|
4432
4824
|
const protectedValue = value["tests_protected"];
|
|
@@ -4451,6 +4843,7 @@ var validateContract = (value, implicit = false) => {
|
|
|
4451
4843
|
const contractScope = scope(value["scope"], issues);
|
|
4452
4844
|
const checks = acceptance(value["acceptance"], issues);
|
|
4453
4845
|
const decisionsNeeded = strings2(value["decisions_needed"], "decisions_needed", issues);
|
|
4846
|
+
const callersIgnore = strings2(value["callers_ignore"], "callers_ignore", issues);
|
|
4454
4847
|
if (issues.length > 0 || id === void 0 || goal === void 0 || size === void 0) return reject(issues);
|
|
4455
4848
|
return accept({
|
|
4456
4849
|
id,
|
|
@@ -4460,6 +4853,7 @@ var validateContract = (value, implicit = false) => {
|
|
|
4460
4853
|
scope: contractScope,
|
|
4461
4854
|
acceptance: checks,
|
|
4462
4855
|
decisionsNeeded,
|
|
4856
|
+
...callersIgnore.length === 0 ? {} : { callersIgnore },
|
|
4463
4857
|
implicit
|
|
4464
4858
|
});
|
|
4465
4859
|
};
|
|
@@ -4476,7 +4870,7 @@ var parseContract = (id, text6) => {
|
|
|
4476
4870
|
if (valid.value.id !== id) return reject({ kind: "invalid", id, issues: [`id: must match the file name (${id})`] });
|
|
4477
4871
|
return accept(valid.value);
|
|
4478
4872
|
};
|
|
4479
|
-
var
|
|
4873
|
+
var pick2 = (lock, sources, id) => {
|
|
4480
4874
|
const approved = lock.approved[id];
|
|
4481
4875
|
if (approved !== void 0) return { text: approved, source: "lock" };
|
|
4482
4876
|
const base = sources.base(taskRelative(id));
|
|
@@ -4487,7 +4881,7 @@ var pick = (lock, sources, id) => {
|
|
|
4487
4881
|
var loadTask = (files, paths, sources, id) => {
|
|
4488
4882
|
if (!TASK_ID.test(id)) return reject({ kind: "not_found", id });
|
|
4489
4883
|
const lock = readTrust(files, paths);
|
|
4490
|
-
const chosen =
|
|
4884
|
+
const chosen = pick2(lock, sources, id);
|
|
4491
4885
|
if (chosen === void 0) return reject({ kind: "not_found", id });
|
|
4492
4886
|
const parsed = parseContract(id, chosen.text);
|
|
4493
4887
|
if (!parsed.ok) return parsed;
|
|
@@ -4542,19 +4936,19 @@ var readBaseline = (runtime, paths) => {
|
|
|
4542
4936
|
var writeBaseline = (runtime, paths, treeHash2, checks) => writeJsonFile(runtime, paths.cache(BASELINE_FILE), { treeHash: treeHash2, checks: { ...checks } }).ok;
|
|
4543
4937
|
|
|
4544
4938
|
// src/gate/diff-shape.ts
|
|
4545
|
-
import { join as
|
|
4939
|
+
import { join as join14 } from "node:path";
|
|
4546
4940
|
|
|
4547
4941
|
// src/gate/git.ts
|
|
4548
4942
|
var EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
4549
4943
|
var GIT_TIMEOUT_MS = 15e3;
|
|
4550
4944
|
var EXCLUDE_FM = `:(exclude)${FM_DIR}`;
|
|
4551
4945
|
var gitAt = (runtime, cwd) => {
|
|
4552
|
-
const run4 = (
|
|
4946
|
+
const run4 = (args2) => runtime.exec({ command: "git", args: ["-c", "core.quotepath=off", ...args2], cwd, timeoutMs: GIT_TIMEOUT_MS });
|
|
4553
4947
|
return {
|
|
4554
4948
|
cwd,
|
|
4555
4949
|
run: run4,
|
|
4556
|
-
out: (
|
|
4557
|
-
const result = run4(
|
|
4950
|
+
out: (args2) => {
|
|
4951
|
+
const result = run4(args2);
|
|
4558
4952
|
return result.found && result.exitCode === 0 ? result.stdout : void 0;
|
|
4559
4953
|
}
|
|
4560
4954
|
};
|
|
@@ -4649,7 +5043,7 @@ var aheadOfUpstream = (git) => {
|
|
|
4649
5043
|
|
|
4650
5044
|
// src/gate/diff-shape.ts
|
|
4651
5045
|
var untrackedLines = (runtime, root, changes) => changes.filter((change) => change.status === "A").reduce((sum, change) => {
|
|
4652
|
-
const text6 = runtime.readText(toAbsolutePath(
|
|
5046
|
+
const text6 = runtime.readText(toAbsolutePath(join14(root, change.path)));
|
|
4653
5047
|
return sum + (text6.ok ? text6.value.split("\n").length : 0);
|
|
4654
5048
|
}, 0);
|
|
4655
5049
|
var diffShape = (runtime, git, base, changes) => ({
|
|
@@ -4676,18 +5070,18 @@ var ciChecks = (value) => Array.isArray(value) ? value.flatMap((item) => {
|
|
|
4676
5070
|
var parsePrView = (text6) => {
|
|
4677
5071
|
const parsed = parseJsonObject(text6);
|
|
4678
5072
|
if (!parsed.ok) return void 0;
|
|
4679
|
-
const
|
|
4680
|
-
const number =
|
|
5073
|
+
const record3 = parsed.value;
|
|
5074
|
+
const number = record3["number"];
|
|
4681
5075
|
if (typeof number !== "number") return void 0;
|
|
4682
5076
|
return {
|
|
4683
5077
|
number,
|
|
4684
|
-
url: optionalString(
|
|
4685
|
-
state: optionalString(
|
|
4686
|
-
mergeable: optionalString(
|
|
4687
|
-
mergeStateStatus: optionalString(
|
|
4688
|
-
baseRefName: optionalString(
|
|
4689
|
-
headRefOid: optionalString(
|
|
4690
|
-
checks: ciChecks(
|
|
5078
|
+
url: optionalString(record3, "url") ?? "",
|
|
5079
|
+
state: optionalString(record3, "state") ?? "",
|
|
5080
|
+
mergeable: optionalString(record3, "mergeable") ?? "UNKNOWN",
|
|
5081
|
+
mergeStateStatus: optionalString(record3, "mergeStateStatus") ?? "UNKNOWN",
|
|
5082
|
+
baseRefName: optionalString(record3, "baseRefName") ?? "",
|
|
5083
|
+
headRefOid: optionalString(record3, "headRefOid") ?? "",
|
|
5084
|
+
checks: ciChecks(record3["statusCheckRollup"])
|
|
4691
5085
|
};
|
|
4692
5086
|
};
|
|
4693
5087
|
var isConflicting = (pr) => pr.mergeable === "CONFLICTING" || pr.mergeStateStatus === "DIRTY";
|
|
@@ -4701,7 +5095,7 @@ var parseMergeTreeConflicts = (stdout) => {
|
|
|
4701
5095
|
var PR_FIELDS = "number,url,state,mergeable,mergeStateStatus,baseRefName,headRefOid,statusCheckRollup";
|
|
4702
5096
|
var PR_CACHE_TTL_MS = 6e4;
|
|
4703
5097
|
var GH_TIMEOUT_MS = 8e3;
|
|
4704
|
-
var gh = (runtime, cwd,
|
|
5098
|
+
var gh = (runtime, cwd, args2, timeoutMs = GH_TIMEOUT_MS) => runtime.exec({ command: "gh", args: args2, cwd, timeoutMs });
|
|
4705
5099
|
var ghAuthenticated = (runtime, cwd, timeoutMs = 4e3) => {
|
|
4706
5100
|
const result = gh(runtime, cwd, ["auth", "status"], timeoutMs);
|
|
4707
5101
|
if (result.timedOut) return "timeout";
|
|
@@ -4855,10 +5249,10 @@ var applySize = (contract, verdict) => {
|
|
|
4855
5249
|
// src/gate/prepare.ts
|
|
4856
5250
|
var diskFiles = (runtime, root) => ({
|
|
4857
5251
|
read: (relative9) => {
|
|
4858
|
-
const text6 = runtime.readText(toAbsolutePath(
|
|
5252
|
+
const text6 = runtime.readText(toAbsolutePath(join15(root, relative9)));
|
|
4859
5253
|
return text6.ok ? text6.value : void 0;
|
|
4860
5254
|
},
|
|
4861
|
-
exists: (relative9) => runtime.stat(toAbsolutePath(
|
|
5255
|
+
exists: (relative9) => runtime.stat(toAbsolutePath(join15(root, relative9))) !== void 0
|
|
4862
5256
|
});
|
|
4863
5257
|
var baseFiles = (git, base, disk) => ({
|
|
4864
5258
|
read: (relative9) => base === EMPTY_TREE ? disk.read(relative9) : fileAt(git, base, relative9),
|
|
@@ -5120,19 +5514,19 @@ var params = (value) => isRecord(value) ? Object.fromEntries(
|
|
|
5120
5514
|
var notes = (value) => Array.isArray(value) ? value.flatMap(
|
|
5121
5515
|
(item) => isRecord(item) && typeof item["key"] === "string" ? [{ key: item["key"], params: params(item["params"]) }] : []
|
|
5122
5516
|
) : [];
|
|
5123
|
-
var text3 = (
|
|
5124
|
-
const value =
|
|
5517
|
+
var text3 = (record3, key) => {
|
|
5518
|
+
const value = record3[key];
|
|
5125
5519
|
return typeof value === "string" ? value : void 0;
|
|
5126
5520
|
};
|
|
5127
|
-
var optional = (
|
|
5128
|
-
const exitCode =
|
|
5129
|
-
const durationMs =
|
|
5130
|
-
const command = text3(
|
|
5131
|
-
const stdoutSha256 = text3(
|
|
5132
|
-
const releasedBy = text3(
|
|
5133
|
-
const reason = oneOf2(UNVERIFIED_REASONS,
|
|
5134
|
-
const failures =
|
|
5135
|
-
const testCount =
|
|
5521
|
+
var optional = (record3) => {
|
|
5522
|
+
const exitCode = record3["exitCode"];
|
|
5523
|
+
const durationMs = record3["durationMs"];
|
|
5524
|
+
const command = text3(record3, "command");
|
|
5525
|
+
const stdoutSha256 = text3(record3, "stdoutSha256");
|
|
5526
|
+
const releasedBy = text3(record3, "releasedBy");
|
|
5527
|
+
const reason = oneOf2(UNVERIFIED_REASONS, record3["reason"]);
|
|
5528
|
+
const failures = record3["failures"];
|
|
5529
|
+
const testCount = record3["testCount"];
|
|
5136
5530
|
return {
|
|
5137
5531
|
...command === void 0 ? {} : { command },
|
|
5138
5532
|
...typeof exitCode === "number" || exitCode === null ? { exitCode } : {},
|
|
@@ -5147,14 +5541,14 @@ var optional = (record2) => {
|
|
|
5147
5541
|
var parseEvidence = (line2) => {
|
|
5148
5542
|
const parsed = parseJsonObject(line2);
|
|
5149
5543
|
if (!parsed.ok) return void 0;
|
|
5150
|
-
const
|
|
5151
|
-
const status = oneOf2(EVIDENCE_STATUSES,
|
|
5152
|
-
const task = text3(
|
|
5153
|
-
const check2 = text3(
|
|
5154
|
-
const kind = text3(
|
|
5155
|
-
const treeHash2 = text3(
|
|
5156
|
-
const at = text3(
|
|
5157
|
-
if (
|
|
5544
|
+
const record3 = parsed.value;
|
|
5545
|
+
const status = oneOf2(EVIDENCE_STATUSES, record3["status"]);
|
|
5546
|
+
const task = text3(record3, "task");
|
|
5547
|
+
const check2 = text3(record3, "check");
|
|
5548
|
+
const kind = text3(record3, "kind");
|
|
5549
|
+
const treeHash2 = text3(record3, "treeHash");
|
|
5550
|
+
const at = text3(record3, "at");
|
|
5551
|
+
if (record3["schema"] !== EVIDENCE_SCHEMA || status === void 0) return void 0;
|
|
5158
5552
|
if (task === void 0 || check2 === void 0 || kind === void 0 || treeHash2 === void 0 || at === void 0) {
|
|
5159
5553
|
return void 0;
|
|
5160
5554
|
}
|
|
@@ -5166,8 +5560,8 @@ var parseEvidence = (line2) => {
|
|
|
5166
5560
|
status,
|
|
5167
5561
|
treeHash: treeHash2,
|
|
5168
5562
|
at,
|
|
5169
|
-
notes: notes(
|
|
5170
|
-
...optional(
|
|
5563
|
+
notes: notes(record3["notes"]),
|
|
5564
|
+
...optional(record3)
|
|
5171
5565
|
};
|
|
5172
5566
|
};
|
|
5173
5567
|
var readLedger = (runtime, path) => {
|
|
@@ -5706,7 +6100,36 @@ var en_default = {
|
|
|
5706
6100
|
"gate.secrets.warning": "warning: {rule} at {file}:{line}",
|
|
5707
6101
|
"doctor.secrets.remote_credential": "git remote {remotes} has a credential in its URL, stored in plain text in .git/config",
|
|
5708
6102
|
"doctor.secrets.fix_remote_credential": "run `git remote set-url {remote} <url without user:token@>`, use a credential helper or SSH, and rotate the token",
|
|
5709
|
-
"secrets.session.remote_credential": "git remote {remotes} has a credential in its URL (plain text in .git/config). Ask the person to run `git remote set-url {remote} <url without user:token@>` and rotate the token; do not print the URL."
|
|
6103
|
+
"secrets.session.remote_credential": "git remote {remotes} has a credential in its URL (plain text in .git/config). Ask the person to run `git remote set-url {remote} <url without user:token@>` and rotate the token; do not print the URL.",
|
|
6104
|
+
"gate.require.callers": "no removed or renamed symbol is still called",
|
|
6105
|
+
"gate.callers.none": "{count, plural, one {# removed definition} other {# removed definitions}}, no caller left",
|
|
6106
|
+
"gate.callers.open": "{name} is gone but still called in {count, plural, one {# place} other {# places}}",
|
|
6107
|
+
"gate.callers.at": "{name} called at {file}:{line}",
|
|
6108
|
+
"gate.callers.warning": "warning only: {percent}% of callers findings were released with callers_ignore ({released} of {total}), above the 5% ceiling",
|
|
6109
|
+
"command.policy_false_positive.summary": "Record a false positive of a guard in shadow so it is not promoted",
|
|
6110
|
+
"policy.error.person_only": "Only the person can record a false positive, and this does not look like their terminal (no TTY, or running inside Claude Code)",
|
|
6111
|
+
"policy.false_positive.recorded": "Recorded for {guard}: {count, plural, one {# false positive} other {# false positives}}; it stays in shadow until you set it in .finitemonkey/policy.yml.",
|
|
6112
|
+
"command.app_check.summary": "Start the app or reuse the running one, call one route and check the answer",
|
|
6113
|
+
"app.check.ok": "{url} answered {status}",
|
|
6114
|
+
"app.check.crashed": "the app exited with code {code} before opening the port",
|
|
6115
|
+
"app.check.no_port": "the port of {url} did not open before the timeout",
|
|
6116
|
+
"app.check.no_start": "nothing listens on {url} and no start command was given",
|
|
6117
|
+
"app.check.wrong_status": "{url} answered {status}",
|
|
6118
|
+
"app.check.body_mismatch": "{url} answered {status} without the expected text",
|
|
6119
|
+
"app.check.request_failed": "the request to {url} failed",
|
|
6120
|
+
"app.check.usage": "give --port as a number from 1 to 65535; --status and --timeout must be positive numbers",
|
|
6121
|
+
"gate.require.runtime": "the app starts and `{command}` answers as declared",
|
|
6122
|
+
"gate.runtime.ok": "started, {route} answered {status}, stopped",
|
|
6123
|
+
"gate.runtime.reused": "reused the running app, {route} answered {status}",
|
|
6124
|
+
"gate.runtime.crashed": "the app exited with code {code} before opening its port",
|
|
6125
|
+
"gate.runtime.no_port": "port {port} did not open before the timeout",
|
|
6126
|
+
"gate.runtime.no_start": "nothing listens on port {port} and the check has no `run` to start the app",
|
|
6127
|
+
"gate.runtime.no_port_declared": "the runtime check needs a `port`",
|
|
6128
|
+
"gate.runtime.wrong_status": "{route} answered {status}, expected {expected}",
|
|
6129
|
+
"gate.runtime.body_mismatch": '{route} did not contain "{body}"',
|
|
6130
|
+
"gate.runtime.request_failed": "the request to {route} failed: {detail}",
|
|
6131
|
+
"gate.runtime.orphan": "the app on port {port} could not be stopped",
|
|
6132
|
+
"gate.runtime.probe_failed": "the app check did not report a result: {line}"
|
|
5710
6133
|
};
|
|
5711
6134
|
|
|
5712
6135
|
// src/i18n/es.json
|
|
@@ -6230,7 +6653,36 @@ var es_default = {
|
|
|
6230
6653
|
"gate.secrets.warning": "aviso: {rule} en {file}:{line}",
|
|
6231
6654
|
"doctor.secrets.remote_credential": "el remoto git {remotes} tiene una credencial en la URL, guardada en texto plano en .git/config",
|
|
6232
6655
|
"doctor.secrets.fix_remote_credential": "ejecuta `git remote set-url {remote} <url sin usuario:token@>`, usa un credential helper o SSH y rota el token",
|
|
6233
|
-
"secrets.session.remote_credential": "el remoto git {remotes} tiene una credencial en la URL (texto plano en .git/config). Pide a la persona que ejecute `git remote set-url {remote} <url sin usuario:token@>` y rote el token; no imprimas la URL."
|
|
6656
|
+
"secrets.session.remote_credential": "el remoto git {remotes} tiene una credencial en la URL (texto plano en .git/config). Pide a la persona que ejecute `git remote set-url {remote} <url sin usuario:token@>` y rote el token; no imprimas la URL.",
|
|
6657
|
+
"gate.require.callers": "ningún símbolo eliminado o renombrado sigue siendo llamado",
|
|
6658
|
+
"gate.callers.none": "{count, plural, one {# definición eliminada} other {# definiciones eliminadas}}, ningún llamador quedó",
|
|
6659
|
+
"gate.callers.open": "{name} ya no existe, pero se llama en {count, plural, one {# lugar} other {# lugares}}",
|
|
6660
|
+
"gate.callers.at": "{name} llamado en {file}:{line}",
|
|
6661
|
+
"gate.callers.warning": "solo aviso: {percent}% de los hallazgos de callers se liberaron con callers_ignore ({released} de {total}), por encima del techo de 5%",
|
|
6662
|
+
"command.policy_false_positive.summary": "Registra un falso positivo de un guard en shadow para que no se promueva",
|
|
6663
|
+
"policy.error.person_only": "Solo la persona puede registrar un falso positivo, y esto no parece su terminal (sin TTY, o dentro de Claude Code)",
|
|
6664
|
+
"policy.false_positive.recorded": "Registrado para {guard}: {count, plural, one {# falso positivo} other {# falsos positivos}}; se queda en shadow hasta que fijes el modo en .finitemonkey/policy.yml.",
|
|
6665
|
+
"command.app_check.summary": "Arranca la app o reutiliza la que ya corre, llama a una ruta y comprueba la respuesta",
|
|
6666
|
+
"app.check.ok": "{url} respondió {status}",
|
|
6667
|
+
"app.check.crashed": "la app salió con código {code} antes de abrir el puerto",
|
|
6668
|
+
"app.check.no_port": "el puerto de {url} no se abrió antes del tiempo límite",
|
|
6669
|
+
"app.check.no_start": "nada escucha en {url} y no se indicó comando de arranque",
|
|
6670
|
+
"app.check.wrong_status": "{url} respondió {status}",
|
|
6671
|
+
"app.check.body_mismatch": "{url} respondió {status} sin el texto esperado",
|
|
6672
|
+
"app.check.request_failed": "la petición a {url} falló",
|
|
6673
|
+
"app.check.usage": "indica --port como número de 1 a 65535; --status y --timeout deben ser números positivos",
|
|
6674
|
+
"gate.require.runtime": "la app arranca y `{command}` responde como se declaró",
|
|
6675
|
+
"gate.runtime.ok": "arrancó, {route} respondió {status}, detenida",
|
|
6676
|
+
"gate.runtime.reused": "reutilizó la app en marcha, {route} respondió {status}",
|
|
6677
|
+
"gate.runtime.crashed": "la app salió con código {code} antes de abrir su puerto",
|
|
6678
|
+
"gate.runtime.no_port": "el puerto {port} no se abrió antes del tiempo límite",
|
|
6679
|
+
"gate.runtime.no_start": "nada escucha en el puerto {port} y la comprobación no tiene `run` para arrancar la app",
|
|
6680
|
+
"gate.runtime.no_port_declared": "la comprobación runtime necesita `port`",
|
|
6681
|
+
"gate.runtime.wrong_status": "{route} respondió {status}, se esperaba {expected}",
|
|
6682
|
+
"gate.runtime.body_mismatch": '{route} no contenía "{body}"',
|
|
6683
|
+
"gate.runtime.request_failed": "la petición a {route} falló: {detail}",
|
|
6684
|
+
"gate.runtime.orphan": "no se pudo detener la app del puerto {port}",
|
|
6685
|
+
"gate.runtime.probe_failed": "la comprobación de la app no devolvió resultado: {line}"
|
|
6234
6686
|
};
|
|
6235
6687
|
|
|
6236
6688
|
// src/i18n/pt-BR.json
|
|
@@ -6754,7 +7206,36 @@ var pt_BR_default = {
|
|
|
6754
7206
|
"gate.secrets.warning": "aviso: {rule} em {file}:{line}",
|
|
6755
7207
|
"doctor.secrets.remote_credential": "o remoto git {remotes} tem uma credencial na URL, guardada em texto puro no .git/config",
|
|
6756
7208
|
"doctor.secrets.fix_remote_credential": "rode `git remote set-url {remote} <url sem usuario:token@>`, use um credential helper ou SSH e rotacione o token",
|
|
6757
|
-
"secrets.session.remote_credential": "o remoto git {remotes} tem uma credencial na URL (texto puro no .git/config). Peça para a pessoa rodar `git remote set-url {remote} <url sem usuario:token@>` e rotacionar o token; não imprima a URL."
|
|
7209
|
+
"secrets.session.remote_credential": "o remoto git {remotes} tem uma credencial na URL (texto puro no .git/config). Peça para a pessoa rodar `git remote set-url {remote} <url sem usuario:token@>` e rotacionar o token; não imprima a URL.",
|
|
7210
|
+
"gate.require.callers": "nenhum símbolo removido ou renomeado continua sendo chamado",
|
|
7211
|
+
"gate.callers.none": "{count, plural, one {# definição removida} other {# definições removidas}}, nenhum chamador sobrou",
|
|
7212
|
+
"gate.callers.open": "{name} saiu, mas é chamado em {count, plural, one {# lugar} other {# lugares}}",
|
|
7213
|
+
"gate.callers.at": "{name} chamado em {file}:{line}",
|
|
7214
|
+
"gate.callers.warning": "só aviso: {percent}% dos achados de callers foram liberados com callers_ignore ({released} de {total}), acima do teto de 5%",
|
|
7215
|
+
"command.policy_false_positive.summary": "Registra um falso positivo de um guard em shadow para ele não ser promovido",
|
|
7216
|
+
"policy.error.person_only": "Só a pessoa pode registrar um falso positivo, e isto não parece o terminal dela (sem TTY, ou rodando dentro do Claude Code)",
|
|
7217
|
+
"policy.false_positive.recorded": "Registrado para {guard}: {count, plural, one {# falso positivo} other {# falsos positivos}}; ele fica em shadow até você definir o modo em .finitemonkey/policy.yml.",
|
|
7218
|
+
"command.app_check.summary": "Sobe o app ou reaproveita o que está rodando, chama uma rota e confere a resposta",
|
|
7219
|
+
"app.check.ok": "{url} respondeu {status}",
|
|
7220
|
+
"app.check.crashed": "o app saiu com código {code} antes de abrir a porta",
|
|
7221
|
+
"app.check.no_port": "a porta de {url} não abriu antes do tempo limite",
|
|
7222
|
+
"app.check.no_start": "nada escuta em {url} e nenhum comando de início foi dado",
|
|
7223
|
+
"app.check.wrong_status": "{url} respondeu {status}",
|
|
7224
|
+
"app.check.body_mismatch": "{url} respondeu {status} sem o texto esperado",
|
|
7225
|
+
"app.check.request_failed": "a requisição para {url} falhou",
|
|
7226
|
+
"app.check.usage": "passe --port como número de 1 a 65535; --status e --timeout precisam ser números positivos",
|
|
7227
|
+
"gate.require.runtime": "o app sobe e `{command}` responde como declarado",
|
|
7228
|
+
"gate.runtime.ok": "subiu, {route} respondeu {status}, parado",
|
|
7229
|
+
"gate.runtime.reused": "reaproveitou o app rodando, {route} respondeu {status}",
|
|
7230
|
+
"gate.runtime.crashed": "o app saiu com código {code} antes de abrir a porta",
|
|
7231
|
+
"gate.runtime.no_port": "a porta {port} não abriu antes do tempo limite",
|
|
7232
|
+
"gate.runtime.no_start": "nada escuta na porta {port} e a checagem não tem `run` para subir o app",
|
|
7233
|
+
"gate.runtime.no_port_declared": "a checagem runtime precisa de `port`",
|
|
7234
|
+
"gate.runtime.wrong_status": "{route} respondeu {status}, esperado {expected}",
|
|
7235
|
+
"gate.runtime.body_mismatch": '{route} não continha "{body}"',
|
|
7236
|
+
"gate.runtime.request_failed": "a requisição para {route} falhou: {detail}",
|
|
7237
|
+
"gate.runtime.orphan": "o app na porta {port} não pôde ser parado",
|
|
7238
|
+
"gate.runtime.probe_failed": "a checagem do app não devolveu resultado: {line}"
|
|
6758
7239
|
};
|
|
6759
7240
|
|
|
6760
7241
|
// src/i18n/review.json
|
|
@@ -7279,7 +7760,36 @@ var review_default = {
|
|
|
7279
7760
|
"gate.secrets.warning": "machine",
|
|
7280
7761
|
"doctor.secrets.remote_credential": "machine",
|
|
7281
7762
|
"doctor.secrets.fix_remote_credential": "machine",
|
|
7282
|
-
"secrets.session.remote_credential": "machine"
|
|
7763
|
+
"secrets.session.remote_credential": "machine",
|
|
7764
|
+
"gate.require.callers": "machine",
|
|
7765
|
+
"gate.callers.none": "machine",
|
|
7766
|
+
"gate.callers.open": "machine",
|
|
7767
|
+
"gate.callers.at": "machine",
|
|
7768
|
+
"gate.callers.warning": "machine",
|
|
7769
|
+
"command.policy_false_positive.summary": "machine",
|
|
7770
|
+
"policy.error.person_only": "machine",
|
|
7771
|
+
"policy.false_positive.recorded": "machine",
|
|
7772
|
+
"command.app_check.summary": "machine",
|
|
7773
|
+
"app.check.ok": "machine",
|
|
7774
|
+
"app.check.crashed": "machine",
|
|
7775
|
+
"app.check.no_port": "machine",
|
|
7776
|
+
"app.check.no_start": "machine",
|
|
7777
|
+
"app.check.wrong_status": "machine",
|
|
7778
|
+
"app.check.body_mismatch": "machine",
|
|
7779
|
+
"app.check.request_failed": "machine",
|
|
7780
|
+
"app.check.usage": "machine",
|
|
7781
|
+
"gate.require.runtime": "machine",
|
|
7782
|
+
"gate.runtime.ok": "machine",
|
|
7783
|
+
"gate.runtime.reused": "machine",
|
|
7784
|
+
"gate.runtime.crashed": "machine",
|
|
7785
|
+
"gate.runtime.no_port": "machine",
|
|
7786
|
+
"gate.runtime.no_start": "machine",
|
|
7787
|
+
"gate.runtime.no_port_declared": "machine",
|
|
7788
|
+
"gate.runtime.wrong_status": "machine",
|
|
7789
|
+
"gate.runtime.body_mismatch": "machine",
|
|
7790
|
+
"gate.runtime.request_failed": "machine",
|
|
7791
|
+
"gate.runtime.orphan": "machine",
|
|
7792
|
+
"gate.runtime.probe_failed": "machine"
|
|
7283
7793
|
},
|
|
7284
7794
|
es: {
|
|
7285
7795
|
"command.init.summary": "machine",
|
|
@@ -7801,7 +8311,36 @@ var review_default = {
|
|
|
7801
8311
|
"gate.secrets.warning": "machine",
|
|
7802
8312
|
"doctor.secrets.remote_credential": "machine",
|
|
7803
8313
|
"doctor.secrets.fix_remote_credential": "machine",
|
|
7804
|
-
"secrets.session.remote_credential": "machine"
|
|
8314
|
+
"secrets.session.remote_credential": "machine",
|
|
8315
|
+
"gate.require.callers": "machine",
|
|
8316
|
+
"gate.callers.none": "machine",
|
|
8317
|
+
"gate.callers.open": "machine",
|
|
8318
|
+
"gate.callers.at": "machine",
|
|
8319
|
+
"gate.callers.warning": "machine",
|
|
8320
|
+
"command.policy_false_positive.summary": "machine",
|
|
8321
|
+
"policy.error.person_only": "machine",
|
|
8322
|
+
"policy.false_positive.recorded": "machine",
|
|
8323
|
+
"command.app_check.summary": "machine",
|
|
8324
|
+
"app.check.ok": "machine",
|
|
8325
|
+
"app.check.crashed": "machine",
|
|
8326
|
+
"app.check.no_port": "machine",
|
|
8327
|
+
"app.check.no_start": "machine",
|
|
8328
|
+
"app.check.wrong_status": "machine",
|
|
8329
|
+
"app.check.body_mismatch": "machine",
|
|
8330
|
+
"app.check.request_failed": "machine",
|
|
8331
|
+
"app.check.usage": "machine",
|
|
8332
|
+
"gate.require.runtime": "machine",
|
|
8333
|
+
"gate.runtime.ok": "machine",
|
|
8334
|
+
"gate.runtime.reused": "machine",
|
|
8335
|
+
"gate.runtime.crashed": "machine",
|
|
8336
|
+
"gate.runtime.no_port": "machine",
|
|
8337
|
+
"gate.runtime.no_start": "machine",
|
|
8338
|
+
"gate.runtime.no_port_declared": "machine",
|
|
8339
|
+
"gate.runtime.wrong_status": "machine",
|
|
8340
|
+
"gate.runtime.body_mismatch": "machine",
|
|
8341
|
+
"gate.runtime.request_failed": "machine",
|
|
8342
|
+
"gate.runtime.orphan": "machine",
|
|
8343
|
+
"gate.runtime.probe_failed": "machine"
|
|
7805
8344
|
}
|
|
7806
8345
|
};
|
|
7807
8346
|
|
|
@@ -8080,6 +8619,256 @@ var recordReview = (runtime, paths, treeHash2, agent) => {
|
|
|
8080
8619
|
return writeJsonFile(runtime, paths.cache(REVIEWS_FILE), { reviews: reviews.map((review2) => ({ ...review2 })) }).ok;
|
|
8081
8620
|
};
|
|
8082
8621
|
|
|
8622
|
+
// src/gate/callers/rate.ts
|
|
8623
|
+
var FLAGGED_EVENT = "callers.flagged";
|
|
8624
|
+
var RELEASED_EVENT = "callers.released";
|
|
8625
|
+
var MIN_SAMPLE = 20;
|
|
8626
|
+
var MAX_FALSE_POSITIVE = 0.05;
|
|
8627
|
+
var pairs = (record3) => {
|
|
8628
|
+
const { task, names } = record3;
|
|
8629
|
+
if (typeof task !== "string" || !Array.isArray(names)) return [];
|
|
8630
|
+
return names.filter((name) => typeof name === "string").map((name) => `${task}\0${name}`);
|
|
8631
|
+
};
|
|
8632
|
+
var callersRate = (runtime, paths) => {
|
|
8633
|
+
const text6 = runtime.readText(paths.log);
|
|
8634
|
+
const flagged = /* @__PURE__ */ new Set();
|
|
8635
|
+
const released2 = /* @__PURE__ */ new Set();
|
|
8636
|
+
for (const line2 of text6.ok ? text6.value.split("\n") : []) {
|
|
8637
|
+
const parsed = line2.trim() === "" ? void 0 : parseJsonObject(line2);
|
|
8638
|
+
if (parsed === void 0 || !parsed.ok || !isRecord(parsed.value)) continue;
|
|
8639
|
+
const event = parsed.value["event"];
|
|
8640
|
+
const target = event === FLAGGED_EVENT ? flagged : event === RELEASED_EVENT ? released2 : void 0;
|
|
8641
|
+
if (target !== void 0) for (const pair of pairs(parsed.value)) target.add(pair);
|
|
8642
|
+
}
|
|
8643
|
+
const total = (/* @__PURE__ */ new Set([...flagged, ...released2])).size;
|
|
8644
|
+
const rate = total === 0 ? 0 : released2.size / total;
|
|
8645
|
+
return {
|
|
8646
|
+
flagged: total,
|
|
8647
|
+
released: released2.size,
|
|
8648
|
+
rate,
|
|
8649
|
+
downgraded: total >= MIN_SAMPLE && rate > MAX_FALSE_POSITIVE
|
|
8650
|
+
};
|
|
8651
|
+
};
|
|
8652
|
+
|
|
8653
|
+
// src/gate/callers/scan.ts
|
|
8654
|
+
import { join as join16 } from "node:path";
|
|
8655
|
+
|
|
8656
|
+
// src/gate/callers/imports.ts
|
|
8657
|
+
import { posix as posix2 } from "node:path";
|
|
8658
|
+
var JS_SPECIFIER = /(?:\bfrom\s*|\bimport\s*\(?\s*|\brequire\s*\(\s*)["']([^"']+)["']/g;
|
|
8659
|
+
var JS_EXTENSION = /\.(?:[cm]?[jt]sx?)$/;
|
|
8660
|
+
var stripModule = (path) => path.replace(JS_EXTENSION, "").replace(/\/index$/, "");
|
|
8661
|
+
var jsSpecifiers = (text6) => [...text6.matchAll(JS_SPECIFIER)].map((match) => match[1] ?? "");
|
|
8662
|
+
var jsImports = (callerPath, callerText, origin) => {
|
|
8663
|
+
const target = stripModule(origin);
|
|
8664
|
+
return jsSpecifiers(callerText).some((specifier) => {
|
|
8665
|
+
if (specifier.startsWith(".")) {
|
|
8666
|
+
return stripModule(posix2.normalize(posix2.join(posix2.dirname(callerPath), specifier))) === target;
|
|
8667
|
+
}
|
|
8668
|
+
const bare = stripModule(specifier.replace(/^[@~#]\//, ""));
|
|
8669
|
+
return target === bare || target.endsWith(`/${bare}`);
|
|
8670
|
+
});
|
|
8671
|
+
};
|
|
8672
|
+
var pyImports = (callerText, origin) => {
|
|
8673
|
+
const stem = posix2.basename(origin).replace(/\.py$/, "");
|
|
8674
|
+
const module = stem === "__init__" ? posix2.basename(posix2.dirname(origin)) : stem;
|
|
8675
|
+
const word2 = new RegExp(`\\b${module}\\b`);
|
|
8676
|
+
return callerText.split(/\r?\n/).some((line2) => /^\s*(?:from|import)\s/.test(line2) && word2.test(line2));
|
|
8677
|
+
};
|
|
8678
|
+
var goImports = (callerPath, callerText, origin) => {
|
|
8679
|
+
const dir = posix2.dirname(origin);
|
|
8680
|
+
if (posix2.dirname(callerPath) === dir) return true;
|
|
8681
|
+
const last = posix2.basename(dir);
|
|
8682
|
+
return new RegExp(`"[^"]*\\b${last}"`).test(callerText);
|
|
8683
|
+
};
|
|
8684
|
+
var importsOrigin = (language, callerPath, callerText, origins) => origins.some((origin) => {
|
|
8685
|
+
if (callerPath === origin) return true;
|
|
8686
|
+
switch (language.imports) {
|
|
8687
|
+
case "none":
|
|
8688
|
+
return true;
|
|
8689
|
+
case "package":
|
|
8690
|
+
return goImports(callerPath, callerText, origin);
|
|
8691
|
+
case "module":
|
|
8692
|
+
return language.id === "py" ? pyImports(callerText, origin) : jsImports(callerPath, callerText, origin);
|
|
8693
|
+
}
|
|
8694
|
+
});
|
|
8695
|
+
|
|
8696
|
+
// src/gate/callers/languages.ts
|
|
8697
|
+
import { extname } from "node:path";
|
|
8698
|
+
var JS_NAME = "[A-Za-z_$][\\w$]*";
|
|
8699
|
+
var LANGUAGES2 = [
|
|
8700
|
+
{
|
|
8701
|
+
id: "js",
|
|
8702
|
+
extensions: [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", ".cts"],
|
|
8703
|
+
definitions: [
|
|
8704
|
+
new RegExp(`^\\s*(?:export\\s+)?(?:default\\s+)?(?:async\\s+)?function\\s*\\*?\\s*(${JS_NAME})`),
|
|
8705
|
+
new RegExp(`^\\s*(?:export\\s+)?(?:default\\s+)?(?:abstract\\s+)?class\\s+(${JS_NAME})`),
|
|
8706
|
+
new RegExp(`^\\s*export\\s+(?:declare\\s+)?(?:const|let|var|enum|interface|type)\\s+(${JS_NAME})`),
|
|
8707
|
+
new RegExp(
|
|
8708
|
+
`^\\s+(?:public\\s+)?(?:static\\s+)?(?:async\\s+)?(${JS_NAME})\\s*\\([^)]*\\)\\s*(?::[^{=]+)?\\{\\s*$`
|
|
8709
|
+
)
|
|
8710
|
+
],
|
|
8711
|
+
imports: "module",
|
|
8712
|
+
comment: /^\s*(?:\/\/|\/\*|\*)/
|
|
8713
|
+
},
|
|
8714
|
+
{
|
|
8715
|
+
id: "py",
|
|
8716
|
+
extensions: [".py"],
|
|
8717
|
+
definitions: [/^\s*(?:async\s+)?def\s+([A-Za-z]\w*)/, /^\s*class\s+([A-Za-z]\w*)/],
|
|
8718
|
+
imports: "module",
|
|
8719
|
+
comment: /^\s*#/
|
|
8720
|
+
},
|
|
8721
|
+
{
|
|
8722
|
+
id: "go",
|
|
8723
|
+
extensions: [".go"],
|
|
8724
|
+
definitions: [/^func\s+(?:\([^)]*\)\s*)?([A-Za-z]\w*)/, /^type\s+([A-Za-z]\w*)\s+(?:struct|interface)/],
|
|
8725
|
+
imports: "package",
|
|
8726
|
+
comment: /^\s*\/\//
|
|
8727
|
+
},
|
|
8728
|
+
{
|
|
8729
|
+
id: "rb",
|
|
8730
|
+
extensions: [".rb"],
|
|
8731
|
+
definitions: [/^\s*def\s+(?:self\.)?([A-Za-z]\w*[?!]?)/, /^\s*(?:class|module)\s+([A-Z]\w*)/],
|
|
8732
|
+
imports: "none",
|
|
8733
|
+
comment: /^\s*#/
|
|
8734
|
+
},
|
|
8735
|
+
{
|
|
8736
|
+
id: "rs",
|
|
8737
|
+
extensions: [".rs"],
|
|
8738
|
+
definitions: [
|
|
8739
|
+
/^\s*(?:pub(?:\([^)]*\))?\s+)?(?:async\s+)?fn\s+([A-Za-z]\w*)/,
|
|
8740
|
+
/^\s*(?:pub\s+)?(?:struct|enum|trait)\s+([A-Z]\w*)/
|
|
8741
|
+
],
|
|
8742
|
+
imports: "none",
|
|
8743
|
+
comment: /^\s*\/\//
|
|
8744
|
+
}
|
|
8745
|
+
];
|
|
8746
|
+
var KEYWORDS = /* @__PURE__ */ new Set([
|
|
8747
|
+
"if",
|
|
8748
|
+
"for",
|
|
8749
|
+
"while",
|
|
8750
|
+
"switch",
|
|
8751
|
+
"catch",
|
|
8752
|
+
"function",
|
|
8753
|
+
"return",
|
|
8754
|
+
"constructor",
|
|
8755
|
+
"with",
|
|
8756
|
+
"do",
|
|
8757
|
+
"else"
|
|
8758
|
+
]);
|
|
8759
|
+
var languageOf = (path) => {
|
|
8760
|
+
const extension = extname(path).toLowerCase();
|
|
8761
|
+
return LANGUAGES2.find((language) => language.extensions.includes(extension));
|
|
8762
|
+
};
|
|
8763
|
+
var definitionsIn = (language, text6) => {
|
|
8764
|
+
const names = /* @__PURE__ */ new Set();
|
|
8765
|
+
for (const line2 of text6.split(/\r?\n/)) {
|
|
8766
|
+
for (const pattern of language.definitions) {
|
|
8767
|
+
const name = pattern.exec(line2)?.[1];
|
|
8768
|
+
if (name !== void 0 && !KEYWORDS.has(name)) names.add(name);
|
|
8769
|
+
}
|
|
8770
|
+
}
|
|
8771
|
+
return names;
|
|
8772
|
+
};
|
|
8773
|
+
var definesName = (language, line2, name) => language.definitions.some((pattern) => pattern.exec(line2)?.[1] === name);
|
|
8774
|
+
|
|
8775
|
+
// src/gate/callers/scan.ts
|
|
8776
|
+
var MAX_SYMBOLS = 200;
|
|
8777
|
+
var current = (runtime, root, path) => {
|
|
8778
|
+
const text6 = runtime.readText(toAbsolutePath(join16(root, path)));
|
|
8779
|
+
return text6.ok ? text6.value : "";
|
|
8780
|
+
};
|
|
8781
|
+
var keyOf = (language, name) => `${language.id}\0${name}`;
|
|
8782
|
+
var redefinedNames = (runtime, git, changes) => {
|
|
8783
|
+
const keys = /* @__PURE__ */ new Set();
|
|
8784
|
+
for (const change of changes) {
|
|
8785
|
+
const language = languageOf(change.path);
|
|
8786
|
+
if (language === void 0 || change.status === "D") continue;
|
|
8787
|
+
for (const name of definitionsIn(language, current(runtime, git.cwd, change.path))) keys.add(keyOf(language, name));
|
|
8788
|
+
}
|
|
8789
|
+
return keys;
|
|
8790
|
+
};
|
|
8791
|
+
var removedSymbols = (runtime, git, base, changes) => {
|
|
8792
|
+
const redefined = redefinedNames(runtime, git, changes);
|
|
8793
|
+
const found = /* @__PURE__ */ new Map();
|
|
8794
|
+
for (const change of changes) {
|
|
8795
|
+
const language = languageOf(change.oldPath ?? change.path);
|
|
8796
|
+
if (language === void 0 || change.status === "A") continue;
|
|
8797
|
+
const before = definitionsIn(language, fileAt(git, base, change.oldPath ?? change.path) ?? "");
|
|
8798
|
+
const origins = [change.path, ...change.oldPath === void 0 ? [] : [change.oldPath]];
|
|
8799
|
+
for (const name of before) {
|
|
8800
|
+
const key = keyOf(language, name);
|
|
8801
|
+
if (redefined.has(key)) continue;
|
|
8802
|
+
const prior = found.get(key);
|
|
8803
|
+
found.set(key, { name, language, origins: [...prior?.origins ?? [], ...origins] });
|
|
8804
|
+
}
|
|
8805
|
+
}
|
|
8806
|
+
return [...found.values()].slice(0, MAX_SYMBOLS);
|
|
8807
|
+
};
|
|
8808
|
+
var parseGrep = (output) => output.split("\n").filter((row) => row !== "").flatMap((row) => {
|
|
8809
|
+
const [file = "", line2 = "", ...rest] = row.split("\0");
|
|
8810
|
+
const number = Number(line2);
|
|
8811
|
+
return file === "" || !Number.isInteger(number) ? [] : [{ file: toPosixPath(file), line: number, text: rest.join("\0") }];
|
|
8812
|
+
});
|
|
8813
|
+
var grep = (git, names) => {
|
|
8814
|
+
if (names.length === 0) return [];
|
|
8815
|
+
const patterns = names.flatMap((name) => ["-e", name]);
|
|
8816
|
+
return parseGrep(
|
|
8817
|
+
git.out(["grep", "-n", "-z", "-w", "-I", "--untracked", "--no-color", ...patterns, "--", ".", EXCLUDE_FM]) ?? ""
|
|
8818
|
+
);
|
|
8819
|
+
};
|
|
8820
|
+
var mentions = (text6, name) => new RegExp(`(?<![\\w$])${name.replace(/[$?!]/g, "\\$&")}(?![\\w$])`).test(text6);
|
|
8821
|
+
var callSites = (runtime, git, symbols) => {
|
|
8822
|
+
const hits = grep(git, [...new Set(symbols.map((symbol) => symbol.name))]);
|
|
8823
|
+
const texts = /* @__PURE__ */ new Map();
|
|
8824
|
+
const textOf4 = (file) => {
|
|
8825
|
+
const cached2 = texts.get(file) ?? current(runtime, git.cwd, file);
|
|
8826
|
+
texts.set(file, cached2);
|
|
8827
|
+
return cached2;
|
|
8828
|
+
};
|
|
8829
|
+
return symbols.flatMap(
|
|
8830
|
+
(symbol) => hits.filter((hit3) => languageOf(hit3.file)?.id === symbol.language.id && mentions(hit3.text, symbol.name)).filter((hit3) => !symbol.language.comment.test(hit3.text) && !definesName(symbol.language, hit3.text, symbol.name)).filter((hit3) => importsOrigin(symbol.language, hit3.file, textOf4(hit3.file), symbol.origins)).map((hit3) => ({ name: symbol.name, file: hit3.file, line: hit3.line }))
|
|
8831
|
+
);
|
|
8832
|
+
};
|
|
8833
|
+
|
|
8834
|
+
// src/gate/checks/callers.ts
|
|
8835
|
+
var MAX_SITES = 10;
|
|
8836
|
+
var byName = (sites) => {
|
|
8837
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
8838
|
+
for (const site of sites) grouped.set(site.name, [...grouped.get(site.name) ?? [], site]);
|
|
8839
|
+
return grouped;
|
|
8840
|
+
};
|
|
8841
|
+
var siteNotes = (open) => [
|
|
8842
|
+
...[...open].map(([name, sites]) => note("gate.callers.open", { name, count: sites.length })),
|
|
8843
|
+
...[...open.values()].flat().slice(0, MAX_SITES).map((site) => note("gate.callers.at", { name: site.name, file: site.file, line: site.line }))
|
|
8844
|
+
];
|
|
8845
|
+
var record = (ctx, event, names) => {
|
|
8846
|
+
if (names.length > 0) logEvent(ctx.runtime, ctx.paths, event, { task: ctx.contract.id, names: [...names] });
|
|
8847
|
+
};
|
|
8848
|
+
var callersCheck = (_check, ctx) => {
|
|
8849
|
+
const symbols = removedSymbols(ctx.runtime, ctx.git, ctx.base, ctx.changes);
|
|
8850
|
+
const grouped = byName(callSites(ctx.runtime, ctx.git, symbols));
|
|
8851
|
+
const ignore = new Set(ctx.contract.callersIgnore ?? []);
|
|
8852
|
+
const open = new Map([...grouped].filter(([name]) => !ignore.has(name)));
|
|
8853
|
+
record(
|
|
8854
|
+
ctx,
|
|
8855
|
+
RELEASED_EVENT,
|
|
8856
|
+
[...grouped.keys()].filter((name) => ignore.has(name))
|
|
8857
|
+
);
|
|
8858
|
+
record(ctx, FLAGGED_EVENT, [...open.keys()]);
|
|
8859
|
+
if (open.size === 0) return passed([note("gate.callers.none", { count: symbols.length })]);
|
|
8860
|
+
const rate = callersRate(ctx.runtime, ctx.paths);
|
|
8861
|
+
if (!rate.downgraded) return failed(siteNotes(open));
|
|
8862
|
+
return passed([
|
|
8863
|
+
note("gate.callers.warning", {
|
|
8864
|
+
percent: Math.round(rate.rate * 100),
|
|
8865
|
+
released: rate.released,
|
|
8866
|
+
total: rate.flagged
|
|
8867
|
+
}),
|
|
8868
|
+
...siteNotes(open)
|
|
8869
|
+
]);
|
|
8870
|
+
};
|
|
8871
|
+
|
|
8083
8872
|
// src/gate/test-summary.ts
|
|
8084
8873
|
var ANSI2 = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
8085
8874
|
var COUNTERS = [
|
|
@@ -8276,13 +9065,13 @@ var manualCheck = (check2, ctx) => {
|
|
|
8276
9065
|
};
|
|
8277
9066
|
|
|
8278
9067
|
// src/gate/checks/placeholders.ts
|
|
8279
|
-
import { join as
|
|
9068
|
+
import { join as join17 } from "node:path";
|
|
8280
9069
|
var MAX_LISTED2 = 10;
|
|
8281
9070
|
var SKIP_FILES = /\.(md|mdx|txt|rst|lock|snap|svg|png|jpg|gif|json)$|(^|\/)(CHANGELOG|LICENSE)/i;
|
|
8282
9071
|
var PLACEHOLDER = /\b(TODO|FIXME|XXX)\b|not implemented|NotImplementedError|unimplemented!|todo!\(|lorem ipsum/i;
|
|
8283
9072
|
var findPlaceholders = (lines3) => lines3.filter((line2) => !isTestFile(line2.path) && !SKIP_FILES.test(line2.path) && PLACEHOLDER.test(line2.text));
|
|
8284
9073
|
var untrackedAdded = (ctx) => ctx.changes.filter((change) => change.status === "A" && !SKIP_FILES.test(change.path)).flatMap((change) => {
|
|
8285
|
-
const text6 = ctx.runtime.readText(toAbsolutePath(
|
|
9074
|
+
const text6 = ctx.runtime.readText(toAbsolutePath(join17(ctx.paths.root, change.path)));
|
|
8286
9075
|
return text6.ok ? text6.value.split("\n").map((line2, index) => ({ path: change.path, line: index + 1, text: line2 })) : [];
|
|
8287
9076
|
});
|
|
8288
9077
|
var placeholdersCheck = (_check, ctx) => {
|
|
@@ -8931,9 +9720,9 @@ var isFlagged = (report2) => report2.spans.length > 0;
|
|
|
8931
9720
|
var MARKDOWN = /\.(?:md|markdown|mdx)$/i;
|
|
8932
9721
|
var RECORD_FILE = /(?:^|\/)(?:CHANGELOG|RELEASE[-_]?NOTES|HISTORY)[^/]*$/i;
|
|
8933
9722
|
var contextOf = (file) => RECORD_FILE.test(toPosixPath(file)) ? "record" : "docs";
|
|
8934
|
-
var listed = (runtime, root,
|
|
8935
|
-
const result = runtime.git(
|
|
8936
|
-
if (!result.ok) return reject(`git ${
|
|
9723
|
+
var listed = (runtime, root, args2) => {
|
|
9724
|
+
const result = runtime.git(args2, root);
|
|
9725
|
+
if (!result.ok) return reject(`git ${args2.join(" ")} failed`);
|
|
8937
9726
|
return accept(result.stdout.split("\0").map(toPosixPath));
|
|
8938
9727
|
};
|
|
8939
9728
|
var changedMarkdown = (runtime, root) => {
|
|
@@ -9013,16 +9802,131 @@ var reviewCheck = (check2, ctx) => {
|
|
|
9013
9802
|
return failed([...sizeNotes(ctx), note("gate.review.missing", { check: check2.id })]);
|
|
9014
9803
|
};
|
|
9015
9804
|
|
|
9805
|
+
// src/app/parse.ts
|
|
9806
|
+
var KINDS2 = [
|
|
9807
|
+
"ok",
|
|
9808
|
+
"crashed",
|
|
9809
|
+
"no_port",
|
|
9810
|
+
"wrong_status",
|
|
9811
|
+
"body_mismatch",
|
|
9812
|
+
"request_failed",
|
|
9813
|
+
"no_start"
|
|
9814
|
+
];
|
|
9815
|
+
var optionalNumber = (value) => typeof value === "number" ? value : void 0;
|
|
9816
|
+
var parseAppCheck = (stdout) => {
|
|
9817
|
+
const envelope = parseJsonObject(stdout.trim());
|
|
9818
|
+
if (!envelope.ok) return void 0;
|
|
9819
|
+
const data = envelope.value["data"];
|
|
9820
|
+
if (!isRecord(data)) return void 0;
|
|
9821
|
+
const kind = KINDS2.find((item) => item === data["kind"]);
|
|
9822
|
+
const { output, reused, stopped, durationMs, exitCode, bodyExcerpt } = data;
|
|
9823
|
+
if (kind === void 0 || typeof output !== "string" || typeof reused !== "boolean" || typeof stopped !== "boolean")
|
|
9824
|
+
return void 0;
|
|
9825
|
+
const status = optionalNumber(data["status"]);
|
|
9826
|
+
return {
|
|
9827
|
+
kind,
|
|
9828
|
+
output,
|
|
9829
|
+
reused,
|
|
9830
|
+
stopped,
|
|
9831
|
+
durationMs: optionalNumber(durationMs) ?? 0,
|
|
9832
|
+
...status === void 0 ? {} : { status },
|
|
9833
|
+
...typeof exitCode === "number" || exitCode === null ? { exitCode } : {},
|
|
9834
|
+
...typeof bodyExcerpt === "string" ? { bodyExcerpt } : {}
|
|
9835
|
+
};
|
|
9836
|
+
};
|
|
9837
|
+
|
|
9838
|
+
// src/gate/checks/runtime.ts
|
|
9839
|
+
var PROBE_MARGIN_MS = 15e3;
|
|
9840
|
+
var MIN_REMAINING_MS2 = 5e3;
|
|
9841
|
+
var TAIL2 = 160;
|
|
9842
|
+
var lastLine = (text6) => {
|
|
9843
|
+
const rows = text6.split(/\r?\n/).filter((row) => row.trim() !== "" && !/^\s*at\s|^Node\.js v\d/.test(row));
|
|
9844
|
+
const line2 = (rows.findLast((row) => /error/i.test(row)) ?? rows.at(-1))?.trim() ?? "";
|
|
9845
|
+
return line2.length > TAIL2 ? `${line2.slice(0, TAIL2)}…` : line2;
|
|
9846
|
+
};
|
|
9847
|
+
var args = (check2, timeoutS) => [
|
|
9848
|
+
"app",
|
|
9849
|
+
"check",
|
|
9850
|
+
"--json",
|
|
9851
|
+
"--port",
|
|
9852
|
+
String(check2.port ?? 0),
|
|
9853
|
+
"--route",
|
|
9854
|
+
check2.route ?? DEFAULT_ROUTE,
|
|
9855
|
+
"--status",
|
|
9856
|
+
String(check2.status ?? DEFAULT_STATUS),
|
|
9857
|
+
"--timeout",
|
|
9858
|
+
String(timeoutS),
|
|
9859
|
+
...check2.run === void 0 ? [] : ["--start", check2.run],
|
|
9860
|
+
...check2.body === void 0 ? [] : ["--body", check2.body]
|
|
9861
|
+
];
|
|
9862
|
+
var resultNotes = (check2, result, ctx) => {
|
|
9863
|
+
const route = check2.route ?? DEFAULT_ROUTE;
|
|
9864
|
+
const line2 = ctx.mask(lastLine(result.output));
|
|
9865
|
+
const output = line2 === "" ? [] : [note("gate.note.output", { line: line2 })];
|
|
9866
|
+
switch (result.kind) {
|
|
9867
|
+
case "ok":
|
|
9868
|
+
return [note(result.reused ? "gate.runtime.reused" : "gate.runtime.ok", { route, status: result.status ?? 0 })];
|
|
9869
|
+
case "crashed":
|
|
9870
|
+
return [note("gate.runtime.crashed", { code: result.exitCode ?? -1 }), ...output];
|
|
9871
|
+
case "no_port":
|
|
9872
|
+
return [note("gate.runtime.no_port", { port: check2.port ?? 0 }), ...output];
|
|
9873
|
+
case "no_start":
|
|
9874
|
+
return [note("gate.runtime.no_start", { port: check2.port ?? 0 })];
|
|
9875
|
+
case "wrong_status":
|
|
9876
|
+
return [
|
|
9877
|
+
note("gate.runtime.wrong_status", {
|
|
9878
|
+
route,
|
|
9879
|
+
status: result.status ?? 0,
|
|
9880
|
+
expected: check2.status ?? DEFAULT_STATUS
|
|
9881
|
+
})
|
|
9882
|
+
];
|
|
9883
|
+
case "body_mismatch":
|
|
9884
|
+
return [note("gate.runtime.body_mismatch", { route, body: ctx.mask(check2.body ?? "") })];
|
|
9885
|
+
case "request_failed":
|
|
9886
|
+
return [note("gate.runtime.request_failed", { route, detail: ctx.mask(result.bodyExcerpt ?? "") })];
|
|
9887
|
+
}
|
|
9888
|
+
};
|
|
9889
|
+
var outcome = (check2, result, ctx) => {
|
|
9890
|
+
const notes2 = [
|
|
9891
|
+
...resultNotes(check2, result, ctx),
|
|
9892
|
+
...result.stopped ? [] : [note("gate.runtime.orphan", { port: check2.port ?? 0 })]
|
|
9893
|
+
];
|
|
9894
|
+
const base = result.kind === "ok" && result.stopped ? passed(notes2) : failed(notes2);
|
|
9895
|
+
return {
|
|
9896
|
+
...base,
|
|
9897
|
+
...check2.run === void 0 ? {} : { command: check2.run },
|
|
9898
|
+
exitCode: result.exitCode ?? (result.kind === "ok" ? 0 : null),
|
|
9899
|
+
stdoutSha256: sha256Hex(result.output),
|
|
9900
|
+
durationMs: result.durationMs
|
|
9901
|
+
};
|
|
9902
|
+
};
|
|
9903
|
+
var runtimeCheck = (check2, ctx) => {
|
|
9904
|
+
if (check2.port === void 0) return failed([note("gate.runtime.no_port_declared")]);
|
|
9905
|
+
if (check2.run !== void 0 && !ctx.trusted)
|
|
9906
|
+
return { ...unverified("untrusted", [note("gate.note.untrusted", { task: ctx.contract.id })]), command: check2.run };
|
|
9907
|
+
const remaining = ctx.deadline - nowMs(ctx.runtime) - PROBE_MARGIN_MS;
|
|
9908
|
+
if (remaining < MIN_REMAINING_MS2) return unverified("budget", [note("gate.note.budget")]);
|
|
9909
|
+
const timeoutS = Math.max(1, Math.min(check2.timeoutS ?? DEFAULT_APP_TIMEOUT_S, Math.floor(remaining / 1e3)));
|
|
9910
|
+
const run4 = ctx.runtime.execSelf(args(check2, timeoutS), ctx.paths.root, timeoutS * 1e3 + PROBE_MARGIN_MS);
|
|
9911
|
+
if (run4.timedOut) return unverified("timeout", [note("gate.note.timeout", { seconds: timeoutS })]);
|
|
9912
|
+
const result = parseAppCheck(run4.stdout);
|
|
9913
|
+
if (result === void 0) {
|
|
9914
|
+
return failed([note("gate.runtime.probe_failed", { line: ctx.mask(lastLine(`${run4.stdout}
|
|
9915
|
+
${run4.stderr}`)) })]);
|
|
9916
|
+
}
|
|
9917
|
+
return outcome(check2, result, ctx);
|
|
9918
|
+
};
|
|
9919
|
+
|
|
9016
9920
|
// src/secrets/baseline.ts
|
|
9017
|
-
import { join as
|
|
9921
|
+
import { join as join18 } from "node:path";
|
|
9018
9922
|
var BASELINE_FILE2 = ".finitemonkey/findings/baseline.json";
|
|
9019
9923
|
var BASELINE_SCHEMA = "finitemonkey/secrets-baseline@1";
|
|
9020
9924
|
var BASELINE_STATES = ["aceito", "falso-positivo"];
|
|
9021
9925
|
var EXCEPTION_MAX_DAYS = 90;
|
|
9022
9926
|
var EMPTY_BASELINE = { schema: BASELINE_SCHEMA, entries: [] };
|
|
9023
|
-
var baselinePath = (root) => toAbsolutePath(
|
|
9024
|
-
var text5 = (
|
|
9025
|
-
const value =
|
|
9927
|
+
var baselinePath = (root) => toAbsolutePath(join18(root, BASELINE_FILE2));
|
|
9928
|
+
var text5 = (record3, key) => {
|
|
9929
|
+
const value = record3[key];
|
|
9026
9930
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
9027
9931
|
};
|
|
9028
9932
|
var stateOf = (value) => BASELINE_STATES.find((state) => state === value);
|
|
@@ -9080,22 +9984,22 @@ var withoutFixed = (baseline, fixed) => {
|
|
|
9080
9984
|
var acceptedFingerprints = (baseline, now) => new Set(baseline.entries.filter((entry2) => entry2.expiresAt >= now).map((entry2) => entry2.fingerprint));
|
|
9081
9985
|
|
|
9082
9986
|
// src/secrets/incidents.ts
|
|
9083
|
-
import { join as
|
|
9987
|
+
import { join as join19 } from "node:path";
|
|
9084
9988
|
var INCIDENTS_DIR = ".finitemonkey/incidents";
|
|
9085
9989
|
var INCIDENT_STATES = ["aberto", "exposicao-local", "rotacionado", "fechado"];
|
|
9086
9990
|
var INCIDENT_SOURCES = ["tool-output", "prompt", "history", "scan"];
|
|
9087
9991
|
var ID = /^inc-[0-9a-f]{12}$/;
|
|
9088
9992
|
var incidentId = (rule, fingerprint) => `inc-${sha256Hex(`${rule}\0${fingerprint}`).slice(0, 12)}`;
|
|
9089
|
-
var incidentsDir = (root) => toAbsolutePath(
|
|
9090
|
-
var incidentFile = (root, id) => toAbsolutePath(
|
|
9091
|
-
var
|
|
9092
|
-
var str2 = (
|
|
9093
|
-
const value =
|
|
9993
|
+
var incidentsDir = (root) => toAbsolutePath(join19(root, INCIDENTS_DIR));
|
|
9994
|
+
var incidentFile = (root, id) => toAbsolutePath(join19(incidentsDir(root), `${id}.json`));
|
|
9995
|
+
var pick3 = (options, value) => options.find((option) => option === value);
|
|
9996
|
+
var str2 = (record3, key) => {
|
|
9997
|
+
const value = record3[key];
|
|
9094
9998
|
return typeof value === "string" ? value : void 0;
|
|
9095
9999
|
};
|
|
9096
10000
|
var parseEvent = (value) => {
|
|
9097
10001
|
if (!isRecord(value)) return void 0;
|
|
9098
|
-
const state =
|
|
10002
|
+
const state = pick3(INCIDENT_STATES, value["state"]);
|
|
9099
10003
|
const at = str2(value, "at");
|
|
9100
10004
|
const by = str2(value, "by");
|
|
9101
10005
|
return state === void 0 || at === void 0 || by === void 0 ? void 0 : { state, at, by };
|
|
@@ -9108,12 +10012,12 @@ var parseHistory = (value) => {
|
|
|
9108
10012
|
var parseIncident = (raw) => {
|
|
9109
10013
|
const parsed = parseJsonObject(raw);
|
|
9110
10014
|
if (!parsed.ok) return parsed;
|
|
9111
|
-
const
|
|
9112
|
-
const [id, rule, where, fingerprint, createdAt, updatedAt] = ["id", "rule", "where", "fingerprint", "createdAt", "updatedAt"].map((key) => str2(
|
|
9113
|
-
const state =
|
|
9114
|
-
const source =
|
|
9115
|
-
const history = parseHistory(
|
|
9116
|
-
const firstCommit = str2(
|
|
10015
|
+
const record3 = parsed.value;
|
|
10016
|
+
const [id, rule, where, fingerprint, createdAt, updatedAt] = ["id", "rule", "where", "fingerprint", "createdAt", "updatedAt"].map((key) => str2(record3, key));
|
|
10017
|
+
const state = pick3(INCIDENT_STATES, record3["state"]);
|
|
10018
|
+
const source = pick3(INCIDENT_SOURCES, record3["source"]);
|
|
10019
|
+
const history = parseHistory(record3["history"]);
|
|
10020
|
+
const firstCommit = str2(record3, "firstCommit");
|
|
9117
10021
|
if (id === void 0 || !ID.test(id) || rule === void 0 || where === void 0) return reject("incomplete");
|
|
9118
10022
|
if (fingerprint === void 0 || createdAt === void 0 || updatedAt === void 0) return reject("incomplete");
|
|
9119
10023
|
if (state === void 0 || source === void 0 || history === void 0) return reject("incomplete");
|
|
@@ -9142,7 +10046,7 @@ var listIncidents = (runtime, root) => {
|
|
|
9142
10046
|
return names.value.filter((name) => name.endsWith(".json")).map((name) => readIncident(runtime, root, name.slice(0, -".json".length))).flatMap((parsed) => parsed.ok ? [parsed.value] : []).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
9143
10047
|
};
|
|
9144
10048
|
var saveIncident = (runtime, root, incident) => {
|
|
9145
|
-
const ignore = runtime.writeText(toAbsolutePath(
|
|
10049
|
+
const ignore = runtime.writeText(toAbsolutePath(join19(incidentsDir(root), ".gitignore")), "*\n");
|
|
9146
10050
|
if (!ignore.ok) return ignore;
|
|
9147
10051
|
return runtime.writeText(incidentFile(root, incident.id), `${JSON.stringify(incident, null, 2)}
|
|
9148
10052
|
`);
|
|
@@ -9189,7 +10093,7 @@ var moveIncident = (incident, to, now, by) => ({
|
|
|
9189
10093
|
var isRotated = (incident) => incident.state === "rotacionado" || incident.state === "fechado";
|
|
9190
10094
|
|
|
9191
10095
|
// src/secrets/scan.ts
|
|
9192
|
-
import { join as
|
|
10096
|
+
import { join as join20, relative as relative4 } from "node:path";
|
|
9193
10097
|
|
|
9194
10098
|
// src/secrets/located.ts
|
|
9195
10099
|
var findingId = (ruleId, file, fingerprint) => `fm-${sha256Hex(`${ruleId}\0${file}\0${fingerprint}`).slice(0, 16)}`;
|
|
@@ -9289,7 +10193,7 @@ var listed2 = (runtime, root, target) => {
|
|
|
9289
10193
|
const base = toAbsolutePath(target, root);
|
|
9290
10194
|
const walked = runtime.walkFiles(base);
|
|
9291
10195
|
if (!walked.ok) return walked;
|
|
9292
|
-
return accept(walked.value.map((name) => toPosixPath(relative4(root,
|
|
10196
|
+
return accept(walked.value.map((name) => toPosixPath(relative4(root, join20(base, toPosixPath(name))))));
|
|
9293
10197
|
};
|
|
9294
10198
|
var scanFile = (runtime, root, file) => {
|
|
9295
10199
|
const text6 = runtime.readText(toAbsolutePath(file, root));
|
|
@@ -9307,15 +10211,15 @@ var diffFindings = (diff) => {
|
|
|
9307
10211
|
return scanner.end().filter((finding3) => !SKIPPED.test(finding3.file));
|
|
9308
10212
|
};
|
|
9309
10213
|
var scanStaged = (runtime, root, includeUnstaged = false) => {
|
|
9310
|
-
const
|
|
9311
|
-
const git = runtime.git(
|
|
10214
|
+
const args2 = includeUnstaged ? ["diff", "HEAD", ...DIFF_FLAGS] : ["diff", "--cached", ...DIFF_FLAGS];
|
|
10215
|
+
const git = runtime.git(args2, root);
|
|
9312
10216
|
if (!git.ok) return reject(git.found ? "git diff failed" : "git not found");
|
|
9313
10217
|
return accept(diffFindings(git.stdout));
|
|
9314
10218
|
};
|
|
9315
10219
|
var scanHistory = async (runtime, root) => {
|
|
9316
10220
|
const scanner = diffScanner();
|
|
9317
|
-
const
|
|
9318
|
-
const git = await runtime.gitLines(
|
|
10221
|
+
const args2 = ["log", "-p", "--all", "--format=commit %H", ...DIFF_FLAGS];
|
|
10222
|
+
const git = await runtime.gitLines(args2, root, scanner.push);
|
|
9319
10223
|
if (!git.ok) return reject(git.found ? "git log failed" : "git not found");
|
|
9320
10224
|
return accept(scanner.end().filter((finding3) => !SKIPPED.test(finding3.file)));
|
|
9321
10225
|
};
|
|
@@ -9331,11 +10235,11 @@ var secretsGateStatus = (runtime, root, since) => {
|
|
|
9331
10235
|
if (!changed.ok) return changed;
|
|
9332
10236
|
const baseline = loadBaseline(runtime, root);
|
|
9333
10237
|
if (!baseline.ok) return baseline;
|
|
9334
|
-
const
|
|
10238
|
+
const outcome2 = ratchet([...changed.value, ...untracked2(runtime, root)], baseline.value, runtime.now(), void 0);
|
|
9335
10239
|
const openIncidents = listIncidents(runtime, root).filter(
|
|
9336
10240
|
(incident) => incident.createdAt >= since && incident.state !== "rotacionado" && incident.state !== "fechado"
|
|
9337
10241
|
);
|
|
9338
|
-
return accept({ findings: [...
|
|
10242
|
+
return accept({ findings: [...outcome2.fresh, ...outcome2.expired], openIncidents });
|
|
9339
10243
|
};
|
|
9340
10244
|
|
|
9341
10245
|
// src/gate/checks/secrets.ts
|
|
@@ -9359,7 +10263,7 @@ var secretsCheck = (_check, ctx) => {
|
|
|
9359
10263
|
};
|
|
9360
10264
|
|
|
9361
10265
|
// src/gate/checks/tests.ts
|
|
9362
|
-
import { join as
|
|
10266
|
+
import { join as join21 } from "node:path";
|
|
9363
10267
|
|
|
9364
10268
|
// src/guards/tests/tree.ts
|
|
9365
10269
|
var outOfScope = (input, change) => input.testsProtected && input.scopePaths.length > 0 && change.status !== "A" && isTestFile(change.path) && !matchesAny(change.path, input.scopePaths) ? [{ kind: "out_of_scope", path: change.path }] : [];
|
|
@@ -9391,7 +10295,7 @@ var treeViolations = (ctx) => {
|
|
|
9391
10295
|
changes: relevant,
|
|
9392
10296
|
before: (path) => fileAt(ctx.git, ctx.base, path),
|
|
9393
10297
|
after: (path) => {
|
|
9394
|
-
const text6 = ctx.runtime.readText(toAbsolutePath(
|
|
10298
|
+
const text6 = ctx.runtime.readText(toAbsolutePath(join21(ctx.paths.root, path)));
|
|
9395
10299
|
return text6.ok ? text6.value : void 0;
|
|
9396
10300
|
},
|
|
9397
10301
|
acceptedConfigs: ctx.trust.configs,
|
|
@@ -9407,12 +10311,12 @@ var testsCheck = (check2, ctx) => {
|
|
|
9407
10311
|
const released2 = grant === void 0 ? {} : { releasedBy: `person@${grant.grantedAt}` };
|
|
9408
10312
|
const releaseNotes = grant === void 0 ? [] : [...notes2, note("gate.note.released", { guard: "tests", reason: grant.reason })];
|
|
9409
10313
|
if (check2.run === void 0) return { ...passed([note("gate.tests.clean"), ...releaseNotes]), ...released2 };
|
|
9410
|
-
const
|
|
9411
|
-
return { ...
|
|
10314
|
+
const outcome2 = runShellCheck(check2, check2.run, ctx);
|
|
10315
|
+
return { ...outcome2, notes: [...outcome2.notes, ...releaseNotes], ...released2 };
|
|
9412
10316
|
};
|
|
9413
10317
|
|
|
9414
10318
|
// src/git/tree.ts
|
|
9415
|
-
import { join as
|
|
10319
|
+
import { join as join22 } from "node:path";
|
|
9416
10320
|
|
|
9417
10321
|
// src/secrets/guards/paths.ts
|
|
9418
10322
|
var SAFE_ENV = /^\.env\.(?:example|sample|template|dist|defaults|schema)$/i;
|
|
@@ -9434,13 +10338,13 @@ var IDENTITY_ENV = {
|
|
|
9434
10338
|
GIT_COMMITTER_EMAIL: "finitemonkey@localhost"
|
|
9435
10339
|
};
|
|
9436
10340
|
var failure = (step3, result) => result.timedOut ? `${step3}: timed out` : `${step3}: ${result.stderr.trim() || "git failed"}`;
|
|
9437
|
-
var run = (runtime, repo,
|
|
9438
|
-
const result = runtime.gitWith(
|
|
10341
|
+
var run = (runtime, repo, args2, env, options, input) => {
|
|
10342
|
+
const result = runtime.gitWith(args2, repo.root, {
|
|
9439
10343
|
env,
|
|
9440
10344
|
timeoutMs: options.timeoutMs,
|
|
9441
10345
|
...input === void 0 ? {} : { input }
|
|
9442
10346
|
});
|
|
9443
|
-
return result.ok ? accept(result.stdout.trim()) : reject(failure(
|
|
10347
|
+
return result.ok ? accept(result.stdout.trim()) : reject(failure(args2[0] ?? "git", result));
|
|
9444
10348
|
};
|
|
9445
10349
|
var dropSensitive = (runtime, repo, env, options) => {
|
|
9446
10350
|
const listed4 = run(runtime, repo, ["ls-files", "-z"], env, options);
|
|
@@ -9451,7 +10355,7 @@ var dropSensitive = (runtime, repo, env, options) => {
|
|
|
9451
10355
|
return removed.ok ? accept(void 0) : removed;
|
|
9452
10356
|
};
|
|
9453
10357
|
var withTempIndex = (runtime, repo, options, work) => {
|
|
9454
|
-
const path = toAbsolutePath(
|
|
10358
|
+
const path = toAbsolutePath(join22(repo.gitDir, `finitemonkey-${options.stamp}.index`));
|
|
9455
10359
|
const env = { GIT_INDEX_FILE: path, GIT_OPTIONAL_LOCKS: "0" };
|
|
9456
10360
|
try {
|
|
9457
10361
|
return work(env);
|
|
@@ -9503,7 +10407,7 @@ var flushHunk = (draft) => {
|
|
|
9503
10407
|
if (draft.current !== void 0) draft.hunks.push(draft.current);
|
|
9504
10408
|
draft.current = void 0;
|
|
9505
10409
|
};
|
|
9506
|
-
var
|
|
10410
|
+
var finish2 = (draft, out) => {
|
|
9507
10411
|
if (draft === void 0) return;
|
|
9508
10412
|
flushHunk(draft);
|
|
9509
10413
|
out.push({
|
|
@@ -9563,7 +10467,7 @@ var parseUnifiedDiff = (text6) => {
|
|
|
9563
10467
|
const line2 = withoutCarriageReturn(raw);
|
|
9564
10468
|
const start = DIFF_GIT.exec(line2);
|
|
9565
10469
|
if (start !== null) {
|
|
9566
|
-
|
|
10470
|
+
finish2(draft, out);
|
|
9567
10471
|
draft = {
|
|
9568
10472
|
oldPath: start[1],
|
|
9569
10473
|
newPath: start[2],
|
|
@@ -9574,7 +10478,7 @@ var parseUnifiedDiff = (text6) => {
|
|
|
9574
10478
|
};
|
|
9575
10479
|
} else if (draft !== void 0) bodyLine(draft, line2);
|
|
9576
10480
|
}
|
|
9577
|
-
|
|
10481
|
+
finish2(draft, out);
|
|
9578
10482
|
return out;
|
|
9579
10483
|
};
|
|
9580
10484
|
var DIFF_ARGS = [
|
|
@@ -9684,26 +10588,26 @@ var entryOf2 = (kind, xy, path) => ({
|
|
|
9684
10588
|
staged: kind !== "untracked" && !xy.startsWith("."),
|
|
9685
10589
|
unstaged: kind === "untracked" || xy.charAt(1) !== "."
|
|
9686
10590
|
});
|
|
9687
|
-
var afterFields = (
|
|
10591
|
+
var afterFields = (record3, count4) => record3.split(" ").slice(count4).join(" ");
|
|
9688
10592
|
var parsePorcelainV2 = (stdout) => {
|
|
9689
10593
|
const records = stdout.split("\0");
|
|
9690
10594
|
const out = [];
|
|
9691
10595
|
for (let index = 0; index < records.length; index += 1) {
|
|
9692
|
-
const
|
|
9693
|
-
const xy =
|
|
9694
|
-
if (
|
|
9695
|
-
else if (
|
|
9696
|
-
out.push(entryOf2("changed", xy, afterFields(
|
|
10596
|
+
const record3 = records[index] ?? "";
|
|
10597
|
+
const xy = record3.split(" ")[1] ?? "..";
|
|
10598
|
+
if (record3.startsWith("1 ")) out.push(entryOf2("changed", xy, afterFields(record3, PORCELAIN_FIELDS.ordinary)));
|
|
10599
|
+
else if (record3.startsWith("2 ")) {
|
|
10600
|
+
out.push(entryOf2("changed", xy, afterFields(record3, PORCELAIN_FIELDS.renamed)));
|
|
9697
10601
|
index += 1;
|
|
9698
|
-
} else if (
|
|
9699
|
-
out.push(entryOf2("unmerged", xy, afterFields(
|
|
9700
|
-
} else if (
|
|
10602
|
+
} else if (record3.startsWith("u ")) {
|
|
10603
|
+
out.push(entryOf2("unmerged", xy, afterFields(record3, PORCELAIN_FIELDS.unmerged)));
|
|
10604
|
+
} else if (record3.startsWith("? ")) out.push(entryOf2("untracked", "??", record3.slice(2)));
|
|
9701
10605
|
}
|
|
9702
10606
|
return out.filter((entry2) => entry2.path !== "" && !entry2.path.startsWith(STATE_PREFIX));
|
|
9703
10607
|
};
|
|
9704
10608
|
|
|
9705
10609
|
// src/git/merge/dropped.ts
|
|
9706
|
-
import { join as
|
|
10610
|
+
import { join as join23 } from "node:path";
|
|
9707
10611
|
|
|
9708
10612
|
// src/git/merge/lines.ts
|
|
9709
10613
|
var normalizeLine = (line2) => line2.replace(/\r$/, "").trim().replace(/\s*,$/, "");
|
|
@@ -9783,7 +10687,7 @@ var parseDropped = (text6) => {
|
|
|
9783
10687
|
return accept({ items, invalid });
|
|
9784
10688
|
};
|
|
9785
10689
|
var loadDropped = (runtime, root, file) => {
|
|
9786
|
-
const path = toAbsolutePath(file ??
|
|
10690
|
+
const path = toAbsolutePath(file ?? join23(root, DROPPED_FILE), root);
|
|
9787
10691
|
const text6 = runtime.readText(path);
|
|
9788
10692
|
if (!text6.ok) return file === void 0 ? accept({ items: [], invalid: [] }) : reject(`${file}: ${text6.error}`);
|
|
9789
10693
|
return parseDropped(text6.value);
|
|
@@ -9793,7 +10697,7 @@ var isDropped = (finding3, items) => items.some(
|
|
|
9793
10697
|
);
|
|
9794
10698
|
|
|
9795
10699
|
// src/git/merge/generated.ts
|
|
9796
|
-
import { join as
|
|
10700
|
+
import { join as join24 } from "node:path";
|
|
9797
10701
|
var LOCKFILES3 = /* @__PURE__ */ new Set([
|
|
9798
10702
|
"package-lock.json",
|
|
9799
10703
|
"npm-shrinkwrap.json",
|
|
@@ -9856,7 +10760,7 @@ var verifyGenerated = (runtime, repo, inputs, paths, generators) => {
|
|
|
9856
10760
|
if (declared.length === 0) return accept([]);
|
|
9857
10761
|
const commit2 = resultCommit(runtime, repo, inputs);
|
|
9858
10762
|
if (!commit2.ok) return commit2;
|
|
9859
|
-
const dir =
|
|
10763
|
+
const dir = join24(repo.gitDir, `finitemonkey-generate-${runtime.now().replace(/[:.]/g, "-")}`);
|
|
9860
10764
|
if (!runtime.git(["worktree", "add", "--detach", dir, commit2.value], repo.root).ok)
|
|
9861
10765
|
return reject("could not check out the merge result");
|
|
9862
10766
|
try {
|
|
@@ -9866,7 +10770,7 @@ var verifyGenerated = (runtime, repo, inputs, paths, generators) => {
|
|
|
9866
10770
|
}
|
|
9867
10771
|
return accept(
|
|
9868
10772
|
declared.flatMap(({ path }) => {
|
|
9869
|
-
const fresh3 = runtime.readText(toAbsolutePath(
|
|
10773
|
+
const fresh3 = runtime.readText(toAbsolutePath(join24(dir, path)));
|
|
9870
10774
|
const merged = blobText(runtime, repo, commit2.value, path);
|
|
9871
10775
|
if (fresh3.ok && fresh3.value === merged) return [];
|
|
9872
10776
|
return [{ kind: "generated-differs", file: path, side: void 0, line: void 0, text: path, weak: false }];
|
|
@@ -10080,16 +10984,16 @@ var failed2 = (error, inputs) => ({
|
|
|
10080
10984
|
generated: [],
|
|
10081
10985
|
error
|
|
10082
10986
|
});
|
|
10083
|
-
var
|
|
10987
|
+
var keyOf2 = (diff) => (diff.status === "added" || diff.status === "copied" ? diff.newPath : diff.oldPath ?? diff.newPath) ?? "";
|
|
10084
10988
|
var pairsOf = (ours, theirs) => {
|
|
10085
|
-
const keys = [...new Set([...ours, ...theirs].map(
|
|
10989
|
+
const keys = [...new Set([...ours, ...theirs].map(keyOf2))].filter((key) => key !== "").sort();
|
|
10086
10990
|
return keys.map((key) => ({
|
|
10087
10991
|
key,
|
|
10088
|
-
ours: ours.find((diff) =>
|
|
10089
|
-
theirs: theirs.find((diff) =>
|
|
10992
|
+
ours: ours.find((diff) => keyOf2(diff) === key),
|
|
10993
|
+
theirs: theirs.find((diff) => keyOf2(diff) === key)
|
|
10090
10994
|
}));
|
|
10091
10995
|
};
|
|
10092
|
-
var contextOf2 = (runtime, repo, inputs,
|
|
10996
|
+
var contextOf2 = (runtime, repo, inputs, pairs2) => {
|
|
10093
10997
|
const resultText = memoBy((path) => blobText(runtime, repo, inputs.result, path));
|
|
10094
10998
|
const context = {
|
|
10095
10999
|
runtime,
|
|
@@ -10097,7 +11001,7 @@ var contextOf2 = (runtime, repo, inputs, pairs) => {
|
|
|
10097
11001
|
inputs,
|
|
10098
11002
|
resultText,
|
|
10099
11003
|
resultFiles: memo2(
|
|
10100
|
-
() =>
|
|
11004
|
+
() => pairs2.flatMap((pair) => {
|
|
10101
11005
|
const path = resultPathOf(context, pair);
|
|
10102
11006
|
return path === void 0 ? [] : [path];
|
|
10103
11007
|
})
|
|
@@ -10114,24 +11018,24 @@ var verifyMerge = (runtime, repo, options = {}) => {
|
|
|
10114
11018
|
const theirs = sideDiff(runtime, repo, inputs.value.base, inputs.value.theirs);
|
|
10115
11019
|
if (!ours.ok) return failed2(ours.error, inputs.value);
|
|
10116
11020
|
if (!theirs.ok) return failed2(theirs.error, inputs.value);
|
|
10117
|
-
const
|
|
11021
|
+
const pairs2 = pairsOf(ours.value, theirs.value);
|
|
10118
11022
|
const generators = configMap(loadProjectConfig(runtime, repo.root), "merge", "generators");
|
|
10119
11023
|
const generated = generatedPaths(
|
|
10120
11024
|
runtime,
|
|
10121
11025
|
repo,
|
|
10122
|
-
|
|
11026
|
+
pairs2.map((pair) => pair.key),
|
|
10123
11027
|
generators
|
|
10124
11028
|
);
|
|
10125
11029
|
const context = contextOf2(
|
|
10126
11030
|
runtime,
|
|
10127
11031
|
repo,
|
|
10128
11032
|
inputs.value,
|
|
10129
|
-
|
|
11033
|
+
pairs2.filter((pair) => !generated.has(pair.key))
|
|
10130
11034
|
);
|
|
10131
11035
|
const regenerated = verifyGenerated(runtime, repo, inputs.value, [...generated], generators);
|
|
10132
11036
|
if (!regenerated.ok) return failed2(regenerated.error, inputs.value);
|
|
10133
11037
|
const all = [
|
|
10134
|
-
...
|
|
11038
|
+
...pairs2.filter((pair) => !generated.has(pair.key)).flatMap((pair) => verifyFile(context, pair)),
|
|
10135
11039
|
...regenerated.value
|
|
10136
11040
|
];
|
|
10137
11041
|
const open = [...dropped.value.invalid, ...all.filter((item) => !isDropped(item, dropped.value.items))];
|
|
@@ -10143,7 +11047,7 @@ var verifyMerge = (runtime, repo, options = {}) => {
|
|
|
10143
11047
|
theirs: inputs.value.theirs,
|
|
10144
11048
|
result: inputs.value.result,
|
|
10145
11049
|
bases: inputs.value.bases,
|
|
10146
|
-
files:
|
|
11050
|
+
files: pairs2.length,
|
|
10147
11051
|
findings: open,
|
|
10148
11052
|
justified: all.length - (open.length - dropped.value.invalid.length),
|
|
10149
11053
|
generated: [...generated].sort(),
|
|
@@ -10152,19 +11056,19 @@ var verifyMerge = (runtime, repo, options = {}) => {
|
|
|
10152
11056
|
};
|
|
10153
11057
|
|
|
10154
11058
|
// src/git/ownership.ts
|
|
10155
|
-
import { join as
|
|
11059
|
+
import { join as join25 } from "node:path";
|
|
10156
11060
|
var OWNERSHIP_DIR = ".finitemonkey/git";
|
|
10157
11061
|
var ownerKey = (raw) => {
|
|
10158
11062
|
const cleaned = (raw ?? "").replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 80);
|
|
10159
11063
|
return cleaned === "" ? "unknown" : cleaned;
|
|
10160
11064
|
};
|
|
10161
|
-
var fileOf = (repo, kind, owner) => toAbsolutePath(
|
|
11065
|
+
var fileOf = (repo, kind, owner) => toAbsolutePath(join25(repo.root, OWNERSHIP_DIR, kind, `${ownerKey(owner)}.json`));
|
|
10162
11066
|
var readDirty = (runtime, root) => {
|
|
10163
11067
|
const status = runtime.git(["status", "--porcelain=v2", "-z", "--untracked-files=all"], root);
|
|
10164
11068
|
return status.ok ? parsePorcelainV2(status.stdout) : void 0;
|
|
10165
11069
|
};
|
|
10166
11070
|
var hashDirty = (runtime, repo, entries) => {
|
|
10167
|
-
const present2 = entries.filter((entry2) => runtime.stat(toAbsolutePath(
|
|
11071
|
+
const present2 = entries.filter((entry2) => runtime.stat(toAbsolutePath(join25(repo.root, entry2.path)))?.kind === "file");
|
|
10168
11072
|
const hashed = present2.length === 0 ? void 0 : runtime.gitWith(["hash-object", "--stdin-paths"], repo.root, {
|
|
10169
11073
|
input: `${present2.map((entry2) => entry2.path).join("\n")}
|
|
10170
11074
|
`
|
|
@@ -10291,8 +11195,8 @@ var openRepo = (runtime, dir) => {
|
|
|
10291
11195
|
};
|
|
10292
11196
|
var globRegex2 = (pattern) => new RegExp(`^${pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`);
|
|
10293
11197
|
var matchesBranch = (branch, patterns) => patterns.some((pattern) => globRegex2(pattern).test(branch));
|
|
10294
|
-
var line = (runtime, root,
|
|
10295
|
-
const result = runtime.git(
|
|
11198
|
+
var line = (runtime, root, args2) => {
|
|
11199
|
+
const result = runtime.git(args2, root);
|
|
10296
11200
|
const value = result.stdout.trim();
|
|
10297
11201
|
return result.ok && value !== "" ? value : void 0;
|
|
10298
11202
|
};
|
|
@@ -10417,20 +11321,22 @@ var DEFAULT_CHECKS = {
|
|
|
10417
11321
|
prose: proseCheck,
|
|
10418
11322
|
worktree: worktreeCheck,
|
|
10419
11323
|
merge: mergeCheck,
|
|
10420
|
-
review: reviewCheck
|
|
11324
|
+
review: reviewCheck,
|
|
11325
|
+
callers: callersCheck,
|
|
11326
|
+
runtime: runtimeCheck
|
|
10421
11327
|
};
|
|
10422
11328
|
|
|
10423
11329
|
// src/gate/run.ts
|
|
10424
11330
|
var FAST_PATH_MS = 10 * 6e4;
|
|
10425
|
-
var toEvidence = (ctx, check2,
|
|
11331
|
+
var toEvidence = (ctx, check2, outcome2) => ({
|
|
10426
11332
|
schema: EVIDENCE_SCHEMA,
|
|
10427
11333
|
task: ctx.contract.id,
|
|
10428
11334
|
check: check2.id,
|
|
10429
11335
|
kind: check2.kind,
|
|
10430
11336
|
treeHash: ctx.treeHash,
|
|
10431
11337
|
at: ctx.runtime.now(),
|
|
10432
|
-
...
|
|
10433
|
-
...check2.run === void 0 ||
|
|
11338
|
+
...outcome2,
|
|
11339
|
+
...check2.run === void 0 || outcome2.command !== void 0 ? {} : { command: check2.run }
|
|
10434
11340
|
});
|
|
10435
11341
|
var reusable = (ledger, ctx, check2) => {
|
|
10436
11342
|
if (!REUSABLE_KINDS.includes(check2.kind)) return false;
|
|
@@ -10611,7 +11517,7 @@ var subagentStopGate = (input) => {
|
|
|
10611
11517
|
};
|
|
10612
11518
|
|
|
10613
11519
|
// src/gate/hooks/subagent.ts
|
|
10614
|
-
import { join as
|
|
11520
|
+
import { join as join26 } from "node:path";
|
|
10615
11521
|
var NO_CONTRACT_AGENTS = /(^|:)(reviewer|verifier|explore|plan|claude-code-guide|statusline-setup)$/i;
|
|
10616
11522
|
var KNOWN_KINDS = /* @__PURE__ */ new Set([
|
|
10617
11523
|
"command",
|
|
@@ -10625,7 +11531,9 @@ var KNOWN_KINDS = /* @__PURE__ */ new Set([
|
|
|
10625
11531
|
"prose",
|
|
10626
11532
|
"worktree",
|
|
10627
11533
|
"merge",
|
|
10628
|
-
"review"
|
|
11534
|
+
"review",
|
|
11535
|
+
"callers",
|
|
11536
|
+
"runtime"
|
|
10629
11537
|
]);
|
|
10630
11538
|
var requirementText = (t, check2) => {
|
|
10631
11539
|
const key = `gate.require.${check2.kind}`;
|
|
@@ -10646,12 +11554,12 @@ var subagentStart = (input) => {
|
|
|
10646
11554
|
const active = readState(runtime, paths).active;
|
|
10647
11555
|
if (active === void 0) return { kind: "allow" };
|
|
10648
11556
|
const disk = (relative9) => {
|
|
10649
|
-
const text6 = runtime.readText(toAbsolutePath(
|
|
11557
|
+
const text6 = runtime.readText(toAbsolutePath(join26(paths.root, relative9)));
|
|
10650
11558
|
return text6.ok ? text6.value : void 0;
|
|
10651
11559
|
};
|
|
10652
11560
|
const loaded = loadTask(runtime, paths, { disk, base: () => void 0 }, active);
|
|
10653
11561
|
if (!loaded.ok) return { kind: "allow" };
|
|
10654
|
-
const briefing = runtime.stat(toAbsolutePath(
|
|
11562
|
+
const briefing = runtime.stat(toAbsolutePath(join26(paths.dir, "briefing.md"))) !== void 0;
|
|
10655
11563
|
return { kind: "allow", output: contextOutput("SubagentStart", contractFacts(t, loaded.value.contract, briefing)) };
|
|
10656
11564
|
};
|
|
10657
11565
|
|
|
@@ -10668,7 +11576,7 @@ var PORTAO_HANDLERS = {
|
|
|
10668
11576
|
};
|
|
10669
11577
|
|
|
10670
11578
|
// src/prod/guard.ts
|
|
10671
|
-
import { join as
|
|
11579
|
+
import { join as join28 } from "node:path";
|
|
10672
11580
|
|
|
10673
11581
|
// src/git/command-text.ts
|
|
10674
11582
|
var MAX_SHOWN = 160;
|
|
@@ -10818,10 +11726,10 @@ var HELM_READ = /* @__PURE__ */ new Set([
|
|
|
10818
11726
|
"env"
|
|
10819
11727
|
]);
|
|
10820
11728
|
var CURL_WRITE = /^(?:-X|--request|-d|--data.*|-F|--form|-T|--upload-file|-o|--output|-O|--remote-name)$/;
|
|
10821
|
-
var firstPositional = (
|
|
11729
|
+
var firstPositional = (args2, valueFlags = /* @__PURE__ */ new Set()) => {
|
|
10822
11730
|
const out = [];
|
|
10823
|
-
for (let index = 0; index <
|
|
10824
|
-
const arg =
|
|
11731
|
+
for (let index = 0; index < args2.length; index += 1) {
|
|
11732
|
+
const arg = args2[index] ?? "";
|
|
10825
11733
|
if (arg.startsWith("-")) {
|
|
10826
11734
|
if (valueFlags.has(arg)) index += 1;
|
|
10827
11735
|
} else out.push(arg);
|
|
@@ -10856,16 +11764,16 @@ var KUBE_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
|
10856
11764
|
"--container",
|
|
10857
11765
|
"--kube-context"
|
|
10858
11766
|
]);
|
|
10859
|
-
var dockerReadOnly = (
|
|
10860
|
-
const words = firstPositional(
|
|
11767
|
+
var dockerReadOnly = (args2) => {
|
|
11768
|
+
const words = firstPositional(args2, DOCKER_VALUE_FLAGS);
|
|
10861
11769
|
const [first, second] = words;
|
|
10862
11770
|
if (first === void 0) return true;
|
|
10863
11771
|
if (DOCKER_GROUPS.has(first))
|
|
10864
11772
|
return second === void 0 || DOCKER_READ.has(second) || first === "compose" && second === "top";
|
|
10865
11773
|
return DOCKER_READ.has(first);
|
|
10866
11774
|
};
|
|
10867
|
-
var kubectlReadOnly = (program,
|
|
10868
|
-
const [verb, object] = firstPositional(
|
|
11775
|
+
var kubectlReadOnly = (program, args2) => {
|
|
11776
|
+
const [verb, object] = firstPositional(args2, KUBE_VALUE_FLAGS);
|
|
10869
11777
|
if (verb === void 0) return true;
|
|
10870
11778
|
if (program === "helm") return HELM_READ.has(verb);
|
|
10871
11779
|
if (verb === "config") return object === "view" || object === "get-contexts" || object === "current-context";
|
|
@@ -10875,24 +11783,24 @@ var kubectlReadOnly = (program, args) => {
|
|
|
10875
11783
|
var writesFile = (command) => command.redirects.some(
|
|
10876
11784
|
(redirect) => (redirect.op === ">" || redirect.op === ">>" || redirect.op === ">|") && !/^(?:\/dev\/null|&?\d)$/.test(redirect.target) && !redirect.op.startsWith("<")
|
|
10877
11785
|
);
|
|
10878
|
-
var gitReadOnly = (
|
|
10879
|
-
const [verb, ...rest] = firstPositional(
|
|
11786
|
+
var gitReadOnly = (args2) => {
|
|
11787
|
+
const [verb, ...rest] = firstPositional(args2, /* @__PURE__ */ new Set(["-C", "-c"]));
|
|
10880
11788
|
if (verb === void 0) return true;
|
|
10881
11789
|
if (GIT_READ.has(verb)) return true;
|
|
10882
11790
|
return GIT_LIST.has(verb) && (rest.length === 0 || verb === "stash" && rest[0] === "list");
|
|
10883
11791
|
};
|
|
10884
11792
|
var programReadOnly = (command) => {
|
|
10885
|
-
const { program, args } = command;
|
|
10886
|
-
if (program === "git") return gitReadOnly(
|
|
11793
|
+
const { program, args: args2 } = command;
|
|
11794
|
+
if (program === "git") return gitReadOnly(args2);
|
|
10887
11795
|
if (program === "docker" || program === "docker-compose")
|
|
10888
|
-
return dockerReadOnly(program === "docker-compose" ? ["compose", ...
|
|
10889
|
-
if (program === "systemctl") return SYSTEMCTL_READ.has(firstPositional(
|
|
10890
|
-
if (program === "kubectl" || program === "helm") return kubectlReadOnly(program,
|
|
10891
|
-
if (program === "curl") return !
|
|
11796
|
+
return dockerReadOnly(program === "docker-compose" ? ["compose", ...args2] : args2);
|
|
11797
|
+
if (program === "systemctl") return SYSTEMCTL_READ.has(firstPositional(args2)[0] ?? "status");
|
|
11798
|
+
if (program === "kubectl" || program === "helm") return kubectlReadOnly(program, args2);
|
|
11799
|
+
if (program === "curl") return !args2.some((arg) => CURL_WRITE.test(arg));
|
|
10892
11800
|
if (program === "find")
|
|
10893
|
-
return !
|
|
10894
|
-
if (program === "sed") return !
|
|
10895
|
-
if (program === "awk") return !
|
|
11801
|
+
return !args2.some((arg) => arg === "-delete" || arg.startsWith("-exec") || arg.startsWith("-ok"));
|
|
11802
|
+
if (program === "sed") return !args2.some((arg) => arg.startsWith("-i") || arg === "--in-place");
|
|
11803
|
+
if (program === "awk") return !args2.some((arg) => arg.includes("system(") || arg.includes(">"));
|
|
10896
11804
|
return READ_PROGRAMS.has(program);
|
|
10897
11805
|
};
|
|
10898
11806
|
var readOnlyCommand = (command) => !writesFile(command) && programReadOnly(command);
|
|
@@ -10952,11 +11860,11 @@ var RSYNC_LONG_VALUE = /* @__PURE__ */ new Set([
|
|
|
10952
11860
|
"--files-from"
|
|
10953
11861
|
]);
|
|
10954
11862
|
var parseSsh = (command) => {
|
|
10955
|
-
const { args } = command;
|
|
11863
|
+
const { args: args2 } = command;
|
|
10956
11864
|
let index = 0;
|
|
10957
11865
|
let noCommand = false;
|
|
10958
|
-
while (index <
|
|
10959
|
-
const arg =
|
|
11866
|
+
while (index < args2.length && (args2[index] ?? "").startsWith("-")) {
|
|
11867
|
+
const arg = args2[index] ?? "";
|
|
10960
11868
|
if (arg === "--") {
|
|
10961
11869
|
index += 1;
|
|
10962
11870
|
break;
|
|
@@ -10965,17 +11873,17 @@ var parseSsh = (command) => {
|
|
|
10965
11873
|
const last = arg.charAt(arg.length - 1);
|
|
10966
11874
|
index += arg.length === 2 && SSH_VALUE.includes(last) ? 2 : 1;
|
|
10967
11875
|
}
|
|
10968
|
-
const destination =
|
|
11876
|
+
const destination = args2[index];
|
|
10969
11877
|
if (destination === void 0) return void 0;
|
|
10970
|
-
return { destination: destination.replace(/^ssh:\/\//, ""), remote:
|
|
11878
|
+
return { destination: destination.replace(/^ssh:\/\//, ""), remote: args2.slice(index + 1), noCommand };
|
|
10971
11879
|
};
|
|
10972
11880
|
var REMOTE_PATH = /^(?:(?:scp|rsync|sftp):\/\/)?(?:[^@/:\s]+@)?(\[[^\]]+\]|[A-Za-z0-9][A-Za-z0-9.-]*)(?::|::|\/)/;
|
|
10973
11881
|
var remoteHostOf = (operand) => {
|
|
10974
11882
|
if (/^[A-Za-z]:[\\/]/.test(operand) || operand.startsWith("/") || operand.startsWith(".")) return void 0;
|
|
10975
|
-
const
|
|
11883
|
+
const url2 = /^(?:scp|rsync|sftp):\/\//.test(operand);
|
|
10976
11884
|
const match = REMOTE_PATH.exec(operand);
|
|
10977
11885
|
if (match === null) return void 0;
|
|
10978
|
-
if (!
|
|
11886
|
+
if (!url2 && !operand.slice(match[0].length - 1).startsWith(":")) return void 0;
|
|
10979
11887
|
return (match[1] ?? "").replace(/^\[|\]$/g, "");
|
|
10980
11888
|
};
|
|
10981
11889
|
var copyOperands = (command, shortValue) => {
|
|
@@ -11010,7 +11918,7 @@ var remoteHostsOf = (command) => {
|
|
|
11010
11918
|
};
|
|
11011
11919
|
|
|
11012
11920
|
// src/prod/targets.ts
|
|
11013
|
-
import { join as
|
|
11921
|
+
import { join as join27 } from "node:path";
|
|
11014
11922
|
var DEPLOY_FILES = [
|
|
11015
11923
|
"deploy.sh",
|
|
11016
11924
|
"scripts/deploy.sh",
|
|
@@ -11034,12 +11942,12 @@ var hostsInText = (text6) => {
|
|
|
11034
11942
|
return [...urls, ...keyed, ...commands].filter((host) => host !== "" && !host.includes("$"));
|
|
11035
11943
|
};
|
|
11036
11944
|
var workflowFiles = (runtime, root) => {
|
|
11037
|
-
const listed4 = runtime.listDir(toAbsolutePath(
|
|
11945
|
+
const listed4 = runtime.listDir(toAbsolutePath(join27(root, ".github", "workflows")));
|
|
11038
11946
|
if (!listed4.ok) return [];
|
|
11039
11947
|
return listed4.value.filter((name) => /deploy/i.test(name) && /\.ya?ml$/i.test(name)).map((name) => `.github/workflows/${name}`);
|
|
11040
11948
|
};
|
|
11041
11949
|
var detectedHosts = (runtime, root, files) => files.flatMap((file) => {
|
|
11042
|
-
const text6 = runtime.readText(toAbsolutePath(
|
|
11950
|
+
const text6 = runtime.readText(toAbsolutePath(join27(root, file)));
|
|
11043
11951
|
return text6.ok ? hostsInText(text6.value) : [];
|
|
11044
11952
|
});
|
|
11045
11953
|
var loadProdTargets = (runtime, root, config) => {
|
|
@@ -11051,7 +11959,7 @@ var loadProdTargets = (runtime, root, config) => {
|
|
|
11051
11959
|
...detectedHosts(runtime, root, deployFiles)
|
|
11052
11960
|
];
|
|
11053
11961
|
const declaredDeploy = configAt(config, "prod", "deploy");
|
|
11054
|
-
const existing = deployFiles.find((file) => runtime.stat(toAbsolutePath(
|
|
11962
|
+
const existing = deployFiles.find((file) => runtime.stat(toAbsolutePath(join27(root, file))) !== void 0);
|
|
11055
11963
|
return {
|
|
11056
11964
|
hosts: new Set(hosts.map(hostKey).filter((host) => host !== "")),
|
|
11057
11965
|
kubeContexts: configList(config, "prod", "kube_contexts"),
|
|
@@ -11080,12 +11988,12 @@ var copyHit = (command, isProd) => {
|
|
|
11080
11988
|
const source = call.sources.map(remoteHostOf).find((host) => host !== void 0 && isProd(host));
|
|
11081
11989
|
return source === void 0 ? void 0 : hit2("read", hostKey(source), command.program);
|
|
11082
11990
|
};
|
|
11083
|
-
var valueAfter = (
|
|
11084
|
-
for (let index = 0; index <
|
|
11085
|
-
const arg =
|
|
11991
|
+
var valueAfter = (args2, ...names) => {
|
|
11992
|
+
for (let index = 0; index < args2.length; index += 1) {
|
|
11993
|
+
const arg = args2[index] ?? "";
|
|
11086
11994
|
const equals = names.find((name) => arg.startsWith(`${name}=`));
|
|
11087
11995
|
if (equals !== void 0) return arg.slice(equals.length + 1);
|
|
11088
|
-
if (names.includes(arg)) return
|
|
11996
|
+
if (names.includes(arg)) return args2[index + 1];
|
|
11089
11997
|
}
|
|
11090
11998
|
return void 0;
|
|
11091
11999
|
};
|
|
@@ -11127,8 +12035,8 @@ var DUMPS = /* @__PURE__ */ new Set(["pg_dump", "mysqldump"]);
|
|
|
11127
12035
|
var databaseHost = (command) => {
|
|
11128
12036
|
const flag2 = valueAfter(command.args, "-h", "--host");
|
|
11129
12037
|
if (flag2 !== void 0) return flag2;
|
|
11130
|
-
const
|
|
11131
|
-
return
|
|
12038
|
+
const url2 = [...command.args, command.env["DATABASE_URL"] ?? ""].map((arg) => URL_HOST.exec(arg)?.[1]).find((host) => host !== void 0);
|
|
12039
|
+
return url2 ?? command.env["PGHOST"];
|
|
11132
12040
|
};
|
|
11133
12041
|
var databaseReadOnly = (command) => {
|
|
11134
12042
|
if (DUMPS.has(command.program)) return true;
|
|
@@ -11228,8 +12136,8 @@ var recordEvidence = (runtime, root, owner, reads) => {
|
|
|
11228
12136
|
] : []
|
|
11229
12137
|
);
|
|
11230
12138
|
if (lines3.length === 0) return;
|
|
11231
|
-
const path = toAbsolutePath(
|
|
11232
|
-
runtime.mkdir(toAbsolutePath(
|
|
12139
|
+
const path = toAbsolutePath(join28(root, EVIDENCE_FILE));
|
|
12140
|
+
runtime.mkdir(toAbsolutePath(join28(root, ".finitemonkey")));
|
|
11233
12141
|
const before = runtime.readText(path);
|
|
11234
12142
|
const text6 = before.ok ? before.value : "";
|
|
11235
12143
|
runtime.writeAtomic(path, `${text6}${text6 === "" || text6.endsWith("\n") ? "" : "\n"}${lines3.join("\n")}
|
|
@@ -11302,14 +12210,14 @@ var globalOption = (globals, name, value) => {
|
|
|
11302
12210
|
var gitInvocation = (command) => {
|
|
11303
12211
|
if (command.program !== "git") return void 0;
|
|
11304
12212
|
const globals = { dirs: [], config: {} };
|
|
11305
|
-
const { args } = command;
|
|
12213
|
+
const { args: args2 } = command;
|
|
11306
12214
|
let index = 0;
|
|
11307
|
-
while (index <
|
|
11308
|
-
const arg =
|
|
12215
|
+
while (index < args2.length && (args2[index] ?? "").startsWith("-")) {
|
|
12216
|
+
const arg = args2[index] ?? "";
|
|
11309
12217
|
const equals = arg.indexOf("=");
|
|
11310
12218
|
const name = equals > 2 && arg.startsWith("--") ? arg.slice(0, equals) : arg;
|
|
11311
12219
|
if (WITH_VALUE.has(name) && equals < 0) {
|
|
11312
|
-
globalOption(globals, name,
|
|
12220
|
+
globalOption(globals, name, args2[index + 1] ?? "");
|
|
11313
12221
|
index += 2;
|
|
11314
12222
|
} else {
|
|
11315
12223
|
if (equals > 0) globalOption(globals, name, arg.slice(equals + 1));
|
|
@@ -11319,8 +12227,8 @@ var gitInvocation = (command) => {
|
|
|
11319
12227
|
return {
|
|
11320
12228
|
dirs: globals.dirs,
|
|
11321
12229
|
config: globals.config,
|
|
11322
|
-
subcommand:
|
|
11323
|
-
args:
|
|
12230
|
+
subcommand: args2[index] ?? "",
|
|
12231
|
+
args: args2.slice(index + 1),
|
|
11324
12232
|
command
|
|
11325
12233
|
};
|
|
11326
12234
|
};
|
|
@@ -11331,7 +12239,7 @@ var withAlias = (invocation, expansion) => ({
|
|
|
11331
12239
|
});
|
|
11332
12240
|
|
|
11333
12241
|
// src/git/path-key.ts
|
|
11334
|
-
import { posix as
|
|
12242
|
+
import { posix as posix3 } from "node:path";
|
|
11335
12243
|
var WSL_MOUNT3 = /^\/mnt\/([a-zA-Z])(\/|$)/;
|
|
11336
12244
|
var DRIVE = /^([a-zA-Z]):(\/|$)/;
|
|
11337
12245
|
var pathKey = (path) => {
|
|
@@ -11340,7 +12248,7 @@ var pathKey = (path) => {
|
|
|
11340
12248
|
(_match, letter, slash) => `${letter}:${slash || "/"}`
|
|
11341
12249
|
);
|
|
11342
12250
|
const drive = DRIVE.exec(posixPath);
|
|
11343
|
-
const normalized =
|
|
12251
|
+
const normalized = posix3.normalize(drive === null ? posixPath : posixPath.slice(2) || "/");
|
|
11344
12252
|
const trimmed = normalized.length > 1 ? normalized.replace(/\/+$/, "") : normalized;
|
|
11345
12253
|
return drive === null ? trimmed : `${(drive[1] ?? "").toLowerCase()}:${trimmed}`.toLowerCase();
|
|
11346
12254
|
};
|
|
@@ -11356,15 +12264,15 @@ var isRootLike = (path, home) => {
|
|
|
11356
12264
|
if (key === "/" || /^[a-z]:\/?$/.test(key)) return true;
|
|
11357
12265
|
return home !== void 0 && home !== "" && key === pathKey(home);
|
|
11358
12266
|
};
|
|
11359
|
-
var resolveFrom = (base, ...parts) => parts.reduce((
|
|
11360
|
-
if (part2 === void 0 || part2 === "") return
|
|
12267
|
+
var resolveFrom = (base, ...parts) => parts.reduce((current2, part2) => {
|
|
12268
|
+
if (part2 === void 0 || part2 === "") return current2;
|
|
11361
12269
|
const next2 = toPosixPath(part2);
|
|
11362
12270
|
if (next2.startsWith("/") || DRIVE.test(next2)) return next2;
|
|
11363
|
-
return
|
|
12271
|
+
return posix3.join(toPosixPath(current2), next2);
|
|
11364
12272
|
}, toPosixPath(base));
|
|
11365
12273
|
|
|
11366
12274
|
// src/git/pathspec.ts
|
|
11367
|
-
import { posix as
|
|
12275
|
+
import { posix as posix4 } from "node:path";
|
|
11368
12276
|
var MAGIC = /^:(?:\(([^)]*)\)|([!^/]*))(.*)$/s;
|
|
11369
12277
|
var GLOB = /[*?[]/;
|
|
11370
12278
|
var specOf = (raw, prefix) => {
|
|
@@ -11374,7 +12282,7 @@ var specOf = (raw, prefix) => {
|
|
|
11374
12282
|
const rest = magic === null ? posixRaw : magic[3] ?? "";
|
|
11375
12283
|
const exclude = /(?:^|,)exclude(?:,|$)|[!^]/.test(words);
|
|
11376
12284
|
const top = /(?:^|,)top(?:,|$)|\//.test(words);
|
|
11377
|
-
const joined =
|
|
12285
|
+
const joined = posix4.normalize(top ? rest : posix4.join(prefix === "" ? "." : prefix, rest));
|
|
11378
12286
|
return { pattern: joined === "./" ? "." : joined.replace(/\/$/, ""), exclude };
|
|
11379
12287
|
};
|
|
11380
12288
|
var globRegex3 = (pattern) => {
|
|
@@ -11407,7 +12315,7 @@ var discard = (op, scope2, pathspecs = []) => ({
|
|
|
11407
12315
|
discard: { scope: scope2, pathspecs }
|
|
11408
12316
|
});
|
|
11409
12317
|
var SEVERITY = { pass: 0, snapshot: 1, discard: 2, block: 3 };
|
|
11410
|
-
var worst = (verdicts) => verdicts.reduce((
|
|
12318
|
+
var worst = (verdicts) => verdicts.reduce((current2, next2) => SEVERITY[next2.kind] > SEVERITY[current2.kind] ? next2 : current2, PASS);
|
|
11411
12319
|
|
|
11412
12320
|
// src/git/options.ts
|
|
11413
12321
|
var canonical = (name, spec2) => {
|
|
@@ -11454,14 +12362,14 @@ var shortCluster = (building, arg, next2, spec2) => {
|
|
|
11454
12362
|
}
|
|
11455
12363
|
return 1;
|
|
11456
12364
|
};
|
|
11457
|
-
var parseOptions = (
|
|
12365
|
+
var parseOptions = (args2, spec2) => {
|
|
11458
12366
|
const building = { flags: /* @__PURE__ */ new Set(), values: /* @__PURE__ */ new Map(), positionals: [] };
|
|
11459
12367
|
let index = 0;
|
|
11460
|
-
while (index <
|
|
11461
|
-
const arg =
|
|
11462
|
-
const next2 =
|
|
12368
|
+
while (index < args2.length) {
|
|
12369
|
+
const arg = args2[index] ?? "";
|
|
12370
|
+
const next2 = args2[index + 1];
|
|
11463
12371
|
if (arg === "--") {
|
|
11464
|
-
return { ...building, paths:
|
|
12372
|
+
return { ...building, paths: args2.slice(index + 1), dashdash: true };
|
|
11465
12373
|
}
|
|
11466
12374
|
if (arg.startsWith("--")) index += longOption(building, arg, next2, spec2);
|
|
11467
12375
|
else if (arg.startsWith("-") && arg.length > 1 && !/^-\d+$/.test(arg)) {
|
|
@@ -11768,24 +12676,24 @@ var branchName = (ref) => {
|
|
|
11768
12676
|
if (ref.startsWith("refs/tags/")) return void 0;
|
|
11769
12677
|
return ref.replace(/^refs\/heads\//, "");
|
|
11770
12678
|
};
|
|
11771
|
-
var refspecTarget = (refspec,
|
|
12679
|
+
var refspecTarget = (refspec, current2) => {
|
|
11772
12680
|
const plain = refspec.replace(/^\+/, "");
|
|
11773
12681
|
const colon = plain.indexOf(":");
|
|
11774
12682
|
const destination = colon < 0 ? plain : plain.slice(colon + 1);
|
|
11775
|
-
if (destination === "HEAD" || colon < 0 && plain === "HEAD") return
|
|
12683
|
+
if (destination === "HEAD" || colon < 0 && plain === "HEAD") return current2;
|
|
11776
12684
|
if (destination === "" || destination.startsWith("refs/tags/")) return void 0;
|
|
11777
12685
|
return branchName(destination);
|
|
11778
12686
|
};
|
|
11779
12687
|
var pushDestinations = (parsed, facts) => {
|
|
11780
12688
|
if (hasAny(parsed, "--all", "--branches", "--mirror")) return "all";
|
|
11781
12689
|
const refspecs = parsed.positionals.slice(1).filter((refspec) => refspec !== ":");
|
|
11782
|
-
const
|
|
12690
|
+
const current2 = facts.currentBranch();
|
|
11783
12691
|
if (refspecs.length === 0) {
|
|
11784
|
-
if (hasAny(parsed, "--tags") ||
|
|
11785
|
-
return [facts.upstream(
|
|
12692
|
+
if (hasAny(parsed, "--tags") || current2 === void 0) return [];
|
|
12693
|
+
return [facts.upstream(current2)?.branch ?? current2];
|
|
11786
12694
|
}
|
|
11787
12695
|
return refspecs.flatMap((refspec) => {
|
|
11788
|
-
const target = refspecTarget(refspec,
|
|
12696
|
+
const target = refspecTarget(refspec, current2);
|
|
11789
12697
|
return target === void 0 ? [] : [target];
|
|
11790
12698
|
});
|
|
11791
12699
|
};
|
|
@@ -11793,10 +12701,10 @@ var leaseVerdict = (parsed, facts) => {
|
|
|
11793
12701
|
const leases = valuesOf(parsed, "--force-with-lease");
|
|
11794
12702
|
const explicit = leases.length > 0 && leases.every((lease) => /^[^:]+:.+$/.test(lease));
|
|
11795
12703
|
if (!explicit || !hasAny(parsed, "--force-if-includes")) return block2("force-push", "--force-with-lease");
|
|
11796
|
-
const
|
|
12704
|
+
const current2 = facts.currentBranch();
|
|
11797
12705
|
const leased = leases.map((lease) => branchName(lease.slice(0, lease.indexOf(":"))));
|
|
11798
12706
|
const destinations = pushDestinations(parsed, facts);
|
|
11799
|
-
const own =
|
|
12707
|
+
const own = current2 !== void 0 && !facts.isProtected(current2) && leased.every((name) => name === current2) && destinations !== "all" && destinations.every((name) => name === current2);
|
|
11800
12708
|
return own ? PASS : block2("lease-not-own", leased.join(", "));
|
|
11801
12709
|
};
|
|
11802
12710
|
var pushVerdict = (invocation, facts) => {
|
|
@@ -11819,8 +12727,8 @@ var LEAVES = /* @__PURE__ */ new Set(["--abort", "--quit", "--skip", "--dry-run"
|
|
|
11819
12727
|
var commitVerdict = (invocation, facts) => {
|
|
11820
12728
|
if (invocation.args.some((arg) => LEAVES.has(arg))) return PASS;
|
|
11821
12729
|
if (invocation.subcommand === "rebase") return PASS;
|
|
11822
|
-
const
|
|
11823
|
-
return
|
|
12730
|
+
const current2 = facts.currentBranch();
|
|
12731
|
+
return current2 !== void 0 && facts.isProtected(current2) ? block2("protected-commit", current2) : PASS;
|
|
11824
12732
|
};
|
|
11825
12733
|
var pushVerdict2 = (invocation, facts) => {
|
|
11826
12734
|
const parsed = parseOptions(invocation.args, PUSH_OPTIONS);
|
|
@@ -11899,9 +12807,9 @@ var gitVerdict = (invocation, facts) => {
|
|
|
11899
12807
|
|
|
11900
12808
|
// src/git/switch-target.ts
|
|
11901
12809
|
var CREATES = /* @__PURE__ */ new Set(["-c", "-C", "-b", "-B", "--create", "--force-create", "--orphan"]);
|
|
11902
|
-
var operands = (
|
|
11903
|
-
if (
|
|
11904
|
-
return
|
|
12810
|
+
var operands = (args2) => {
|
|
12811
|
+
if (args2.includes("--")) return void 0;
|
|
12812
|
+
return args2.filter((arg) => !arg.startsWith("-"));
|
|
11905
12813
|
};
|
|
11906
12814
|
var switchTarget = (invocation, facts) => {
|
|
11907
12815
|
if (invocation.subcommand !== "switch" && invocation.subcommand !== "checkout") return void 0;
|
|
@@ -12041,33 +12949,33 @@ var listWorktrees = (runtime, cwd) => {
|
|
|
12041
12949
|
};
|
|
12042
12950
|
|
|
12043
12951
|
// src/git/journal.ts
|
|
12044
|
-
import { join as
|
|
12952
|
+
import { join as join29 } from "node:path";
|
|
12045
12953
|
var JOURNAL_FILE = ".finitemonkey/git-journal.jsonl";
|
|
12046
12954
|
var JOURNAL_SCHEMA = "finitemonkey/git-journal@1";
|
|
12047
|
-
var journalPath = (root) => toAbsolutePath(
|
|
12955
|
+
var journalPath = (root) => toAbsolutePath(join29(root, JOURNAL_FILE));
|
|
12048
12956
|
var headsOf = (value) => isRecord(value) ? Object.fromEntries(
|
|
12049
12957
|
Object.entries(value).filter((entry2) => typeof entry2[1] === "string")
|
|
12050
12958
|
) : {};
|
|
12051
12959
|
var parseJournalLine = (line2) => {
|
|
12052
12960
|
const parsed = parseJsonObject(line2);
|
|
12053
12961
|
if (!parsed.ok) return void 0;
|
|
12054
|
-
const
|
|
12055
|
-
const ref = optionalString(
|
|
12056
|
-
const commit2 = optionalString(
|
|
12057
|
-
const at = optionalString(
|
|
12962
|
+
const record3 = parsed.value;
|
|
12963
|
+
const ref = optionalString(record3, "ref");
|
|
12964
|
+
const commit2 = optionalString(record3, "commit");
|
|
12965
|
+
const at = optionalString(record3, "at");
|
|
12058
12966
|
if (ref === void 0 || commit2 === void 0 || at === void 0) return void 0;
|
|
12059
12967
|
return {
|
|
12060
12968
|
schema: JOURNAL_SCHEMA,
|
|
12061
12969
|
at,
|
|
12062
|
-
op: optionalString(
|
|
12970
|
+
op: optionalString(record3, "op") ?? "",
|
|
12063
12971
|
ref,
|
|
12064
12972
|
commit: commit2,
|
|
12065
|
-
head: optionalString(
|
|
12066
|
-
branch: optionalString(
|
|
12067
|
-
index: optionalString(
|
|
12068
|
-
owner: optionalString(
|
|
12069
|
-
command: optionalString(
|
|
12070
|
-
heads: headsOf(
|
|
12973
|
+
head: optionalString(record3, "head"),
|
|
12974
|
+
branch: optionalString(record3, "branch"),
|
|
12975
|
+
index: optionalString(record3, "index"),
|
|
12976
|
+
owner: optionalString(record3, "owner"),
|
|
12977
|
+
command: optionalString(record3, "command"),
|
|
12978
|
+
heads: headsOf(record3["heads"])
|
|
12071
12979
|
};
|
|
12072
12980
|
};
|
|
12073
12981
|
var readJournal = (runtime, root) => {
|
|
@@ -12080,7 +12988,7 @@ var readJournal = (runtime, root) => {
|
|
|
12080
12988
|
};
|
|
12081
12989
|
var appendJournal = (runtime, root, entry2) => {
|
|
12082
12990
|
const path = journalPath(root);
|
|
12083
|
-
const made = runtime.mkdir(toAbsolutePath(
|
|
12991
|
+
const made = runtime.mkdir(toAbsolutePath(join29(root, ".finitemonkey")));
|
|
12084
12992
|
if (!made.ok) return made;
|
|
12085
12993
|
const previous = runtime.readText(path);
|
|
12086
12994
|
const before = previous.ok ? previous.value : "";
|
|
@@ -12095,8 +13003,8 @@ var BACKUP_PREFIX = "refs/finitemonkey/backup/";
|
|
|
12095
13003
|
var DEFAULT_SNAPSHOT_TIMEOUT_MS = 2500;
|
|
12096
13004
|
var stampOf = (iso) => iso.replace(/[:.]/g, "-");
|
|
12097
13005
|
var slug = (op) => op.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "manual";
|
|
12098
|
-
var optionalLine = (runtime, repo,
|
|
12099
|
-
const result = runtime.git(
|
|
13006
|
+
var optionalLine = (runtime, repo, args2) => {
|
|
13007
|
+
const result = runtime.git(args2, repo.root);
|
|
12100
13008
|
const value = result.stdout.trim();
|
|
12101
13009
|
return result.ok && value !== "" ? value : void 0;
|
|
12102
13010
|
};
|
|
@@ -12201,17 +13109,17 @@ var fileList = (items) => {
|
|
|
12201
13109
|
return items.length > MAX_FILES2 ? `${head} (+${String(items.length - MAX_FILES2)})` : head;
|
|
12202
13110
|
};
|
|
12203
13111
|
var foreignLabel = (t, file) => t(file.reason === "before-session" ? "git.foreign.before" : "git.foreign.other", { path: file.path });
|
|
12204
|
-
var outcomeMessage = (t,
|
|
12205
|
-
switch (
|
|
13112
|
+
var outcomeMessage = (t, outcome2) => {
|
|
13113
|
+
switch (outcome2.kind) {
|
|
12206
13114
|
case "block":
|
|
12207
|
-
return ruleMessage(t,
|
|
13115
|
+
return ruleMessage(t, outcome2.rule, outcome2.command, outcome2.subject);
|
|
12208
13116
|
case "foreign":
|
|
12209
13117
|
return t("git.block.foreign", {
|
|
12210
|
-
command:
|
|
12211
|
-
files: fileList(
|
|
13118
|
+
command: outcome2.command,
|
|
13119
|
+
files: fileList(outcome2.files.map((file) => foreignLabel(t, file)))
|
|
12212
13120
|
});
|
|
12213
13121
|
case "unknown":
|
|
12214
|
-
return t("git.block.unknown", { command:
|
|
13122
|
+
return t("git.block.unknown", { command: outcome2.command });
|
|
12215
13123
|
case "pass":
|
|
12216
13124
|
case "snapshot":
|
|
12217
13125
|
return void 0;
|
|
@@ -12255,10 +13163,10 @@ var review = (input, commands, owner) => {
|
|
|
12255
13163
|
const decision = PROD_PROGRAMS.has(command.program) ? prod().decide(command) : void 0;
|
|
12256
13164
|
if (decision?.kind === "block") return { blocked: decision.reason, snapshots, reads };
|
|
12257
13165
|
if (decision !== void 0) reads.push(decision);
|
|
12258
|
-
for (const
|
|
12259
|
-
const message = outcomeMessage(input.t,
|
|
13166
|
+
for (const outcome2 of guardGitCommand({ runtime: input.runtime, cwd: input.cwd, owner, chain }, command)) {
|
|
13167
|
+
const message = outcomeMessage(input.t, outcome2);
|
|
12260
13168
|
if (message !== void 0) return { blocked: message, snapshots, reads };
|
|
12261
|
-
if (
|
|
13169
|
+
if (outcome2.kind === "snapshot") snapshots.push(outcome2);
|
|
12262
13170
|
}
|
|
12263
13171
|
}
|
|
12264
13172
|
return { blocked: void 0, snapshots, reads };
|
|
@@ -12322,6 +13230,83 @@ var GIT_HANDLERS = {
|
|
|
12322
13230
|
PostToolUse: [gitPostToolUse]
|
|
12323
13231
|
};
|
|
12324
13232
|
|
|
13233
|
+
// src/policy/guards.ts
|
|
13234
|
+
var SHADOW_FILE = "shadow.json";
|
|
13235
|
+
var numberOf = (value) => typeof value === "number" && Number.isInteger(value) ? value : 0;
|
|
13236
|
+
var readShadow = (runtime, paths) => {
|
|
13237
|
+
const text6 = runtime.readText(paths.cache(SHADOW_FILE));
|
|
13238
|
+
const parsed = text6.ok ? parseJsonObject(text6.value) : void 0;
|
|
13239
|
+
if (parsed?.ok !== true) return {};
|
|
13240
|
+
return Object.fromEntries(
|
|
13241
|
+
Object.entries(parsed.value).flatMap(
|
|
13242
|
+
([id, value]) => isRecord(value) ? [
|
|
13243
|
+
[
|
|
13244
|
+
id,
|
|
13245
|
+
{
|
|
13246
|
+
firings: numberOf(value["firings"]),
|
|
13247
|
+
falsePositives: numberOf(value["falsePositives"]),
|
|
13248
|
+
promoted: value["promoted"] === true
|
|
13249
|
+
}
|
|
13250
|
+
]
|
|
13251
|
+
] : []
|
|
13252
|
+
)
|
|
13253
|
+
);
|
|
13254
|
+
};
|
|
13255
|
+
var writeShadow = (runtime, paths, state) => {
|
|
13256
|
+
runtime.writeText(paths.cache(SHADOW_FILE), `${JSON.stringify(state, null, 2)}
|
|
13257
|
+
`);
|
|
13258
|
+
};
|
|
13259
|
+
var modeOf2 = (set, policy, shadow) => {
|
|
13260
|
+
if (set.damage) return "active";
|
|
13261
|
+
const chosen = policy.guards[set.id];
|
|
13262
|
+
if (chosen !== void 0) return chosen;
|
|
13263
|
+
if (!policy.exists) return "active";
|
|
13264
|
+
return shadow[set.id]?.promoted === true ? "active" : "shadow";
|
|
13265
|
+
};
|
|
13266
|
+
var recordShadow = (input, paths, set, policy, reason) => {
|
|
13267
|
+
const { runtime } = input;
|
|
13268
|
+
const state = readShadow(runtime, paths);
|
|
13269
|
+
const before = state[set.id] ?? { firings: 0, falsePositives: 0, promoted: false };
|
|
13270
|
+
const firings = before.firings + 1;
|
|
13271
|
+
const promoted = !before.promoted && firings >= policy.promoteAfter && before.falsePositives === 0;
|
|
13272
|
+
writeShadow(runtime, paths, { ...state, [set.id]: { ...before, firings, promoted: before.promoted || promoted } });
|
|
13273
|
+
logEvent(runtime, paths, "guard_shadow", {
|
|
13274
|
+
guard: set.id,
|
|
13275
|
+
hook: input.event,
|
|
13276
|
+
tool: toolName2(input.payload),
|
|
13277
|
+
wouldHave: "block",
|
|
13278
|
+
reason
|
|
13279
|
+
});
|
|
13280
|
+
if (promoted) {
|
|
13281
|
+
logEvent(runtime, paths, "guard_promoted", {
|
|
13282
|
+
guard: set.id,
|
|
13283
|
+
firings,
|
|
13284
|
+
falsePositives: before.falsePositives,
|
|
13285
|
+
promoteAfter: policy.promoteAfter
|
|
13286
|
+
});
|
|
13287
|
+
}
|
|
13288
|
+
};
|
|
13289
|
+
var governed = (set, handler) => async (input) => {
|
|
13290
|
+
const paths = projectPaths(input);
|
|
13291
|
+
if (paths === void 0) return handler(input);
|
|
13292
|
+
const policy = loadPolicy(input.runtime, paths.root);
|
|
13293
|
+
const mode = modeOf2(set, policy, readShadow(input.runtime, paths));
|
|
13294
|
+
if (mode === "off") return { kind: "allow" };
|
|
13295
|
+
const decision = await handler(input);
|
|
13296
|
+
if (mode === "active" || decision.kind !== "block") return decision;
|
|
13297
|
+
recordShadow(input, paths, set, policy, decision.reason);
|
|
13298
|
+
return { kind: "allow" };
|
|
13299
|
+
};
|
|
13300
|
+
var withPolicy = (set) => {
|
|
13301
|
+
if (set.damage) return set.handlers;
|
|
13302
|
+
const wrapped = {};
|
|
13303
|
+
for (const event of HOOK_EVENTS) {
|
|
13304
|
+
const list4 = set.handlers[event];
|
|
13305
|
+
if (list4 !== void 0) wrapped[event] = list4.map((handler) => governed(set, handler));
|
|
13306
|
+
}
|
|
13307
|
+
return wrapped;
|
|
13308
|
+
};
|
|
13309
|
+
|
|
12325
13310
|
// src/prose/marker-guard.ts
|
|
12326
13311
|
import { relative as relative6 } from "node:path";
|
|
12327
13312
|
|
|
@@ -12427,7 +13412,7 @@ var addsMarker = (input, tool, inputs, file) => {
|
|
|
12427
13412
|
return false;
|
|
12428
13413
|
}
|
|
12429
13414
|
};
|
|
12430
|
-
var isReadOnly = (program,
|
|
13415
|
+
var isReadOnly = (program, args2) => READ_ONLY.has(program) || program === "git" && GIT_READ_ONLY.has(args2[0] ?? "");
|
|
12431
13416
|
var shellAddsMarker = (command) => markerCount(command) > 0 && shellWords(command).segments.some((segment) => !isReadOnly(segment.program, segment.args));
|
|
12432
13417
|
var shown2 = (cwd, file) => {
|
|
12433
13418
|
const rel = toPosixPath(relative6(cwd, file));
|
|
@@ -12465,17 +13450,17 @@ var GIT_FLAGS = { title: [], body: ["-m", "--message"], file: ["-F", "--file"] }
|
|
|
12465
13450
|
var GH_FLAGS = { title: ["-t", "--title"], body: ["-b", "--body"], file: ["-F", "--body-file"] };
|
|
12466
13451
|
var GIT_GLOBAL_WITH_VALUE = /* @__PURE__ */ new Set(["-C", "-c", "--git-dir", "--work-tree", "--namespace"]);
|
|
12467
13452
|
var CLUSTER_WITH_MESSAGE = /^-[a-zA-Z]*m$/;
|
|
12468
|
-
var flagValues = (
|
|
13453
|
+
var flagValues = (args2, spec2) => {
|
|
12469
13454
|
const known2 = [...spec2.title, ...spec2.body, ...spec2.file];
|
|
12470
13455
|
const out = [];
|
|
12471
|
-
for (let index = 0; index <
|
|
12472
|
-
const arg =
|
|
13456
|
+
for (let index = 0; index < args2.length; index += 1) {
|
|
13457
|
+
const arg = args2[index] ?? "";
|
|
12473
13458
|
const equals = arg.indexOf("=");
|
|
12474
13459
|
const name = arg.startsWith("--") && equals > 0 ? arg.slice(0, equals) : arg;
|
|
12475
13460
|
if (name !== arg && known2.includes(name)) {
|
|
12476
13461
|
out.push({ flag: name, value: arg.slice(equals + 1) });
|
|
12477
13462
|
} else if (known2.includes(arg) || spec2 === GIT_FLAGS && CLUSTER_WITH_MESSAGE.test(arg)) {
|
|
12478
|
-
out.push({ flag: known2.includes(arg) ? arg : "-m", value:
|
|
13463
|
+
out.push({ flag: known2.includes(arg) ? arg : "-m", value: args2[index + 1] ?? "" });
|
|
12479
13464
|
index += 1;
|
|
12480
13465
|
}
|
|
12481
13466
|
}
|
|
@@ -12489,17 +13474,17 @@ var sourceOf = (value, isFile, segment, heredocs) => {
|
|
|
12489
13474
|
const piped = segment.args.map((arg) => heredocIn(arg, heredocs)).find((text6) => text6 !== void 0);
|
|
12490
13475
|
return { kind: "inline", text: piped ?? "" };
|
|
12491
13476
|
};
|
|
12492
|
-
var collect3 = (target, segment,
|
|
12493
|
-
const values = flagValues(
|
|
13477
|
+
var collect3 = (target, segment, args2, spec2, heredocs) => {
|
|
13478
|
+
const values = flagValues(args2, spec2);
|
|
12494
13479
|
if (values.length === 0) return void 0;
|
|
12495
13480
|
const title = values.find((item) => spec2.title.includes(item.flag));
|
|
12496
13481
|
const bodies = values.filter((item) => !spec2.title.includes(item.flag)).map((item) => sourceOf(item.value, spec2.file.includes(item.flag), segment, heredocs));
|
|
12497
13482
|
return { target, title: title === void 0 ? void 0 : sourceOf(title.value, false, segment, heredocs), bodies };
|
|
12498
13483
|
};
|
|
12499
|
-
var gitSubcommand = (
|
|
13484
|
+
var gitSubcommand = (args2) => {
|
|
12500
13485
|
let index = 0;
|
|
12501
|
-
while (index <
|
|
12502
|
-
index += GIT_GLOBAL_WITH_VALUE.has(
|
|
13486
|
+
while (index < args2.length && (args2[index] ?? "").startsWith("-")) {
|
|
13487
|
+
index += GIT_GLOBAL_WITH_VALUE.has(args2[index] ?? "") ? 2 : 1;
|
|
12503
13488
|
}
|
|
12504
13489
|
return index;
|
|
12505
13490
|
};
|
|
@@ -12631,7 +13616,7 @@ var liveFindings = (input, texts) => {
|
|
|
12631
13616
|
const accepted2 = acceptedFingerprints(baseline.ok ? baseline.value : EMPTY_BASELINE, input.runtime.now());
|
|
12632
13617
|
return texts.flatMap((text6) => scanText(text6, { rules: CREDENTIAL_RULES })).filter((finding3) => !accepted2.has(finding3.fingerprint));
|
|
12633
13618
|
};
|
|
12634
|
-
var
|
|
13619
|
+
var record2 = (input, findings, draft, where) => {
|
|
12635
13620
|
const ids2 = findings.map((finding3) => {
|
|
12636
13621
|
const saved = recordIncident(
|
|
12637
13622
|
input.runtime,
|
|
@@ -12647,7 +13632,7 @@ var secretsUserPromptSubmit = (input) => {
|
|
|
12647
13632
|
const prompt = optionalString(input.payload.raw, "prompt") ?? "";
|
|
12648
13633
|
const findings = liveFindings(input, [prompt]);
|
|
12649
13634
|
if (findings.length === 0) return { kind: "allow" };
|
|
12650
|
-
const incident =
|
|
13635
|
+
const incident = record2(input, findings, { source: "prompt", state: "exposicao-local" }, "prompt");
|
|
12651
13636
|
const items = findings.map((finding3) => ({ ruleId: finding3.ruleId, where: lineWhere(input.t, finding3) }));
|
|
12652
13637
|
return {
|
|
12653
13638
|
kind: "block",
|
|
@@ -12658,7 +13643,7 @@ var secretsPostToolUse = (input) => {
|
|
|
12658
13643
|
const findings = liveFindings(input, stringLeaves(input.payload.raw["tool_response"]));
|
|
12659
13644
|
if (findings.length === 0) return { kind: "allow" };
|
|
12660
13645
|
const tool = toolName2(input.payload) || "tool";
|
|
12661
|
-
const incident =
|
|
13646
|
+
const incident = record2(input, findings, { source: "tool-output", state: "aberto" }, `tool-output:${tool}`);
|
|
12662
13647
|
const items = findings.map((finding3) => ({ ruleId: finding3.ruleId, where: lineWhere(input.t, finding3) }));
|
|
12663
13648
|
const context = input.t("secrets.post.context", { tool, found: describeFindings(input.t, items), incident });
|
|
12664
13649
|
return {
|
|
@@ -12715,7 +13700,7 @@ var READERS = /* @__PURE__ */ new Set([
|
|
|
12715
13700
|
var SECRET_NAME = /(?:KEY|SECRET|TOKEN|PASSWORD|PASSWD|PASS|PWD|CREDENTIAL)/i;
|
|
12716
13701
|
var PERSON_ONLY = /\b(?:finitemonkey|fm)(?:\.mjs)?\s+secrets\s+(?:accept\b|incident\s+(?:rotated|close)\b)/;
|
|
12717
13702
|
var LISTS_ENV = /* @__PURE__ */ new Set(["get-childitem", "gci", "dir", "ls"]);
|
|
12718
|
-
var onlyFlags = (
|
|
13703
|
+
var onlyFlags = (args2) => args2.every((arg) => arg.startsWith("-"));
|
|
12719
13704
|
var readTarget = (segment) => {
|
|
12720
13705
|
const redirected = segment.redirectedFrom.find(isSensitivePath);
|
|
12721
13706
|
if (redirected !== void 0) return redirected;
|
|
@@ -12849,17 +13834,17 @@ var secretsPreToolUse = (input) => {
|
|
|
12849
13834
|
// src/secrets/remotes.ts
|
|
12850
13835
|
var USER_AND_SECRET = /^[a-z][a-z0-9+.-]*:\/\/[^\s/@:]+:[^\s/@]+@/i;
|
|
12851
13836
|
var USERINFO = /^[a-z][a-z0-9+.-]*:\/\/(?<user>[^\s/@]+)@/i;
|
|
12852
|
-
var hasCredential = (
|
|
12853
|
-
if (USER_AND_SECRET.test(
|
|
12854
|
-
const user = USERINFO.exec(
|
|
13837
|
+
var hasCredential = (url2) => {
|
|
13838
|
+
if (USER_AND_SECRET.test(url2)) return true;
|
|
13839
|
+
const user = USERINFO.exec(url2)?.groups?.["user"];
|
|
12855
13840
|
return user !== void 0 && scanText(user, { rules: CREDENTIAL_RULES }).length > 0;
|
|
12856
13841
|
};
|
|
12857
13842
|
var parseRemotes = (output) => {
|
|
12858
13843
|
const remotes = /* @__PURE__ */ new Map();
|
|
12859
13844
|
for (const line2 of output.split(/\r?\n/)) {
|
|
12860
|
-
const [name,
|
|
12861
|
-
if (name === void 0 ||
|
|
12862
|
-
remotes.set(name, [...remotes.get(name) ?? [],
|
|
13845
|
+
const [name, url2] = line2.trim().split(/\s+/);
|
|
13846
|
+
if (name === void 0 || url2 === void 0 || name === "") continue;
|
|
13847
|
+
remotes.set(name, [...remotes.get(name) ?? [], url2]);
|
|
12863
13848
|
}
|
|
12864
13849
|
return remotes;
|
|
12865
13850
|
};
|
|
@@ -12940,12 +13925,13 @@ var SECRETS_HANDLERS = {
|
|
|
12940
13925
|
PostToolUse: [secretsPostToolUse],
|
|
12941
13926
|
SessionStart: [secretsSessionStart]
|
|
12942
13927
|
};
|
|
12943
|
-
var
|
|
12944
|
-
SECRETS_HANDLERS,
|
|
12945
|
-
PORTAO_HANDLERS,
|
|
12946
|
-
PROSE_HANDLERS,
|
|
12947
|
-
GIT_HANDLERS
|
|
12948
|
-
|
|
13928
|
+
var GUARD_SETS = [
|
|
13929
|
+
{ id: "secrets", damage: true, handlers: SECRETS_HANDLERS },
|
|
13930
|
+
{ id: "gate", damage: true, handlers: PORTAO_HANDLERS },
|
|
13931
|
+
{ id: "prose", damage: false, handlers: PROSE_HANDLERS },
|
|
13932
|
+
{ id: "git", damage: true, handlers: GIT_HANDLERS }
|
|
13933
|
+
];
|
|
13934
|
+
var DEFAULT_HANDLERS = mergeHandlers(...GUARD_SETS.map(withPolicy));
|
|
12949
13935
|
|
|
12950
13936
|
// src/hooks/payload.ts
|
|
12951
13937
|
var EMPTY_PAYLOAD = {
|
|
@@ -13107,9 +14093,9 @@ var invocationOf = (entry2, vars) => {
|
|
|
13107
14093
|
const seconds = typeof timeout === "number" && timeout > 0 ? timeout : DEFAULT_TIMEOUT_S;
|
|
13108
14094
|
const timeoutMs = Math.min(seconds * 1e3, PROBE_TIMEOUT_MS2);
|
|
13109
14095
|
const command = expand(raw, vars);
|
|
13110
|
-
const
|
|
13111
|
-
const shell =
|
|
13112
|
-
return { command, args, shell, timeoutMs, display: [command, ...
|
|
14096
|
+
const args2 = strings3(entry2.handler["args"]).map((arg) => expand(arg, vars));
|
|
14097
|
+
const shell = args2.length === 0;
|
|
14098
|
+
return { command, args: args2, shell, timeoutMs, display: [command, ...args2].join(" ") };
|
|
13113
14099
|
};
|
|
13114
14100
|
var versionInvocation = (invocation) => {
|
|
13115
14101
|
if (invocation.shell) {
|
|
@@ -13118,8 +14104,8 @@ var versionInvocation = (invocation) => {
|
|
|
13118
14104
|
}
|
|
13119
14105
|
const at = invocation.args.indexOf("hook");
|
|
13120
14106
|
if (at < 0) return void 0;
|
|
13121
|
-
const
|
|
13122
|
-
return { ...invocation, args, display: [invocation.command, ...
|
|
14107
|
+
const args2 = [...invocation.args.slice(0, at), "version", "--json"];
|
|
14108
|
+
return { ...invocation, args: args2, display: [invocation.command, ...args2].join(" ") };
|
|
13123
14109
|
};
|
|
13124
14110
|
var syntheticPayload = (event, cwd) => {
|
|
13125
14111
|
const base = { session_id: "finitemonkey-doctor", transcript_path: "", cwd, hook_event_name: event };
|
|
@@ -13320,10 +14306,10 @@ var handlerProblems = (at, handler, claude) => {
|
|
|
13320
14306
|
const type = handler["type"];
|
|
13321
14307
|
if (typeof type !== "string" || !HANDLER_TYPES.some((known2) => known2 === type)) return [`${at}.type: unknown`];
|
|
13322
14308
|
const problems = [];
|
|
13323
|
-
const { command, args, timeout, onFailure } = handler;
|
|
14309
|
+
const { command, args: args2, timeout, onFailure } = handler;
|
|
13324
14310
|
if (type === "command" && (typeof command !== "string" || command.trim() === ""))
|
|
13325
14311
|
problems.push(`${at}.command: empty`);
|
|
13326
|
-
if (
|
|
14312
|
+
if (args2 !== void 0 && !(Array.isArray(args2) && args2.every((arg) => typeof arg === "string"))) {
|
|
13327
14313
|
problems.push(`${at}.args: not a list of strings`);
|
|
13328
14314
|
}
|
|
13329
14315
|
if (timeout !== void 0 && !(typeof timeout === "number" && timeout > 0))
|
|
@@ -13356,12 +14342,12 @@ var hookSchemaProblems = (hooks, claude) => {
|
|
|
13356
14342
|
};
|
|
13357
14343
|
|
|
13358
14344
|
// src/doctor/hook-sources.ts
|
|
13359
|
-
import { join as
|
|
14345
|
+
import { join as join30 } from "node:path";
|
|
13360
14346
|
var entriesOf = (source, hooks, own) => Object.entries(asRecord(hooks)).flatMap(
|
|
13361
14347
|
([event, groups]) => asList(groups).flatMap((group) => {
|
|
13362
|
-
const
|
|
13363
|
-
const matcher = optionalString(
|
|
13364
|
-
return asList(
|
|
14348
|
+
const record3 = asRecord(group);
|
|
14349
|
+
const matcher = optionalString(record3, "matcher");
|
|
14350
|
+
return asList(record3["hooks"]).filter((handler) => isRecord(handler) && own(handler)).map((handler) => ({ source, event, matcher, handler }));
|
|
13365
14351
|
})
|
|
13366
14352
|
);
|
|
13367
14353
|
var PROJECT_SCOPES = { project: "project", local: "local" };
|
|
@@ -13369,10 +14355,10 @@ var projectHookFiles = (env) => readSettings(env).flatMap(({ scope: scope2, path
|
|
|
13369
14355
|
const source = PROJECT_SCOPES[scope2];
|
|
13370
14356
|
return source === void 0 || !("hooks" in settings) ? [] : [{ source, path, hooks: settings["hooks"] }];
|
|
13371
14357
|
});
|
|
13372
|
-
var pluginsDir = ({ env, runtime }) =>
|
|
14358
|
+
var pluginsDir = ({ env, runtime }) => join30(env["CLAUDE_CONFIG_DIR"] ?? join30(runtime.homeDir(), ".claude"), "plugins");
|
|
13373
14359
|
var installRecord = (value, root) => {
|
|
13374
14360
|
const records = Array.isArray(value) ? value.filter(isRecord) : isRecord(value) ? [value] : [];
|
|
13375
|
-
return records.find((
|
|
14361
|
+
return records.find((record3) => record3["projectPath"] === root) ?? records[0];
|
|
13376
14362
|
};
|
|
13377
14363
|
var readJson = (env, path) => {
|
|
13378
14364
|
const text6 = env.runtime.readText(path);
|
|
@@ -13380,18 +14366,18 @@ var readJson = (env, path) => {
|
|
|
13380
14366
|
return parsed?.ok === true ? parsed.value : void 0;
|
|
13381
14367
|
};
|
|
13382
14368
|
var installedPlugin = (env) => {
|
|
13383
|
-
const index = readJson(env, toAbsolutePath(
|
|
13384
|
-
const
|
|
13385
|
-
const installPath =
|
|
13386
|
-
if (
|
|
14369
|
+
const index = readJson(env, toAbsolutePath(join30(pluginsDir(env), "installed_plugins.json")));
|
|
14370
|
+
const record3 = installRecord(asRecord(index?.["plugins"])[PLUGIN_ID], env.root);
|
|
14371
|
+
const installPath = record3 === void 0 ? void 0 : optionalString(record3, "installPath");
|
|
14372
|
+
if (record3 === void 0 || installPath === void 0) return void 0;
|
|
13387
14373
|
const root = toAbsolutePath(installPath);
|
|
13388
|
-
const manifest = readJson(env, toAbsolutePath(
|
|
13389
|
-
const hooksPath = toAbsolutePath(
|
|
14374
|
+
const manifest = readJson(env, toAbsolutePath(join30(root, ".claude-plugin", "plugin.json")));
|
|
14375
|
+
const hooksPath = toAbsolutePath(join30(root, "hooks", "hooks.json"));
|
|
13390
14376
|
const text6 = env.runtime.readText(hooksPath);
|
|
13391
14377
|
const parsed = text6.ok ? parseJsonObject(text6.value) : void 0;
|
|
13392
14378
|
return {
|
|
13393
14379
|
root,
|
|
13394
|
-
version: (manifest === void 0 ? void 0 : optionalString(manifest, "version")) ?? optionalString(
|
|
14380
|
+
version: (manifest === void 0 ? void 0 : optionalString(manifest, "version")) ?? optionalString(record3, "version"),
|
|
13395
14381
|
hooksFile: parsed?.ok === true ? { source: "plugin", path: hooksPath, hooks: parsed.value["hooks"] } : void 0,
|
|
13396
14382
|
hooksText: text6.ok ? text6.value : void 0
|
|
13397
14383
|
};
|
|
@@ -13431,7 +14417,7 @@ var schemaFindings = (env, plugin, claude) => {
|
|
|
13431
14417
|
const files = [...projectHookFiles(env), ...plugin?.hooksFile === void 0 ? [] : [plugin.hooksFile]];
|
|
13432
14418
|
const invalidJson = [
|
|
13433
14419
|
...unreadableProjectFiles(env),
|
|
13434
|
-
...plugin?.hooksText !== void 0 && plugin.hooksFile === void 0 ? [toAbsolutePath(
|
|
14420
|
+
...plugin?.hooksText !== void 0 && plugin.hooksFile === void 0 ? [toAbsolutePath(join31(plugin.root, "hooks", "hooks.json"))] : []
|
|
13435
14421
|
];
|
|
13436
14422
|
return [
|
|
13437
14423
|
...invalidJson.flatMap((file) => schemaFinding(file, ["not valid JSON"], label2)),
|
|
@@ -13452,7 +14438,7 @@ var pluginFindings = (env, projectOwn) => {
|
|
|
13452
14438
|
];
|
|
13453
14439
|
};
|
|
13454
14440
|
var coverageFindings = (env, entries) => {
|
|
13455
|
-
const installed = env.runtime.stat(toAbsolutePath(
|
|
14441
|
+
const installed = env.runtime.stat(toAbsolutePath(join31(env.root, ...LOCK_FILE.split("/")))) !== void 0;
|
|
13456
14442
|
if (entries.length === 0 && !installed) {
|
|
13457
14443
|
return [finding("hooks.registered", "warn", "doctor.hooks.none", {}, "doctor.hooks.fix_none")];
|
|
13458
14444
|
}
|
|
@@ -13504,17 +14490,17 @@ var checkHooks = (env) => {
|
|
|
13504
14490
|
};
|
|
13505
14491
|
|
|
13506
14492
|
// src/doctor/instructions.ts
|
|
13507
|
-
import { join as
|
|
14493
|
+
import { join as join32 } from "node:path";
|
|
13508
14494
|
var INSTRUCTION_LINE_CEILING = 200;
|
|
13509
14495
|
var TOP_INSTRUCTIONS = ["AGENTS.md", "CLAUDE.md", ".claude/CLAUDE.md", "CLAUDE.local.md"];
|
|
13510
14496
|
var RULES_DIR = ".claude/rules";
|
|
13511
14497
|
var SHOWN = 3;
|
|
13512
14498
|
var readProjectFile = (env, relative9) => {
|
|
13513
|
-
const text6 = env.runtime.readText(toAbsolutePath(
|
|
14499
|
+
const text6 = env.runtime.readText(toAbsolutePath(join32(env.root, ...relative9.split("/"))));
|
|
13514
14500
|
return text6.ok ? text6.value : void 0;
|
|
13515
14501
|
};
|
|
13516
14502
|
var instructionFiles = (env) => {
|
|
13517
|
-
const listed4 = env.runtime.listDir(toAbsolutePath(
|
|
14503
|
+
const listed4 = env.runtime.listDir(toAbsolutePath(join32(env.root, ...RULES_DIR.split("/"))));
|
|
13518
14504
|
const rules = listed4.ok ? listed4.value.filter((name) => name.endsWith(".md")).map((name) => `${RULES_DIR}/${name}`) : [];
|
|
13519
14505
|
return [...TOP_INSTRUCTIONS, ...rules].flatMap((path) => {
|
|
13520
14506
|
const text6 = readProjectFile(env, path);
|
|
@@ -13619,12 +14605,12 @@ var checkInstructions = (env) => {
|
|
|
13619
14605
|
};
|
|
13620
14606
|
|
|
13621
14607
|
// src/doctor/line-endings.ts
|
|
13622
|
-
import { join as
|
|
14608
|
+
import { join as join33 } from "node:path";
|
|
13623
14609
|
var LF_RULE = /^\*\s.*\beol=lf\b/m;
|
|
13624
14610
|
var checkLineEndings = ({ root, runtime }) => {
|
|
13625
14611
|
const autocrlf = runtime.git(["config", "--get", "core.autocrlf"], root);
|
|
13626
14612
|
if (!autocrlf.ok || autocrlf.stdout.trim().toLowerCase() !== "true") return [];
|
|
13627
|
-
const attributes = runtime.readText(toAbsolutePath(
|
|
14613
|
+
const attributes = runtime.readText(toAbsolutePath(join33(root, ".gitattributes")));
|
|
13628
14614
|
if (attributes.ok && LF_RULE.test(attributes.value)) return [];
|
|
13629
14615
|
return [
|
|
13630
14616
|
finding("git.autocrlf", "warn", "doctor.git.autocrlf", { file: ".gitattributes" }, "doctor.git.fix_autocrlf")
|
|
@@ -13632,7 +14618,7 @@ var checkLineEndings = ({ root, runtime }) => {
|
|
|
13632
14618
|
};
|
|
13633
14619
|
|
|
13634
14620
|
// src/doctor/memory.ts
|
|
13635
|
-
import { join as
|
|
14621
|
+
import { join as join34 } from "node:path";
|
|
13636
14622
|
var ceilingOf = (config) => {
|
|
13637
14623
|
const value = configAt(parseConfigYaml(config ?? ""), "memory", "maxLines");
|
|
13638
14624
|
const parsed = typeof value === "string" ? Number.parseInt(value, 10) : Number.NaN;
|
|
@@ -13640,9 +14626,9 @@ var ceilingOf = (config) => {
|
|
|
13640
14626
|
};
|
|
13641
14627
|
var lineCount2 = (text6) => text6.replace(/\r?\n$/, "").split(/\r?\n/).length;
|
|
13642
14628
|
var checkAgentsFile = ({ root, runtime }) => {
|
|
13643
|
-
const text6 = runtime.readText(toAbsolutePath(
|
|
14629
|
+
const text6 = runtime.readText(toAbsolutePath(join34(root, AGENTS_FILE)));
|
|
13644
14630
|
if (!text6.ok) return [];
|
|
13645
|
-
const config = runtime.readText(toAbsolutePath(
|
|
14631
|
+
const config = runtime.readText(toAbsolutePath(join34(root, CONFIG_FILE)));
|
|
13646
14632
|
const max = ceilingOf(config.ok ? config.value : void 0);
|
|
13647
14633
|
const lines3 = lineCount2(text6.value);
|
|
13648
14634
|
const params2 = { file: AGENTS_FILE, lines: lines3, max };
|
|
@@ -13650,7 +14636,7 @@ var checkAgentsFile = ({ root, runtime }) => {
|
|
|
13650
14636
|
};
|
|
13651
14637
|
|
|
13652
14638
|
// src/doctor/project-files.ts
|
|
13653
|
-
import { join as
|
|
14639
|
+
import { join as join35 } from "node:path";
|
|
13654
14640
|
var variants = (base, extensions) => extensions.map((extension) => `${base}${extension}`);
|
|
13655
14641
|
var SCRIPT = [".js", ".cjs", ".mjs", ".ts", ".cts", ".mts"];
|
|
13656
14642
|
var DATA = ["", ".json", ".yml", ".yaml"];
|
|
@@ -13681,7 +14667,7 @@ var CONFIG_GROUPS = [
|
|
|
13681
14667
|
files: [...variants("commitlint.config", SCRIPT), ...variants(".commitlintrc", [...DATA, ".js"])]
|
|
13682
14668
|
}
|
|
13683
14669
|
];
|
|
13684
|
-
var present = (env, files) => files.filter((file) => env.runtime.stat(toAbsolutePath(
|
|
14670
|
+
var present = (env, files) => files.filter((file) => env.runtime.stat(toAbsolutePath(join35(env.root, file)))?.kind === "file");
|
|
13685
14671
|
var crowded = (env, groups, build) => groups.flatMap(({ name, files }) => {
|
|
13686
14672
|
const found = present(env, files);
|
|
13687
14673
|
return found.length > 1 ? [build(name, found.join(", "))] : [];
|
|
@@ -13742,9 +14728,9 @@ var CHECKS = [
|
|
|
13742
14728
|
];
|
|
13743
14729
|
var probeOnce = (probe) => {
|
|
13744
14730
|
const seen = /* @__PURE__ */ new Map();
|
|
13745
|
-
return (command,
|
|
13746
|
-
const key = [command, ...
|
|
13747
|
-
const known2 = seen.get(key) ?? probe(command,
|
|
14731
|
+
return (command, args2, timeoutMs) => {
|
|
14732
|
+
const key = [command, ...args2].join(" ");
|
|
14733
|
+
const known2 = seen.get(key) ?? probe(command, args2, timeoutMs);
|
|
13748
14734
|
seen.set(key, known2);
|
|
13749
14735
|
return known2;
|
|
13750
14736
|
};
|
|
@@ -13855,93 +14841,7 @@ var doctorCommand = {
|
|
|
13855
14841
|
usage: "finitemonkey doctor [--fix] [--report] [--dry-run] [--json]",
|
|
13856
14842
|
status: "ready",
|
|
13857
14843
|
options: { fix: { type: "boolean" }, report: { type: "boolean" } },
|
|
13858
|
-
run: (ctx,
|
|
13859
|
-
};
|
|
13860
|
-
|
|
13861
|
-
// src/core/args.ts
|
|
13862
|
-
import { parseArgs } from "node:util";
|
|
13863
|
-
|
|
13864
|
-
// src/core/problem.ts
|
|
13865
|
-
var usageProblem = (code, messageKey, fix, params2 = {}) => ({
|
|
13866
|
-
code,
|
|
13867
|
-
messageKey,
|
|
13868
|
-
params: params2,
|
|
13869
|
-
fix,
|
|
13870
|
-
exitCode: ExitCode.Usage
|
|
13871
|
-
});
|
|
13872
|
-
var needsPersonProblem = (messageKey, fix, params2 = {}) => ({
|
|
13873
|
-
code: "needs_confirmation",
|
|
13874
|
-
messageKey,
|
|
13875
|
-
params: params2,
|
|
13876
|
-
fix,
|
|
13877
|
-
exitCode: ExitCode.NeedsPerson
|
|
13878
|
-
});
|
|
13879
|
-
var FmError = class extends Error {
|
|
13880
|
-
problem;
|
|
13881
|
-
constructor(problem4) {
|
|
13882
|
-
super(problem4.code);
|
|
13883
|
-
this.name = "FmError";
|
|
13884
|
-
this.problem = problem4;
|
|
13885
|
-
}
|
|
13886
|
-
};
|
|
13887
|
-
|
|
13888
|
-
// src/core/args.ts
|
|
13889
|
-
var GLOBAL_OPTIONS = {
|
|
13890
|
-
json: { type: "boolean" },
|
|
13891
|
-
lang: { type: "string" },
|
|
13892
|
-
yes: { type: "boolean", short: "y" },
|
|
13893
|
-
"dry-run": { type: "boolean" },
|
|
13894
|
-
version: { type: "boolean", short: "v" },
|
|
13895
|
-
help: { type: "boolean", short: "h" }
|
|
13896
|
-
};
|
|
13897
|
-
var flag = (values, name) => values[name] === true;
|
|
13898
|
-
var looseArgs = (argv) => {
|
|
13899
|
-
const { values, positionals } = parseArgs({
|
|
13900
|
-
args: [...argv],
|
|
13901
|
-
options: GLOBAL_OPTIONS,
|
|
13902
|
-
strict: false,
|
|
13903
|
-
allowPositionals: true
|
|
13904
|
-
});
|
|
13905
|
-
const lang = values.lang;
|
|
13906
|
-
return {
|
|
13907
|
-
positionals,
|
|
13908
|
-
json: flag(values, "json"),
|
|
13909
|
-
yes: flag(values, "yes"),
|
|
13910
|
-
dryRun: flag(values, "dry-run"),
|
|
13911
|
-
version: flag(values, "version"),
|
|
13912
|
-
help: flag(values, "help"),
|
|
13913
|
-
lang: typeof lang === "string" ? lang : void 0
|
|
13914
|
-
};
|
|
13915
|
-
};
|
|
13916
|
-
var resolveCommand = (registry, positionals) => registry.filter((spec2) => spec2.path.every((part2, index) => positionals[index] === part2)).reduce(
|
|
13917
|
-
(best, spec2) => best === void 0 || spec2.path.length > best.consumed ? { spec: spec2, consumed: spec2.path.length } : best,
|
|
13918
|
-
void 0
|
|
13919
|
-
);
|
|
13920
|
-
var strictArgs = (argv, spec2) => {
|
|
13921
|
-
const fix = `finitemonkey help ${spec2.path.join(" ")}`;
|
|
13922
|
-
let parsed;
|
|
13923
|
-
try {
|
|
13924
|
-
parsed = parseArgs({
|
|
13925
|
-
args: [...argv],
|
|
13926
|
-
options: { ...GLOBAL_OPTIONS, ...spec2.options },
|
|
13927
|
-
strict: true,
|
|
13928
|
-
allowPositionals: true
|
|
13929
|
-
});
|
|
13930
|
-
} catch (error) {
|
|
13931
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
13932
|
-
return reject(usageProblem("invalid_arguments", "error.invalid_arguments", fix, { detail }));
|
|
13933
|
-
}
|
|
13934
|
-
const rest = parsed.positionals.slice(spec2.path.length);
|
|
13935
|
-
const max = spec2.maxPositionals ?? 0;
|
|
13936
|
-
const extra = rest[max];
|
|
13937
|
-
if (extra !== void 0) {
|
|
13938
|
-
return reject(usageProblem("unexpected_argument", "error.unexpected_argument", fix, { argument: extra }));
|
|
13939
|
-
}
|
|
13940
|
-
return accept({ values: parsed.values, positionals: rest });
|
|
13941
|
-
};
|
|
13942
|
-
var stringOption = (values, name) => {
|
|
13943
|
-
const value = values[name];
|
|
13944
|
-
return typeof value === "string" ? value : void 0;
|
|
14844
|
+
run: (ctx, args2) => Promise.resolve(runDoctor(ctx, args2.values["fix"] === true, args2.values["report"] === true))
|
|
13945
14845
|
};
|
|
13946
14846
|
|
|
13947
14847
|
// src/gate/baseline-run.ts
|
|
@@ -13956,8 +14856,8 @@ var runBaseline = (options) => {
|
|
|
13956
14856
|
if (commands.length === 0) return { kind: "skipped", reason: "no_commands" };
|
|
13957
14857
|
const checks = {};
|
|
13958
14858
|
for (const check2 of commands) {
|
|
13959
|
-
const
|
|
13960
|
-
checks[check2.id] = { exitCode:
|
|
14859
|
+
const outcome2 = runShellCheck(check2, check2.run ?? "", ctx);
|
|
14860
|
+
checks[check2.id] = { exitCode: outcome2.exitCode ?? null, failures: outcome2.failures ?? [] };
|
|
13961
14861
|
}
|
|
13962
14862
|
if (treeHash(gitAt(ctx.runtime, ctx.paths.root)) !== ctx.treeHash) return { kind: "skipped", reason: "tree_moved" };
|
|
13963
14863
|
writeBaseline(ctx.runtime, ctx.paths, ctx.treeHash, checks);
|
|
@@ -14016,19 +14916,19 @@ var gateCommand = {
|
|
|
14016
14916
|
baseline: { type: "boolean" },
|
|
14017
14917
|
prs: { type: "boolean" }
|
|
14018
14918
|
},
|
|
14019
|
-
run: (ctx,
|
|
14020
|
-
const taskId = stringOption(
|
|
14919
|
+
run: (ctx, args2) => {
|
|
14920
|
+
const taskId = stringOption(args2.values, "task");
|
|
14021
14921
|
const options = {
|
|
14022
14922
|
runtime: ctx.runtime,
|
|
14023
14923
|
cwd: ctx.cwd,
|
|
14024
14924
|
env: ctx.env,
|
|
14025
14925
|
sessionId: void 0,
|
|
14026
|
-
budgetMs: budgetMs(stringOption(
|
|
14027
|
-
fast:
|
|
14926
|
+
budgetMs: budgetMs(stringOption(args2.values, "budget")),
|
|
14927
|
+
fast: args2.values["fast"] === true,
|
|
14028
14928
|
...taskId === void 0 ? {} : { taskId }
|
|
14029
14929
|
};
|
|
14030
|
-
if (
|
|
14031
|
-
if (
|
|
14930
|
+
if (args2.values["prs"] === true) return Promise.resolve(prsResult(ctx));
|
|
14931
|
+
if (args2.values["baseline"] === true) {
|
|
14032
14932
|
const baseline = runBaseline(options);
|
|
14033
14933
|
return Promise.resolve(
|
|
14034
14934
|
success(baseline, [
|
|
@@ -14038,7 +14938,7 @@ var gateCommand = {
|
|
|
14038
14938
|
])
|
|
14039
14939
|
);
|
|
14040
14940
|
}
|
|
14041
|
-
return Promise.resolve(runResult(ctx, runGate(options),
|
|
14941
|
+
return Promise.resolve(runResult(ctx, runGate(options), args2.values["strict"] === true));
|
|
14042
14942
|
}
|
|
14043
14943
|
};
|
|
14044
14944
|
|
|
@@ -14119,11 +15019,11 @@ var mergeVerifyCommand = {
|
|
|
14119
15019
|
status: "ready",
|
|
14120
15020
|
options: { since: { type: "string" }, dropped: { type: "string" } },
|
|
14121
15021
|
maxPositionals: 1,
|
|
14122
|
-
run: (ctx,
|
|
15022
|
+
run: (ctx, args2) => {
|
|
14123
15023
|
const repo = repoOf2(ctx);
|
|
14124
15024
|
if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
|
|
14125
|
-
const dropped = stringOption(
|
|
14126
|
-
const targets2 = targetsOf2(ctx, repo,
|
|
15025
|
+
const dropped = stringOption(args2.values, "dropped");
|
|
15026
|
+
const targets2 = targetsOf2(ctx, repo, args2.positionals[0], stringOption(args2.values, "since"));
|
|
14127
15027
|
if (targets2.length === 0) {
|
|
14128
15028
|
const data = { schema: MERGE_VERIFY_RUN_SCHEMA, merges: [] };
|
|
14129
15029
|
return Promise.resolve(success(data, [ctx.t("git.merge.none", { detail: "HEAD" })]));
|
|
@@ -14160,11 +15060,11 @@ var unreachable = (runtime, repo, shas) => {
|
|
|
14160
15060
|
var fsckDangling = (runtime, repo) => {
|
|
14161
15061
|
const result = runtime.git(["fsck", "--no-reflogs", "--no-progress", "--dangling"], repo.root);
|
|
14162
15062
|
const lines3 = linesOf(result.stdout);
|
|
14163
|
-
const
|
|
15063
|
+
const pick4 = (kind) => lines3.flatMap((line2) => {
|
|
14164
15064
|
const [label2, type, sha] = line2.split(" ");
|
|
14165
15065
|
return label2 === "dangling" && type === kind && sha !== void 0 ? [sha] : [];
|
|
14166
15066
|
});
|
|
14167
|
-
return { commits:
|
|
15067
|
+
return { commits: pick4("commit"), blobs: pick4("blob").length };
|
|
14168
15068
|
};
|
|
14169
15069
|
var describe = (runtime, repo, shas) => {
|
|
14170
15070
|
if (shas.length === 0) return /* @__PURE__ */ new Map();
|
|
@@ -14231,11 +15131,11 @@ var rescueCommand = {
|
|
|
14231
15131
|
usage: "finitemonkey rescue [--restore <sha> --branch <name>] [--json]",
|
|
14232
15132
|
status: "ready",
|
|
14233
15133
|
options: { restore: { type: "string" }, branch: { type: "string" } },
|
|
14234
|
-
run: (ctx,
|
|
15134
|
+
run: (ctx, args2) => {
|
|
14235
15135
|
const repo = repoOf2(ctx);
|
|
14236
15136
|
if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
|
|
14237
|
-
const sha = stringOption(
|
|
14238
|
-
const branch = stringOption(
|
|
15137
|
+
const sha = stringOption(args2.values, "restore");
|
|
15138
|
+
const branch = stringOption(args2.values, "branch");
|
|
14239
15139
|
if (sha === void 0 && branch === void 0) {
|
|
14240
15140
|
const report2 = rescueReport(ctx.runtime, repo);
|
|
14241
15141
|
return Promise.resolve(success({ schema: RESCUE_SCHEMA, action: "list", ...report2 }, listing(ctx.t, report2)));
|
|
@@ -14264,10 +15164,10 @@ var rescueCommand = {
|
|
|
14264
15164
|
};
|
|
14265
15165
|
|
|
14266
15166
|
// src/git/undo.ts
|
|
14267
|
-
import { basename as basename3, dirname as dirname8, join as
|
|
15167
|
+
import { basename as basename3, dirname as dirname8, join as join36 } from "node:path";
|
|
14268
15168
|
var INDEX_SUBJECT = "finitemonkey index:";
|
|
14269
|
-
var gitLine = (runtime, repo,
|
|
14270
|
-
const result = runtime.git(
|
|
15169
|
+
var gitLine = (runtime, repo, args2) => {
|
|
15170
|
+
const result = runtime.git(args2, repo.root);
|
|
14271
15171
|
const value = result.stdout.trim();
|
|
14272
15172
|
return result.ok && value !== "" ? value : void 0;
|
|
14273
15173
|
};
|
|
@@ -14307,7 +15207,7 @@ var findSnapshot = (views, wanted) => {
|
|
|
14307
15207
|
};
|
|
14308
15208
|
var stampOf2 = (iso) => iso.replace(/[:.]/g, "-");
|
|
14309
15209
|
var worktreePath = (runtime, repo) => {
|
|
14310
|
-
const base =
|
|
15210
|
+
const base = join36(dirname8(repo.root), `${basename3(repo.root)}-undo-${stampOf2(runtime.now())}`);
|
|
14311
15211
|
return toAbsolutePath(runtime.stat(toAbsolutePath(base)) === void 0 ? base : `${base}-2`);
|
|
14312
15212
|
};
|
|
14313
15213
|
var intoWorktree = (runtime, repo, view2) => {
|
|
@@ -14315,13 +15215,13 @@ var intoWorktree = (runtime, repo, view2) => {
|
|
|
14315
15215
|
const added = runtime.gitWith(["worktree", "add", "--detach", path, view2.commit], repo.root, {});
|
|
14316
15216
|
return added.ok ? accept(path) : reject(failure2("worktree add", added));
|
|
14317
15217
|
};
|
|
14318
|
-
var step2 = (runtime, repo,
|
|
14319
|
-
const result = runtime.gitWith(
|
|
14320
|
-
return result.ok ? accept(void 0) : reject(failure2(
|
|
15218
|
+
var step2 = (runtime, repo, args2) => {
|
|
15219
|
+
const result = runtime.gitWith(args2, repo.root, {});
|
|
15220
|
+
return result.ok ? accept(void 0) : reject(failure2(args2.join(" "), result));
|
|
14321
15221
|
};
|
|
14322
15222
|
var inPlace = (runtime, repo, view2) => {
|
|
14323
|
-
const
|
|
14324
|
-
if (view2.head !== void 0 && view2.head !==
|
|
15223
|
+
const current2 = gitLine(runtime, repo, ["rev-parse", "--verify", "--quiet", "HEAD"]);
|
|
15224
|
+
if (view2.head !== void 0 && view2.head !== current2) {
|
|
14325
15225
|
const moved = step2(runtime, repo, ["reset", "--soft", view2.head]);
|
|
14326
15226
|
if (!moved.ok) return moved;
|
|
14327
15227
|
}
|
|
@@ -14350,10 +15250,10 @@ var snapshotCommand = {
|
|
|
14350
15250
|
usage: "finitemonkey snapshot [--message <text>] [--json]",
|
|
14351
15251
|
status: "ready",
|
|
14352
15252
|
options: { message: { type: "string", short: "m" } },
|
|
14353
|
-
run: (ctx,
|
|
15253
|
+
run: (ctx, args2) => {
|
|
14354
15254
|
const repo = repoOf2(ctx);
|
|
14355
15255
|
if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
|
|
14356
|
-
const message =
|
|
15256
|
+
const message = args2.values["message"];
|
|
14357
15257
|
const op = typeof message === "string" && message.trim() !== "" ? message.trim() : "manual";
|
|
14358
15258
|
const taken = takeSnapshot(ctx.runtime, repo, { op });
|
|
14359
15259
|
if (!taken.ok) {
|
|
@@ -14383,12 +15283,12 @@ var undoCommand = {
|
|
|
14383
15283
|
status: "ready",
|
|
14384
15284
|
options: { list: { type: "boolean" }, "in-place": { type: "boolean" } },
|
|
14385
15285
|
maxPositionals: 1,
|
|
14386
|
-
run: (ctx,
|
|
15286
|
+
run: (ctx, args2) => {
|
|
14387
15287
|
const repo = repoOf2(ctx);
|
|
14388
15288
|
if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
|
|
14389
15289
|
const views = snapshotViews(ctx.runtime, repo);
|
|
14390
|
-
if (
|
|
14391
|
-
const wanted =
|
|
15290
|
+
if (args2.values["list"] === true) return Promise.resolve(listResult(ctx, views));
|
|
15291
|
+
const wanted = args2.positionals[0];
|
|
14392
15292
|
const view2 = findSnapshot(views, wanted);
|
|
14393
15293
|
if (view2 === void 0) {
|
|
14394
15294
|
return Promise.resolve(
|
|
@@ -14401,7 +15301,7 @@ var undoCommand = {
|
|
|
14401
15301
|
})
|
|
14402
15302
|
);
|
|
14403
15303
|
}
|
|
14404
|
-
const inPlace2 =
|
|
15304
|
+
const inPlace2 = args2.values["in-place"] === true;
|
|
14405
15305
|
if (inPlace2 && !(ctx.yes && isPerson(ctx))) {
|
|
14406
15306
|
return Promise.resolve(
|
|
14407
15307
|
failureFrom(ctx.t, needsPersonProblem("git.undo.confirm", `finitemonkey undo ${view2.ref} --in-place --yes`))
|
|
@@ -14490,8 +15390,8 @@ var helpCommand = {
|
|
|
14490
15390
|
usage: "finitemonkey help [command]",
|
|
14491
15391
|
status: "ready",
|
|
14492
15392
|
maxPositionals: 3,
|
|
14493
|
-
run: (ctx,
|
|
14494
|
-
|
|
15393
|
+
run: (ctx, args2, registry) => Promise.resolve(
|
|
15394
|
+
args2.positionals.length === 0 ? overview(ctx, registry) : topicHelp(ctx, registry, args2.positionals)
|
|
14495
15395
|
)
|
|
14496
15396
|
};
|
|
14497
15397
|
|
|
@@ -14503,8 +15403,8 @@ var hookCommand = {
|
|
|
14503
15403
|
status: "ready",
|
|
14504
15404
|
maxPositionals: 1,
|
|
14505
15405
|
options: { via: { type: "string" } },
|
|
14506
|
-
run: async (ctx,
|
|
14507
|
-
const raw =
|
|
15406
|
+
run: async (ctx, args2) => {
|
|
15407
|
+
const raw = args2.positionals[0] ?? "";
|
|
14508
15408
|
const event = parseHookEvent(raw);
|
|
14509
15409
|
if (event === void 0) {
|
|
14510
15410
|
const params2 = { event: raw, events: HOOK_EVENTS.join(", ") };
|
|
@@ -14513,10 +15413,10 @@ var hookCommand = {
|
|
|
14513
15413
|
usageProblem("unknown_hook_event", "error.unknown_hook_event", "finitemonkey help hook", params2)
|
|
14514
15414
|
);
|
|
14515
15415
|
}
|
|
14516
|
-
if (stringOption(
|
|
15416
|
+
if (stringOption(args2.values, "via") === PLUGIN_VIA && projectOwnsHooks(ctx)) {
|
|
14517
15417
|
return { ok: true, exitCode: 0, data: { event, verdict: "allow" }, raw: { stdout: YIELD_STDOUT, stderr: "" } };
|
|
14518
15418
|
}
|
|
14519
|
-
const
|
|
15419
|
+
const outcome2 = await runHook(event, {
|
|
14520
15420
|
stdin: ctx.io.stdin,
|
|
14521
15421
|
deadlineMs: deadlineFor(event, ctx.env["FINITEMONKEY_HOOK_DEADLINE_MS"]),
|
|
14522
15422
|
handlers: DEFAULT_HANDLERS,
|
|
@@ -14526,11 +15426,11 @@ var hookCommand = {
|
|
|
14526
15426
|
runtime: ctx.runtime,
|
|
14527
15427
|
env: ctx.env
|
|
14528
15428
|
});
|
|
14529
|
-
logHookFailure(ctx, event,
|
|
14530
|
-
const data = { event, verdict:
|
|
14531
|
-
const output = { stdout:
|
|
14532
|
-
if (
|
|
14533
|
-
const message =
|
|
15429
|
+
logHookFailure(ctx, event, outcome2);
|
|
15430
|
+
const data = { event, verdict: outcome2.verdict };
|
|
15431
|
+
const output = { stdout: outcome2.stdout, stderr: outcome2.stderr };
|
|
15432
|
+
if (outcome2.exitCode === 0) return { ok: true, exitCode: 0, data, raw: output };
|
|
15433
|
+
const message = outcome2.stderr.trim();
|
|
14534
15434
|
return {
|
|
14535
15435
|
ok: false,
|
|
14536
15436
|
exitCode: 2,
|
|
@@ -14542,7 +15442,7 @@ var hookCommand = {
|
|
|
14542
15442
|
};
|
|
14543
15443
|
|
|
14544
15444
|
// src/commands/i18n-check.ts
|
|
14545
|
-
import { join as
|
|
15445
|
+
import { join as join37 } from "node:path";
|
|
14546
15446
|
|
|
14547
15447
|
// src/i18n/message.ts
|
|
14548
15448
|
var ARG = /^\s*([a-zA-Z][a-zA-Z0-9_]*)\s*(?:,\s*(plural|select)\s*,)?/;
|
|
@@ -14744,7 +15644,7 @@ var unreadable = (dir, file, detail) => ({
|
|
|
14744
15644
|
exitCode: ExitCode.NotConfigured
|
|
14745
15645
|
});
|
|
14746
15646
|
var readCatalog = (ctx, dir, lang) => {
|
|
14747
|
-
const file = toAbsolutePath(
|
|
15647
|
+
const file = toAbsolutePath(join37(dir, `${lang}.json`));
|
|
14748
15648
|
const text6 = ctx.runtime.readText(file);
|
|
14749
15649
|
if (!text6.ok) return reject(unreadable(dir, file, text6.error));
|
|
14750
15650
|
const parsed = parseJsonObject(text6.value);
|
|
@@ -14767,7 +15667,7 @@ var describeProblem = (ctx, problem4) => {
|
|
|
14767
15667
|
return ` ${problem4.lang} ${problem4.key} ${kind}`;
|
|
14768
15668
|
};
|
|
14769
15669
|
var loadReview = (ctx, dir) => {
|
|
14770
|
-
const text6 = ctx.runtime.readText(toAbsolutePath(
|
|
15670
|
+
const text6 = ctx.runtime.readText(toAbsolutePath(join37(dir, "review.json")));
|
|
14771
15671
|
const parsed = text6.ok ? parseJsonObject(text6.value) : void 0;
|
|
14772
15672
|
if (parsed?.ok !== true) return void 0;
|
|
14773
15673
|
const statuses = (lang) => {
|
|
@@ -14801,17 +15701,17 @@ var i18nCheckCommand = {
|
|
|
14801
15701
|
usage: "finitemonkey i18n check [--dir <path>]",
|
|
14802
15702
|
status: "ready",
|
|
14803
15703
|
options: { dir: { type: "string" } },
|
|
14804
|
-
run: (ctx,
|
|
14805
|
-
const dir = stringOption(
|
|
15704
|
+
run: (ctx, args2) => {
|
|
15705
|
+
const dir = stringOption(args2.values, "dir");
|
|
14806
15706
|
return Promise.resolve(runI18nCheck(ctx, dir === void 0 ? void 0 : toAbsolutePath(dir, ctx.cwd)));
|
|
14807
15707
|
}
|
|
14808
15708
|
};
|
|
14809
15709
|
|
|
14810
15710
|
// src/history/look-back.ts
|
|
14811
|
-
import { join as
|
|
15711
|
+
import { join as join38 } from "node:path";
|
|
14812
15712
|
|
|
14813
15713
|
// src/history/patterns.ts
|
|
14814
|
-
import { posix as
|
|
15714
|
+
import { posix as posix5 } from "node:path";
|
|
14815
15715
|
var PATTERNS = ["done_after_failing_tests", "env_read", "no_verify", "force_push", "test_skip"];
|
|
14816
15716
|
var TEST_COMMAND = /(^|[\s;&|(])((npm|pnpm|yarn|bun)\s+(run\s+)?(test|check)\b|npx\s+(vitest|jest)\b|vitest\b|jest\b|pytest\b|go\s+test\b|cargo\s+test\b|node\s+--test\b|mocha\b)/;
|
|
14817
15717
|
var DONE_CLAIM = /\b(done|all (tests|checks) (pass|passed|are passing)|everything (passes|works)|it works|pronto|feito|conclu[ií]do|listo|terminado)\b/i;
|
|
@@ -14833,7 +15733,7 @@ var stringField = (tool, name) => {
|
|
|
14833
15733
|
return typeof value === "string" ? value : "";
|
|
14834
15734
|
};
|
|
14835
15735
|
var isEnvPath = (path) => {
|
|
14836
|
-
const name =
|
|
15736
|
+
const name = posix5.basename(path.replace(/\\/g, "/"));
|
|
14837
15737
|
return /^\.env(\.[A-Za-z0-9_-]+)?$/.test(name) && !ENV_TEMPLATE.test(name);
|
|
14838
15738
|
};
|
|
14839
15739
|
var envRead = (tool) => {
|
|
@@ -14921,7 +15821,7 @@ var addResults = (draft, blocks) => {
|
|
|
14921
15821
|
};
|
|
14922
15822
|
var parseTranscript = (jsonl) => {
|
|
14923
15823
|
const turns = [];
|
|
14924
|
-
let
|
|
15824
|
+
let current2;
|
|
14925
15825
|
for (const line2 of jsonl.split(/\r?\n/)) {
|
|
14926
15826
|
if (line2.trim() === "") continue;
|
|
14927
15827
|
const parsed = parseJsonObject(line2);
|
|
@@ -14929,13 +15829,13 @@ var parseTranscript = (jsonl) => {
|
|
|
14929
15829
|
const entry2 = parsed.value;
|
|
14930
15830
|
const blocks = blocksOf(entry2["message"]);
|
|
14931
15831
|
if (entry2["type"] === "user") {
|
|
14932
|
-
if (
|
|
15832
|
+
if (current2 !== void 0 && addResults(current2, blocks)) continue;
|
|
14933
15833
|
const prompt = blocks.map(textOf3).join("\n").trim();
|
|
14934
15834
|
if (prompt === "") continue;
|
|
14935
|
-
|
|
14936
|
-
turns.push(
|
|
14937
|
-
} else if (entry2["type"] === "assistant" &&
|
|
14938
|
-
addAssistant(
|
|
15835
|
+
current2 = startTurn(prompt);
|
|
15836
|
+
turns.push(current2);
|
|
15837
|
+
} else if (entry2["type"] === "assistant" && current2 !== void 0) {
|
|
15838
|
+
addAssistant(current2, blocks);
|
|
14939
15839
|
}
|
|
14940
15840
|
}
|
|
14941
15841
|
return turns;
|
|
@@ -14944,7 +15844,7 @@ var parseTranscript = (jsonl) => {
|
|
|
14944
15844
|
// src/history/look-back.ts
|
|
14945
15845
|
var SAMPLES = 3;
|
|
14946
15846
|
var projectLogKey = (root) => root.replace(/[^A-Za-z0-9-]/g, "-");
|
|
14947
|
-
var projectLogDir = (runtime, root, env) =>
|
|
15847
|
+
var projectLogDir = (runtime, root, env) => join38(env["CLAUDE_CONFIG_DIR"] ?? join38(runtime.homeDir(), ".claude"), "projects", projectLogKey(root));
|
|
14948
15848
|
var lookBack = (runtime, root, env) => {
|
|
14949
15849
|
const dir = projectLogDir(runtime, root, env);
|
|
14950
15850
|
const listed4 = runtime.listDir(toAbsolutePath(dir));
|
|
@@ -14965,7 +15865,7 @@ var lookBack = (runtime, root, env) => {
|
|
|
14965
15865
|
};
|
|
14966
15866
|
let turns = 0;
|
|
14967
15867
|
for (const name of files) {
|
|
14968
|
-
const text6 = runtime.readText(toAbsolutePath(
|
|
15868
|
+
const text6 = runtime.readText(toAbsolutePath(join38(dir, name)));
|
|
14969
15869
|
if (!text6.ok) continue;
|
|
14970
15870
|
for (const turn of parseTranscript(text6.value)) {
|
|
14971
15871
|
turns += 1;
|
|
@@ -15146,11 +16046,11 @@ var runInstall = (ctx, request, presetFallback) => {
|
|
|
15146
16046
|
if (ctx.dryRun || !ctx.yes || !needsWrite(plan.value)) return presentPlan(ctx, request, plan.value);
|
|
15147
16047
|
return applyAndReport(ctx, request, plan.value);
|
|
15148
16048
|
};
|
|
15149
|
-
var initRequest = (
|
|
16049
|
+
var initRequest = (args2) => ({
|
|
15150
16050
|
mode: "init",
|
|
15151
|
-
preset: stringOption(
|
|
15152
|
-
bin: stringOption(
|
|
15153
|
-
marketplace:
|
|
16051
|
+
preset: stringOption(args2.values, "preset"),
|
|
16052
|
+
bin: stringOption(args2.values, "bin"),
|
|
16053
|
+
marketplace: args2.values["marketplace"] === true,
|
|
15154
16054
|
showDiff: false
|
|
15155
16055
|
});
|
|
15156
16056
|
var withLookBack = (ctx, result) => {
|
|
@@ -15175,10 +16075,36 @@ var initCommand = {
|
|
|
15175
16075
|
marketplace: { type: "boolean" },
|
|
15176
16076
|
"look-back": { type: "boolean" }
|
|
15177
16077
|
},
|
|
15178
|
-
run: (ctx,
|
|
15179
|
-
const result = runInstall(ctx, initRequest(
|
|
15180
|
-
return Promise.resolve(
|
|
15181
|
-
}
|
|
16078
|
+
run: (ctx, args2) => {
|
|
16079
|
+
const result = runInstall(ctx, initRequest(args2), "neutral");
|
|
16080
|
+
return Promise.resolve(args2.values["look-back"] === true ? withLookBack(ctx, result) : result);
|
|
16081
|
+
}
|
|
16082
|
+
};
|
|
16083
|
+
|
|
16084
|
+
// src/commands/policy.ts
|
|
16085
|
+
var FIX = `finitemonkey policy false-positive <${OPTIONAL_GUARDS.join("|")}>`;
|
|
16086
|
+
var recordFalsePositive = (ctx, guard) => {
|
|
16087
|
+
if (!isPerson(ctx)) return failureFrom(ctx.t, needsPersonProblem("policy.error.person_only", FIX));
|
|
16088
|
+
if (!OPTIONAL_GUARDS.some((id) => id === guard)) {
|
|
16089
|
+
return failureFrom(ctx.t, usageProblem("invalid_arguments", "error.invalid_arguments", FIX, { detail: guard }));
|
|
16090
|
+
}
|
|
16091
|
+
const paths = fmPaths(findRepoRoot(ctx.runtime, ctx.cwd) ?? ctx.cwd);
|
|
16092
|
+
const state = readShadow(ctx.runtime, paths);
|
|
16093
|
+
const before = state[guard] ?? { firings: 0, falsePositives: 0, promoted: false };
|
|
16094
|
+
const falsePositives = before.falsePositives + 1;
|
|
16095
|
+
writeShadow(ctx.runtime, paths, { ...state, [guard]: { ...before, falsePositives } });
|
|
16096
|
+
logEvent(ctx.runtime, paths, "guard_false_positive", { guard, falsePositives });
|
|
16097
|
+
return success({ guard, falsePositives }, [
|
|
16098
|
+
ctx.t("policy.false_positive.recorded", { guard, count: falsePositives })
|
|
16099
|
+
]);
|
|
16100
|
+
};
|
|
16101
|
+
var policyFalsePositiveCommand = {
|
|
16102
|
+
path: ["policy", "false-positive"],
|
|
16103
|
+
summaryKey: "command.policy_false_positive.summary",
|
|
16104
|
+
usage: FIX,
|
|
16105
|
+
status: "ready",
|
|
16106
|
+
maxPositionals: 1,
|
|
16107
|
+
run: (ctx, args2) => Promise.resolve(recordFalsePositive(ctx, args2.positionals[0] ?? ""))
|
|
15182
16108
|
};
|
|
15183
16109
|
|
|
15184
16110
|
// src/commands/prose-check.ts
|
|
@@ -15304,10 +16230,10 @@ var proseCheckCommand = {
|
|
|
15304
16230
|
status: "ready",
|
|
15305
16231
|
options: { stdin: { type: "boolean" }, record: { type: "boolean" } },
|
|
15306
16232
|
maxPositionals: 1e3,
|
|
15307
|
-
run: async (ctx,
|
|
16233
|
+
run: async (ctx, args2) => {
|
|
15308
16234
|
const root = projectRoot(ctx);
|
|
15309
|
-
const mode = { stdin:
|
|
15310
|
-
const reports = await reportsOf(ctx, root,
|
|
16235
|
+
const mode = { stdin: args2.values["stdin"] === true, record: args2.values["record"] === true };
|
|
16236
|
+
const reports = await reportsOf(ctx, root, args2.positionals, mode);
|
|
15311
16237
|
if (!reports.ok) return failureFrom(ctx.t, reports.error);
|
|
15312
16238
|
return resultOf2(ctx, reports.value);
|
|
15313
16239
|
}
|
|
@@ -15362,7 +16288,7 @@ var inputProblem = (ctx, id, reason, days) => {
|
|
|
15362
16288
|
}
|
|
15363
16289
|
return void 0;
|
|
15364
16290
|
};
|
|
15365
|
-
var entryOf3 = (ctx, finding3,
|
|
16291
|
+
var entryOf3 = (ctx, finding3, args2) => {
|
|
15366
16292
|
const now = ctx.runtime.now();
|
|
15367
16293
|
return {
|
|
15368
16294
|
id: finding3.id,
|
|
@@ -15370,30 +16296,30 @@ var entryOf3 = (ctx, finding3, args) => {
|
|
|
15370
16296
|
file: finding3.file,
|
|
15371
16297
|
line: finding3.line,
|
|
15372
16298
|
fingerprint: finding3.fingerprint,
|
|
15373
|
-
state:
|
|
16299
|
+
state: args2.falsePositive ? "falso-positivo" : "aceito",
|
|
15374
16300
|
owner: personName(ctx),
|
|
15375
|
-
reason:
|
|
16301
|
+
reason: args2.reason,
|
|
15376
16302
|
acceptedAt: now,
|
|
15377
|
-
expiresAt: plusDays(now,
|
|
16303
|
+
expiresAt: plusDays(now, args2.days)
|
|
15378
16304
|
};
|
|
15379
16305
|
};
|
|
15380
|
-
var acceptEntry = (ctx,
|
|
16306
|
+
var acceptEntry = (ctx, args2) => {
|
|
15381
16307
|
const root = projectRoot(ctx);
|
|
15382
16308
|
const baseline = loadBaseline(ctx.runtime, root);
|
|
15383
16309
|
if (!baseline.ok) {
|
|
15384
16310
|
return failure3(ctx, baselineInvalid(baseline.error));
|
|
15385
16311
|
}
|
|
15386
16312
|
const scanned = scanPath(ctx.runtime, root, ".");
|
|
15387
|
-
const finding3 = scanned.ok ? scanned.value.find((item) => item.id ===
|
|
16313
|
+
const finding3 = scanned.ok ? scanned.value.find((item) => item.id === args2.id) : void 0;
|
|
15388
16314
|
if (finding3 === void 0) {
|
|
15389
16315
|
return failure3(
|
|
15390
16316
|
ctx,
|
|
15391
16317
|
usageProblem("secrets_finding_not_found", "secrets.error.finding_not_found", "finitemonkey secrets scan", {
|
|
15392
|
-
id:
|
|
16318
|
+
id: args2.id
|
|
15393
16319
|
})
|
|
15394
16320
|
);
|
|
15395
16321
|
}
|
|
15396
|
-
const entry2 = entryOf3(ctx, finding3,
|
|
16322
|
+
const entry2 = entryOf3(ctx, finding3, args2);
|
|
15397
16323
|
const entries = [...baseline.value.entries.filter((item) => item.id !== entry2.id), entry2];
|
|
15398
16324
|
const saved = saveBaseline(ctx.runtime, root, { schema: baseline.value.schema, entries });
|
|
15399
16325
|
if (!saved.ok) {
|
|
@@ -15416,10 +16342,10 @@ var secretsAcceptCommand = {
|
|
|
15416
16342
|
status: "ready",
|
|
15417
16343
|
maxPositionals: 1,
|
|
15418
16344
|
options: { reason: { type: "string" }, days: { type: "string" }, "false-positive": { type: "boolean" } },
|
|
15419
|
-
run: (ctx,
|
|
15420
|
-
const id =
|
|
15421
|
-
const reason = stringOption(
|
|
15422
|
-
const days = daysOf(
|
|
16345
|
+
run: (ctx, args2) => {
|
|
16346
|
+
const id = args2.positionals[0] ?? "";
|
|
16347
|
+
const reason = stringOption(args2.values, "reason");
|
|
16348
|
+
const days = daysOf(args2.values);
|
|
15423
16349
|
const problem4 = inputProblem(ctx, id, reason, days);
|
|
15424
16350
|
if (problem4 !== void 0 || reason === void 0 || days === void 0) {
|
|
15425
16351
|
return Promise.resolve(
|
|
@@ -15430,7 +16356,7 @@ var secretsAcceptCommand = {
|
|
|
15430
16356
|
);
|
|
15431
16357
|
}
|
|
15432
16358
|
return Promise.resolve(
|
|
15433
|
-
acceptEntry(ctx, { id, reason, days, falsePositive:
|
|
16359
|
+
acceptEntry(ctx, { id, reason, days, falsePositive: args2.values["false-positive"] === true })
|
|
15434
16360
|
);
|
|
15435
16361
|
}
|
|
15436
16362
|
};
|
|
@@ -15511,18 +16437,18 @@ var secretsIncidentCommand = {
|
|
|
15511
16437
|
usage: USAGE,
|
|
15512
16438
|
status: "ready",
|
|
15513
16439
|
maxPositionals: 2,
|
|
15514
|
-
run: (ctx,
|
|
15515
|
-
const action = actionOf(
|
|
16440
|
+
run: (ctx, args2) => {
|
|
16441
|
+
const action = actionOf(args2.positionals[0]);
|
|
15516
16442
|
const root = projectRoot(ctx);
|
|
15517
16443
|
if (action === void 0)
|
|
15518
16444
|
return Promise.resolve(
|
|
15519
16445
|
fail2(
|
|
15520
16446
|
ctx,
|
|
15521
|
-
usageProblem("invalid_arguments", "error.invalid_arguments", USAGE, { detail:
|
|
16447
|
+
usageProblem("invalid_arguments", "error.invalid_arguments", USAGE, { detail: args2.positionals[0] ?? "" })
|
|
15522
16448
|
)
|
|
15523
16449
|
);
|
|
15524
16450
|
if (action === "list") return Promise.resolve(list2(ctx, root));
|
|
15525
|
-
const id =
|
|
16451
|
+
const id = args2.positionals[1] ?? "";
|
|
15526
16452
|
const incident = readIncident(ctx.runtime, root, id);
|
|
15527
16453
|
if (!incident.ok) return Promise.resolve(notFound2(ctx, id));
|
|
15528
16454
|
return Promise.resolve(action === "purge" ? purge(ctx, incident.value) : move(ctx, root, incident.value, action));
|
|
@@ -15543,7 +16469,7 @@ var view = (finding3, status) => ({
|
|
|
15543
16469
|
status,
|
|
15544
16470
|
...finding3.commit === void 0 ? {} : { commit: finding3.commit }
|
|
15545
16471
|
});
|
|
15546
|
-
var
|
|
16472
|
+
var modeOf3 = (values) => {
|
|
15547
16473
|
const chosen = [values["staged"] === true, values["history"] === true, stringOption(values, "path") !== void 0];
|
|
15548
16474
|
if (chosen.filter(Boolean).length > 1) return void 0;
|
|
15549
16475
|
if (chosen[0] === true) return "staged";
|
|
@@ -15572,25 +16498,25 @@ var historyIncidents = (ctx, root, fresh3) => fresh3.flatMap((finding3) => {
|
|
|
15572
16498
|
const saved = recordIncident(ctx.runtime, root, draft, HOOK_ACTOR);
|
|
15573
16499
|
return saved.ok ? [saved.value.id] : [];
|
|
15574
16500
|
});
|
|
15575
|
-
var dataOf = (mode,
|
|
16501
|
+
var dataOf = (mode, outcome2, incidents) => {
|
|
15576
16502
|
const findings = [
|
|
15577
|
-
...
|
|
15578
|
-
...
|
|
15579
|
-
...
|
|
16503
|
+
...outcome2.fresh.map((finding3) => view(finding3, "new")),
|
|
16504
|
+
...outcome2.known.map((finding3) => view(finding3, "known")),
|
|
16505
|
+
...outcome2.expired.map((finding3) => view(finding3, "expired"))
|
|
15580
16506
|
];
|
|
15581
16507
|
return {
|
|
15582
16508
|
schema: SECRETS_SCAN_SCHEMA,
|
|
15583
16509
|
mode,
|
|
15584
16510
|
baseline: BASELINE_FILE2,
|
|
15585
16511
|
findings,
|
|
15586
|
-
fixed:
|
|
16512
|
+
fixed: outcome2.fixed.map((entry2) => entry2.id),
|
|
15587
16513
|
incidents,
|
|
15588
16514
|
summary: {
|
|
15589
16515
|
total: findings.length,
|
|
15590
|
-
new:
|
|
15591
|
-
known:
|
|
15592
|
-
expired:
|
|
15593
|
-
fixed:
|
|
16516
|
+
new: outcome2.fresh.length,
|
|
16517
|
+
known: outcome2.known.length,
|
|
16518
|
+
expired: outcome2.expired.length,
|
|
16519
|
+
fixed: outcome2.fixed.length
|
|
15594
16520
|
}
|
|
15595
16521
|
};
|
|
15596
16522
|
};
|
|
@@ -15624,8 +16550,8 @@ var resultOf3 = (ctx, data) => {
|
|
|
15624
16550
|
human2
|
|
15625
16551
|
);
|
|
15626
16552
|
};
|
|
15627
|
-
var prune = (ctx, root, baseline,
|
|
15628
|
-
if (
|
|
16553
|
+
var prune = (ctx, root, baseline, outcome2) => {
|
|
16554
|
+
if (outcome2.fixed.length > 0) saveBaseline(ctx.runtime, root, withoutFixed(baseline, outcome2.fixed));
|
|
15629
16555
|
};
|
|
15630
16556
|
var secretsScanCommand = {
|
|
15631
16557
|
path: ["secrets", "scan"],
|
|
@@ -15633,30 +16559,30 @@ var secretsScanCommand = {
|
|
|
15633
16559
|
usage: "finitemonkey secrets scan [--staged | --history | --path <dir>] [--json]",
|
|
15634
16560
|
status: "ready",
|
|
15635
16561
|
options: { staged: { type: "boolean" }, history: { type: "boolean" }, path: { type: "string" } },
|
|
15636
|
-
run: async (ctx,
|
|
15637
|
-
const mode =
|
|
16562
|
+
run: async (ctx, args2) => {
|
|
16563
|
+
const mode = modeOf3(args2.values);
|
|
15638
16564
|
const fix = "finitemonkey help secrets scan";
|
|
15639
16565
|
if (mode === void 0) return failureFrom(ctx.t, usageProblem("invalid_arguments", "secrets.error.one_mode", fix));
|
|
15640
16566
|
const root = projectRoot(ctx);
|
|
15641
|
-
const target = stringOption(
|
|
16567
|
+
const target = stringOption(args2.values, "path") ?? ".";
|
|
15642
16568
|
const scanned = await runScan(ctx, root, mode, target);
|
|
15643
16569
|
if (!scanned.ok) return failureFrom(ctx.t, gitUnavailable(scanned.error));
|
|
15644
16570
|
const baseline = loadBaseline(ctx.runtime, root);
|
|
15645
16571
|
if (!baseline.ok) return failureFrom(ctx.t, baselineInvalid(baseline.error));
|
|
15646
|
-
const
|
|
16572
|
+
const outcome2 = ratchet(
|
|
15647
16573
|
scanned.value,
|
|
15648
16574
|
baseline.value,
|
|
15649
16575
|
ctx.runtime.now(),
|
|
15650
16576
|
mode === "path" ? scopeOf(target) : void 0
|
|
15651
16577
|
);
|
|
15652
|
-
if (mode === "path") prune(ctx, root, baseline.value,
|
|
15653
|
-
const incidents = mode === "history" ? historyIncidents(ctx, root,
|
|
15654
|
-
return resultOf3(ctx, dataOf(mode,
|
|
16578
|
+
if (mode === "path") prune(ctx, root, baseline.value, outcome2);
|
|
16579
|
+
const incidents = mode === "history" ? historyIncidents(ctx, root, outcome2.fresh) : [];
|
|
16580
|
+
return resultOf3(ctx, dataOf(mode, outcome2, incidents));
|
|
15655
16581
|
}
|
|
15656
16582
|
};
|
|
15657
16583
|
|
|
15658
16584
|
// src/task/create.ts
|
|
15659
|
-
import { join as
|
|
16585
|
+
import { join as join39 } from "node:path";
|
|
15660
16586
|
|
|
15661
16587
|
// src/core/yaml/write.ts
|
|
15662
16588
|
var PLAIN_SAFE = /^[A-Za-z_./][A-Za-z0-9_./ ()-]*$/;
|
|
@@ -15702,7 +16628,11 @@ var checkYaml = (check2) => ({
|
|
|
15702
16628
|
...check2.timeoutS === void 0 ? {} : { timeout_s: check2.timeoutS },
|
|
15703
16629
|
...check2.require === void 0 ? {} : { require: [...check2.require] },
|
|
15704
16630
|
...check2.requireOpen === void 0 ? {} : { require_open: check2.requireOpen },
|
|
15705
|
-
...check2.description === void 0 ? {} : { description: check2.description }
|
|
16631
|
+
...check2.description === void 0 ? {} : { description: check2.description },
|
|
16632
|
+
...check2.port === void 0 ? {} : { port: check2.port },
|
|
16633
|
+
...check2.route === void 0 ? {} : { route: check2.route },
|
|
16634
|
+
...check2.status === void 0 ? {} : { status: check2.status },
|
|
16635
|
+
...check2.body === void 0 ? {} : { body: check2.body }
|
|
15706
16636
|
});
|
|
15707
16637
|
var contractYaml = (contract) => ({
|
|
15708
16638
|
id: contract.id,
|
|
@@ -15715,7 +16645,8 @@ var contractYaml = (contract) => ({
|
|
|
15715
16645
|
tests_protected: contract.scope.testsProtected
|
|
15716
16646
|
},
|
|
15717
16647
|
acceptance: contract.acceptance.map(checkYaml),
|
|
15718
|
-
decisions_needed: [...contract.decisionsNeeded]
|
|
16648
|
+
decisions_needed: [...contract.decisionsNeeded],
|
|
16649
|
+
...contract.callersIgnore === void 0 ? {} : { callers_ignore: [...contract.callersIgnore] }
|
|
15719
16650
|
});
|
|
15720
16651
|
var contractJson = (contract) => ({
|
|
15721
16652
|
id: contract.id,
|
|
@@ -15740,10 +16671,10 @@ var explicitChecks = (draft) => [
|
|
|
15740
16671
|
var draftChecks = (runtime, paths, draft) => {
|
|
15741
16672
|
const disk = {
|
|
15742
16673
|
read: (relative9) => {
|
|
15743
|
-
const text6 = runtime.readText(toAbsolutePath(
|
|
16674
|
+
const text6 = runtime.readText(toAbsolutePath(join39(paths.root, relative9)));
|
|
15744
16675
|
return text6.ok ? text6.value : void 0;
|
|
15745
16676
|
},
|
|
15746
|
-
exists: (relative9) => runtime.stat(toAbsolutePath(
|
|
16677
|
+
exists: (relative9) => runtime.stat(toAbsolutePath(join39(paths.root, relative9))) !== void 0
|
|
15747
16678
|
};
|
|
15748
16679
|
const detected = detectChecks(disk);
|
|
15749
16680
|
const base = draft.runs.length > 0 || draft.tests !== void 0 ? explicitChecks(draft) : detected;
|
|
@@ -15756,6 +16687,7 @@ var draftChecks = (runtime, paths, draft) => {
|
|
|
15756
16687
|
{ id: "prose", kind: "prose" },
|
|
15757
16688
|
{ id: "worktree", kind: "worktree" },
|
|
15758
16689
|
{ id: "merge", kind: "merge" },
|
|
16690
|
+
{ id: "callers", kind: "callers" },
|
|
15759
16691
|
...draft.paths.length > 0 || draft.budget !== void 0 ? [{ id: "diff", kind: "diff" }] : [],
|
|
15760
16692
|
...draft.git.length > 0 ? [{ id: "git", kind: "git", require: draft.git }] : [],
|
|
15761
16693
|
{ id: "pr", kind: "pr", requireOpen: draft.git.includes("pr_open") },
|
|
@@ -15816,13 +16748,13 @@ var list3 = (values, name) => {
|
|
|
15816
16748
|
return raw.filter((item) => typeof item === "string" && item.trim() !== "");
|
|
15817
16749
|
};
|
|
15818
16750
|
var csv = (values, name) => list3(values, name).flatMap((item) => item.split(",").map((part2) => part2.trim())).filter((part2) => part2 !== "");
|
|
15819
|
-
var
|
|
16751
|
+
var positive3 = (values, name) => {
|
|
15820
16752
|
const parsed = Number.parseInt(stringOption(values, name) ?? "", 10);
|
|
15821
16753
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
|
|
15822
16754
|
};
|
|
15823
16755
|
var draftFrom = (ctx, paths, id, values) => {
|
|
15824
|
-
const files =
|
|
15825
|
-
const lines3 =
|
|
16756
|
+
const files = positive3(values, "budget-files");
|
|
16757
|
+
const lines3 = positive3(values, "budget-lines");
|
|
15826
16758
|
const size = SIZES.find((item) => item === stringOption(values, "size")) ?? "S";
|
|
15827
16759
|
return {
|
|
15828
16760
|
id,
|
|
@@ -15925,7 +16857,7 @@ var taskNewCommand = {
|
|
|
15925
16857
|
status: "ready",
|
|
15926
16858
|
options: TASK_OPTIONS,
|
|
15927
16859
|
maxPositionals: 1,
|
|
15928
|
-
run: (ctx,
|
|
16860
|
+
run: (ctx, args2) => Promise.resolve(newTask(ctx, args2.positionals[0], args2.values))
|
|
15929
16861
|
};
|
|
15930
16862
|
var taskShowCommand = {
|
|
15931
16863
|
path: ["task", "show"],
|
|
@@ -15933,7 +16865,7 @@ var taskShowCommand = {
|
|
|
15933
16865
|
usage: "finitemonkey task show [<id>]",
|
|
15934
16866
|
status: "ready",
|
|
15935
16867
|
maxPositionals: 1,
|
|
15936
|
-
run: (ctx,
|
|
16868
|
+
run: (ctx, args2) => Promise.resolve(showTask(ctx, args2.positionals[0]))
|
|
15937
16869
|
};
|
|
15938
16870
|
var taskDoneCommand = {
|
|
15939
16871
|
path: ["task", "done"],
|
|
@@ -15941,19 +16873,19 @@ var taskDoneCommand = {
|
|
|
15941
16873
|
usage: "finitemonkey task done [<id>]",
|
|
15942
16874
|
status: "ready",
|
|
15943
16875
|
maxPositionals: 1,
|
|
15944
|
-
run: (ctx,
|
|
16876
|
+
run: (ctx, args2) => Promise.resolve(doneTask(ctx, args2.positionals[0]))
|
|
15945
16877
|
};
|
|
15946
16878
|
|
|
15947
16879
|
// src/install/uninstall.ts
|
|
15948
|
-
var structural = (entry2,
|
|
16880
|
+
var structural = (entry2, current2) => {
|
|
15949
16881
|
if (entry2.kind === "gitignore" || entry2.kind === "markdown") {
|
|
15950
|
-
const text6 = entry2.kind === "gitignore" ? withoutBlock(
|
|
16882
|
+
const text6 = entry2.kind === "gitignore" ? withoutBlock(current2) : withoutMarkdownBlock(current2);
|
|
15951
16883
|
return text6 === "" && !entry2.existed ? void 0 : text6;
|
|
15952
16884
|
}
|
|
15953
|
-
const parsed = parseSettings(
|
|
15954
|
-
if (!parsed.ok || entry2.added === void 0) return
|
|
16885
|
+
const parsed = parseSettings(current2);
|
|
16886
|
+
if (!parsed.ok || entry2.added === void 0) return current2;
|
|
15955
16887
|
const settings = unmergeSettings(parsed.value, entry2.added, entry2.handlers ?? []);
|
|
15956
|
-
return Object.keys(settings).length === 0 && !entry2.existed ? void 0 : renderJson(settings, styleOf(
|
|
16888
|
+
return Object.keys(settings).length === 0 && !entry2.existed ? void 0 : renderJson(settings, styleOf(current2));
|
|
15957
16889
|
};
|
|
15958
16890
|
var unchanged = (project, entry2) => {
|
|
15959
16891
|
if (!entry2.existed) return { path: entry2.path, action: "delete", after: void 0 };
|
|
@@ -15961,17 +16893,17 @@ var unchanged = (project, entry2) => {
|
|
|
15961
16893
|
return original === void 0 ? void 0 : { path: entry2.path, action: "restore", after: original };
|
|
15962
16894
|
};
|
|
15963
16895
|
var removalFor = (project, entry2, options) => {
|
|
15964
|
-
const
|
|
15965
|
-
if (
|
|
16896
|
+
const current2 = project.read(entry2.path);
|
|
16897
|
+
if (current2 === void 0) return { path: entry2.path, action: "gone", after: void 0 };
|
|
15966
16898
|
if (entry2.kind === "generated" && options.keepGenerated) {
|
|
15967
|
-
return { path: entry2.path, action: "keep-generated", after:
|
|
16899
|
+
return { path: entry2.path, action: "keep-generated", after: current2 };
|
|
15968
16900
|
}
|
|
15969
|
-
if (hashText(
|
|
16901
|
+
if (hashText(current2) === entry2.hash) {
|
|
15970
16902
|
const exact2 = unchanged(project, entry2);
|
|
15971
16903
|
if (exact2 !== void 0) return exact2;
|
|
15972
16904
|
}
|
|
15973
|
-
if (entry2.kind === "generated") return { path: entry2.path, action: "keep-edited", after:
|
|
15974
|
-
const after = structural(entry2,
|
|
16905
|
+
if (entry2.kind === "generated") return { path: entry2.path, action: "keep-edited", after: current2 };
|
|
16906
|
+
const after = structural(entry2, current2);
|
|
15975
16907
|
return { path: entry2.path, action: after === void 0 ? "delete" : "unmerge", after };
|
|
15976
16908
|
};
|
|
15977
16909
|
var planRemoval = (project, lock, options) => ({
|
|
@@ -15991,7 +16923,7 @@ var isEmptyDir = (project, relative9) => {
|
|
|
15991
16923
|
const listed4 = project.runtime.listDir(project.abs(relative9));
|
|
15992
16924
|
return listed4.ok && listed4.value.length === 0;
|
|
15993
16925
|
};
|
|
15994
|
-
var
|
|
16926
|
+
var finish3 = (project, plan) => {
|
|
15995
16927
|
const target = plan.purge ? STATE_DIR : LOCK_FILE;
|
|
15996
16928
|
const removed = project.runtime.remove(project.abs(target));
|
|
15997
16929
|
if (!removed.ok) return reject({ path: target, detail: removed.error });
|
|
@@ -16006,7 +16938,7 @@ var applyRemoval = (project, plan) => {
|
|
|
16006
16938
|
}
|
|
16007
16939
|
return accept(void 0);
|
|
16008
16940
|
});
|
|
16009
|
-
return changed.ok ?
|
|
16941
|
+
return changed.ok ? finish3(project, plan) : changed;
|
|
16010
16942
|
};
|
|
16011
16943
|
|
|
16012
16944
|
// src/commands/uninstall.ts
|
|
@@ -16037,22 +16969,22 @@ var linesOf2 = (ctx, data) => [
|
|
|
16037
16969
|
ctx.t("install.uninstall.outside"),
|
|
16038
16970
|
...OUTSIDE.map(({ key, command }) => ` ${ctx.t(key, { command })}`)
|
|
16039
16971
|
];
|
|
16040
|
-
var fixFor = (
|
|
16972
|
+
var fixFor = (args2) => [
|
|
16041
16973
|
"finitemonkey uninstall --yes",
|
|
16042
|
-
...
|
|
16043
|
-
...
|
|
16974
|
+
...args2.values["purge"] === true ? ["--purge"] : [],
|
|
16975
|
+
...args2.values["keep-generated"] === true ? ["--keep-generated"] : []
|
|
16044
16976
|
].join(" ");
|
|
16045
|
-
var runUninstall = (ctx,
|
|
16977
|
+
var runUninstall = (ctx, args2) => {
|
|
16046
16978
|
const { project } = openProject(ctx);
|
|
16047
16979
|
const lock = readPreviousLock(project);
|
|
16048
16980
|
if (!lock.ok) return failureFrom(ctx.t, planProblem(lock.error));
|
|
16049
16981
|
if (lock.value === void 0) return failureFrom(ctx.t, notInstalled());
|
|
16050
|
-
const options = { purge:
|
|
16982
|
+
const options = { purge: args2.values["purge"] === true, keepGenerated: args2.values["keep-generated"] === true };
|
|
16051
16983
|
const plan = planRemoval(project, lock.value, options);
|
|
16052
16984
|
const preview = dataOf2(project.root, plan, false);
|
|
16053
16985
|
if (ctx.dryRun) return success(preview, [...linesOf2(ctx, preview), ctx.t("install.dry_run")]);
|
|
16054
16986
|
if (!ctx.yes) {
|
|
16055
|
-
const next2 = fixFor(
|
|
16987
|
+
const next2 = fixFor(args2);
|
|
16056
16988
|
const problem4 = confirmProblem("uninstall", ctx.t("install.uninstall.question"), next2);
|
|
16057
16989
|
return { ...failureFrom(ctx.t, { ...problem4, data: preview }, linesOf2(ctx, preview)), next: next2 };
|
|
16058
16990
|
}
|
|
@@ -16067,7 +16999,7 @@ var uninstallCommand = {
|
|
|
16067
16999
|
usage: "finitemonkey uninstall [--purge] [--keep-generated] [--dry-run] [--yes]",
|
|
16068
17000
|
status: "ready",
|
|
16069
17001
|
options: { purge: { type: "boolean" }, "keep-generated": { type: "boolean" } },
|
|
16070
|
-
run: (ctx,
|
|
17002
|
+
run: (ctx, args2) => Promise.resolve(runUninstall(ctx, args2))
|
|
16071
17003
|
};
|
|
16072
17004
|
|
|
16073
17005
|
// src/commands/update.ts
|
|
@@ -16090,14 +17022,14 @@ var check = (ctx, bin) => {
|
|
|
16090
17022
|
error: { code: "install_outdated", message: ctx.t("error.install_outdated"), fix: "finitemonkey update --yes" }
|
|
16091
17023
|
};
|
|
16092
17024
|
};
|
|
16093
|
-
var runUpdate = (ctx,
|
|
16094
|
-
const bin = stringOption(
|
|
16095
|
-
if (
|
|
17025
|
+
var runUpdate = (ctx, args2) => {
|
|
17026
|
+
const bin = stringOption(args2.values, "bin");
|
|
17027
|
+
if (args2.values["check"] === true) return check(ctx, bin);
|
|
16096
17028
|
const { project } = openProject(ctx);
|
|
16097
17029
|
const previous = readPreviousLock(project);
|
|
16098
17030
|
if (!previous.ok) return failureFrom(ctx.t, planProblem(previous.error));
|
|
16099
17031
|
if (previous.value === void 0) return failureFrom(ctx.t, notInstalled());
|
|
16100
|
-
const preset = stringOption(
|
|
17032
|
+
const preset = stringOption(args2.values, "preset");
|
|
16101
17033
|
const request = { mode: "update", preset, bin, marketplace: false, showDiff: true };
|
|
16102
17034
|
return runInstall(ctx, request, previous.value.preset);
|
|
16103
17035
|
};
|
|
@@ -16107,7 +17039,7 @@ var updateCommand = {
|
|
|
16107
17039
|
usage: "finitemonkey update [--check] [--preset neutral|gabriel] [--bin <path>] [--yes]",
|
|
16108
17040
|
status: "ready",
|
|
16109
17041
|
options: { check: { type: "boolean" }, preset: { type: "string" }, bin: { type: "string" } },
|
|
16110
|
-
run: (ctx,
|
|
17042
|
+
run: (ctx, args2) => Promise.resolve(runUpdate(ctx, args2))
|
|
16111
17043
|
};
|
|
16112
17044
|
|
|
16113
17045
|
// src/commands/version.ts
|
|
@@ -16136,18 +17068,20 @@ var REGISTRY = [
|
|
|
16136
17068
|
taskDoneCommand,
|
|
16137
17069
|
secretsScanCommand,
|
|
16138
17070
|
secretsAcceptCommand,
|
|
17071
|
+
policyFalsePositiveCommand,
|
|
16139
17072
|
secretsIncidentCommand,
|
|
16140
17073
|
proseCheckCommand,
|
|
16141
17074
|
snapshotCommand,
|
|
16142
17075
|
undoCommand,
|
|
16143
17076
|
rescueCommand,
|
|
16144
17077
|
mergeVerifyCommand,
|
|
17078
|
+
appCheckCommand,
|
|
16145
17079
|
versionCommand,
|
|
16146
17080
|
helpCommand
|
|
16147
17081
|
];
|
|
16148
17082
|
|
|
16149
17083
|
// src/i18n/lang-settings.ts
|
|
16150
|
-
import { join as
|
|
17084
|
+
import { join as join40 } from "node:path";
|
|
16151
17085
|
var read3 = (runtime, path) => {
|
|
16152
17086
|
const text6 = runtime.readText(toAbsolutePath(path));
|
|
16153
17087
|
return text6.ok ? text6.value : void 0;
|
|
@@ -16162,14 +17096,14 @@ var languageIn = (text6) => {
|
|
|
16162
17096
|
const value = parsed.ok && isRecord(parsed.value) ? parsed.value["language"] : void 0;
|
|
16163
17097
|
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
16164
17098
|
};
|
|
16165
|
-
var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ??
|
|
17099
|
+
var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ?? join40(runtime.homeDir(), ".claude");
|
|
16166
17100
|
var langSettings = (runtime, cwd, env, platform) => {
|
|
16167
17101
|
const root = findGitRoot(cwd, runtime) ?? cwd;
|
|
16168
|
-
const config = parseConfigYaml(read3(runtime,
|
|
17102
|
+
const config = parseConfigYaml(read3(runtime, join40(root, CONFIG_FILE)) ?? "");
|
|
16169
17103
|
const claudeFiles = [
|
|
16170
|
-
|
|
16171
|
-
|
|
16172
|
-
|
|
17104
|
+
join40(root, ".claude", "settings.local.json"),
|
|
17105
|
+
join40(root, ".claude", "settings.json"),
|
|
17106
|
+
join40(claudeDir(runtime, env), "settings.json")
|
|
16173
17107
|
];
|
|
16174
17108
|
return {
|
|
16175
17109
|
config: configText(config, "lang"),
|
|
@@ -16220,9 +17154,9 @@ var failWith = (ctx, name, problem4) => ({
|
|
|
16220
17154
|
});
|
|
16221
17155
|
var invoke = (ctx, argv, spec2, registry) => {
|
|
16222
17156
|
const name = commandName(spec2);
|
|
16223
|
-
const
|
|
16224
|
-
if (!
|
|
16225
|
-
return { name, execute: () => spec2.run(ctx,
|
|
17157
|
+
const args2 = strictArgs(argv, spec2);
|
|
17158
|
+
if (!args2.ok) return failWith(ctx, name, args2.error);
|
|
17159
|
+
return { name, execute: () => spec2.run(ctx, args2.value, registry) };
|
|
16226
17160
|
};
|
|
16227
17161
|
var builtin = (registry, name) => registry.find((spec2) => commandName(spec2) === name);
|
|
16228
17162
|
var dispatch = (ctx, argv, loose, registry) => {
|