finitemonkey 0.1.0-next.16 → 0.1.0-next.18

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.
@@ -69,7 +69,7 @@ var resolveWindowsCommand = (command, env, exists) => {
69
69
  }
70
70
  return { command, shell: false };
71
71
  };
72
- var windowsCommandLine = (resolved, args) => [quoteForCmd(resolved.command), ...args.map(quoteForCmd)].join(" ");
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) => (args, cwd) => {
120
+ var nodeSpawnSelf = (selfPath) => (args2, cwd) => {
121
121
  try {
122
- const child2 = spawn(process.execPath, [selfPath, ...args], {
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 = (args, cwd) => {
294
- const result = spawnSync2("git", [...args], {
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 = (args, cwd, onLine) => new Promise((resolve2) => {
309
- const child2 = spawn2("git", [...args], {
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 = (args, cwd, options) => {
327
- const result = spawnSync2("git", [...args], {
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, args, timeout) => {
353
+ var spawnProbe = (command, args2, timeout) => {
354
354
  const options = { encoding: "utf8", timeout, windowsHide: true };
355
- return process.platform === "win32" ? spawnSync3([command, ...args].join(" "), { ...options, shell: true }) : spawnSync3(command, [...args], options);
355
+ return process.platform === "win32" ? spawnSync3([command, ...args2].join(" "), { ...options, shell: true }) : spawnSync3(command, [...args2], options);
356
356
  };
357
- var nodeProbe = (command, args, timeoutMs) => {
358
- if (![command, ...args].every((token) => SAFE_TOKEN.test(token))) {
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, args, timeoutMs);
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/commands/doctor.ts
419
- import { dirname as dirname7 } from "node:path";
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 ask = strings(value["ask"]) ?? [];
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) => {
@@ -837,8 +1142,8 @@ var execForm = (runner, event) => {
837
1142
  };
838
1143
  var hookHandler = (runner, spec2, failClosedSupported) => {
839
1144
  const failure4 = spec2.guard && failClosedSupported ? { onFailure: "block" } : {};
840
- const { command, args } = execForm(runner, spec2.event);
841
- 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 };
842
1147
  };
843
1148
  var OWN_SHELL = /(^|[\s/"'])finitemonkey(@[^\s"]+|\.mjs|\.cmd|\.exe)?"?\s+hook\s/;
844
1149
  var OWN_SCRIPT = /finitemonkey(\.mjs|\.cmd|\.exe)?$/;
@@ -847,19 +1152,19 @@ var isOwnHandler = (handler) => {
847
1152
  if (!isRecord(handler)) return false;
848
1153
  const command = handler["command"];
849
1154
  if (typeof command !== "string") return false;
850
- const args = stringsOf(handler["args"]);
851
- if (args.length === 0) return OWN_SHELL.test(`${command} `);
852
- const hookAt = args.indexOf("hook");
1155
+ const args2 = stringsOf(handler["args"]);
1156
+ if (args2.length === 0) return OWN_SHELL.test(`${command} `);
1157
+ const hookAt = args2.indexOf("hook");
853
1158
  if (hookAt < 0) return false;
854
- const target = hookAt === 0 ? command : args[hookAt - 1] ?? "";
1159
+ const target = hookAt === 0 ? command : args2[hookAt - 1] ?? "";
855
1160
  return OWN_SCRIPT.test(target);
856
1161
  };
857
1162
 
858
1163
  // src/install/skill-template.ts
859
- var cliCommand = (runner, args) => {
860
- if (runner.kind === "local") return `node ${LOCAL_BIN} ${args}`;
861
- if (runner.kind === "vendored") return `node ${VENDOR_BIN} ${args}`;
862
- return isScript(runner.path) ? `node "${runner.path}" ${args}` : `"${runner.path}" ${args}`;
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}`;
863
1168
  };
864
1169
  var verifySkillTemplate = (runner, lang) => {
865
1170
  const skill = TEMPLATES[lang].verifySkill;
@@ -1258,7 +1563,7 @@ var mergeSettings = (current2, wanted, recorded) => {
1258
1563
  const existingDeny = asList(permissions["deny"]);
1259
1564
  const existingAsk = asList(permissions["ask"]);
1260
1565
  const deny2 = missing(wanted.deny, existingDeny);
1261
- const ask = missing(wanted.ask, existingAsk);
1566
+ const ask2 = missing(wanted.ask, existingAsk);
1262
1567
  const blockReads = !(BLOCK_READS in permissions);
1263
1568
  const attribution = wanted.attributionOff && !("attribution" in base);
1264
1569
  const marketplace = wanted.marketplace && !hasEntry(base, "extraKnownMarketplaces", MARKETPLACE_KEY);
@@ -1266,14 +1571,14 @@ var mergeSettings = (current2, wanted, recorded) => {
1266
1571
  const language = "language" in base ? void 0 : wanted.language;
1267
1572
  let settings = withPermissions(withHooks(base, hooks), {
1268
1573
  deny: [...existingDeny, ...deny2],
1269
- ask: [...existingAsk, ...ask],
1574
+ ask: [...existingAsk, ...ask2],
1270
1575
  blockReads: blockReads ? true : permissions[BLOCK_READS]
1271
1576
  });
1272
1577
  if (attribution) settings = { ...settings, attribution: ATTRIBUTION_OFF };
1273
1578
  if (marketplace) settings = withEntry(settings, "extraKnownMarketplaces", MARKETPLACE_KEY, MARKETPLACE_ENTRY);
1274
1579
  if (plugin) settings = withEntry(settings, "enabledPlugins", PLUGIN_ID, true);
1275
1580
  if (language !== void 0) settings = { ...settings, language };
1276
- const added = { schema, deny: deny2, ask, blockReads, attribution, marketplace, plugin };
1581
+ const added = { schema, deny: deny2, ask: ask2, blockReads, attribution, marketplace, plugin };
1277
1582
  return { settings, added: language === void 0 ? added : { ...added, language } };
1278
1583
  };
1279
1584
  var sameAttribution = (value) => JSON.stringify(value) === JSON.stringify(ATTRIBUTION_OFF);
@@ -2192,8 +2497,8 @@ var fmPaths = (root) => {
2192
2497
  var HOOK_FAILURE_EVENT = "hook.failed";
2193
2498
  var FAILURES = ["failed_open", "failed_closed", "stop_released"];
2194
2499
  var MAX_DETAIL = 500;
2195
- var logHookFailure = (ctx, event, outcome) => {
2196
- if (!FAILURES.includes(outcome.verdict)) return;
2500
+ var logHookFailure = (ctx, event, outcome2) => {
2501
+ if (!FAILURES.includes(outcome2.verdict)) return;
2197
2502
  const fromEnv = ctx.env["CLAUDE_PROJECT_DIR"];
2198
2503
  const root = fromEnv === void 0 ? findGitRoot(ctx.cwd, ctx.runtime) : toAbsolutePath(fromEnv);
2199
2504
  if (root === void 0) return;
@@ -2201,8 +2506,8 @@ var logHookFailure = (ctx, event, outcome) => {
2201
2506
  if (ctx.runtime.stat(paths.dir)?.kind !== "dir") return;
2202
2507
  logEvent(ctx.runtime, paths, HOOK_FAILURE_EVENT, {
2203
2508
  hook: event,
2204
- verdict: outcome.verdict,
2205
- detail: maskText(outcome.stderr.trim()).slice(0, MAX_DETAIL)
2509
+ verdict: outcome2.verdict,
2510
+ detail: maskText(outcome2.stderr.trim()).slice(0, MAX_DETAIL)
2206
2511
  });
2207
2512
  };
2208
2513
 
@@ -2615,7 +2920,7 @@ var checkSandbox = (env) => {
2615
2920
  }
2616
2921
  if (os === "macos") return [finding("sandbox", "ok", "doctor.sandbox.ok")];
2617
2922
  if (os === "other") return [];
2618
- const missing2 = SANDBOX_TOOLS.filter(({ command, args }) => !env.runtime.probe(command, args, PROBE_MS2).found).map(
2923
+ const missing2 = SANDBOX_TOOLS.filter(({ command, args: args2 }) => !env.runtime.probe(command, args2, PROBE_MS2).found).map(
2619
2924
  ({ command }) => command
2620
2925
  );
2621
2926
  if (missing2.length === 0) return [finding("sandbox", "ok", "doctor.sandbox.ok")];
@@ -3336,10 +3641,10 @@ var withAssignments = (stage) => {
3336
3641
  }
3337
3642
  return { ...stage, argv: stage.argv.slice(index), env };
3338
3643
  };
3339
- var skipOptions = (args, wrapper) => {
3644
+ var skipOptions = (args2, wrapper) => {
3340
3645
  let index = 0;
3341
- while (index < args.length) {
3342
- const arg = args[index] ?? "";
3646
+ while (index < args2.length) {
3647
+ const arg = args2[index] ?? "";
3343
3648
  if (arg === "--") return index + 1 + wrapper.positionals;
3344
3649
  if (!arg.startsWith("-") || arg === "-") break;
3345
3650
  if (wrapper.queries.has(arg)) return void 0;
@@ -3369,33 +3674,33 @@ var envCommand = (stage) => {
3369
3674
  return printsEnvironment ? { kind: "command", stage, extra: [] } : shift(stage, index, "env");
3370
3675
  };
3371
3676
  var shellScript = (stage, name, stdin) => {
3372
- const args = stage.argv.slice(1);
3677
+ const args2 = stage.argv.slice(1);
3373
3678
  let index = 0;
3374
3679
  let command = false;
3375
- while (index < args.length && /^[-+]/.test(args[index] ?? "")) {
3376
- const arg = args[index] ?? "";
3680
+ while (index < args2.length && /^[-+]/.test(args2[index] ?? "")) {
3681
+ const arg = args2[index] ?? "";
3377
3682
  if (/^-[A-Za-z]*c[A-Za-z]*$/.test(arg)) command = true;
3378
3683
  index += SHELL_WITH_VALUE.has(arg) ? 2 : 1;
3379
3684
  }
3380
- const script = args[index];
3685
+ const script = args2[index];
3381
3686
  if (command && script !== void 0) return { kind: "script", script, dialect: "bash", stage: shift(stage, 0, name) };
3382
3687
  if (script === void 0 && stdin !== void 0) return { kind: "script", script: stdin, dialect: "bash", stage };
3383
3688
  return void 0;
3384
3689
  };
3385
3690
  var decodeEncoded = (value) => Buffer.from(value, "base64").toString("utf16le");
3386
3691
  var powershellScript = (stage, name) => {
3387
- const args = stage.argv.slice(1);
3388
- const at = args.findIndex((arg) => /^[-/](?:c|co|com|comm|comma|comman|command)$/i.test(arg));
3389
- const encoded = args.findIndex((arg) => /^[-/](?:e|ec|enc|encodedcommand)$/i.test(arg));
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));
3390
3695
  if (at >= 0) {
3391
3696
  return {
3392
3697
  kind: "script",
3393
- script: args.slice(at + 1).join(" "),
3698
+ script: args2.slice(at + 1).join(" "),
3394
3699
  dialect: "powershell",
3395
3700
  stage: shift(stage, 0, name)
3396
3701
  };
3397
3702
  }
3398
- const value = encoded >= 0 ? args[encoded + 1] : void 0;
3703
+ const value = encoded >= 0 ? args2[encoded + 1] : void 0;
3399
3704
  if (value !== void 0) {
3400
3705
  return { kind: "script", script: decodeEncoded(value), dialect: "powershell", stage: shift(stage, 0, name) };
3401
3706
  }
@@ -3574,20 +3879,20 @@ var envFindings = (command) => {
3574
3879
  const exported = command.argv[0] === "export" ? command.argv.slice(1).map((arg) => arg.split("=", 2)) : [];
3575
3880
  return [...pairs2, ...exported].filter(([name = ""]) => HOOK_ENV.test(name)).map(([name = "", value = ""]) => ({ kind: "hook_env", detail: `${name}=${value}` }));
3576
3881
  };
3577
- var splitGit = (args) => {
3882
+ var splitGit = (args2) => {
3578
3883
  let index = 0;
3579
3884
  const globals = [];
3580
- while (index < args.length && (args[index] ?? "").startsWith("-")) {
3581
- const arg = args[index] ?? "";
3885
+ while (index < args2.length && (args2[index] ?? "").startsWith("-")) {
3886
+ const arg = args2[index] ?? "";
3582
3887
  globals.push(arg);
3583
- if (GIT_VALUE_OPTIONS.has(arg)) globals.push(args[index + 1] ?? "");
3888
+ if (GIT_VALUE_OPTIONS.has(arg)) globals.push(args2[index + 1] ?? "");
3584
3889
  index += GIT_VALUE_OPTIONS.has(arg) ? 2 : 1;
3585
3890
  }
3586
- return { globals, rest: args.slice(index) };
3891
+ return { globals, rest: args2.slice(index) };
3587
3892
  };
3588
- var commitHasShortN = (args) => {
3589
- for (let index = 0; index < args.length; index += 1) {
3590
- const arg = args[index] ?? "";
3893
+ var commitHasShortN = (args2) => {
3894
+ for (let index = 0; index < args2.length; index += 1) {
3895
+ const arg = args2[index] ?? "";
3591
3896
  if (arg === "--") return false;
3592
3897
  if (!/^-[A-Za-z]/.test(arg)) continue;
3593
3898
  for (const char of arg.slice(1)) {
@@ -3600,8 +3905,8 @@ var commitHasShortN = (args) => {
3600
3905
  }
3601
3906
  return false;
3602
3907
  };
3603
- var gitFindings = (args) => {
3604
- const { globals, rest } = splitGit(args);
3908
+ var gitFindings = (args2) => {
3909
+ const { globals, rest } = splitGit(args2);
3605
3910
  const found = [];
3606
3911
  globals.forEach((arg, index) => {
3607
3912
  if (arg === "-c" && isHooksPath(globals[index + 1] ?? "") || /^--config-env=core\.hookspath/i.test(arg)) {
@@ -3618,10 +3923,21 @@ var gitFindings = (args) => {
3618
3923
  return found;
3619
3924
  };
3620
3925
  var FM_NAMES = /* @__PURE__ */ new Set(["finitemonkey", "fm"]);
3926
+ var FM_SCRIPT = /(^|[\\/])(finitemonkey|fm)(\.c?m?js)?$/;
3927
+ var RUNNERS = /* @__PURE__ */ new Set(["npx", "pnpm", "bunx", "yarn"]);
3928
+ var fmStart = (argv) => {
3929
+ const first = argv[0] ?? "";
3930
+ if (RUNNERS.has(first)) return argv.findIndex((arg) => FM_NAMES.has(arg.replace(/@.*$/, "")));
3931
+ if (first === "node" || first === "bun" || first === "deno") return FM_SCRIPT.test(argv[1] ?? "") ? 1 : -1;
3932
+ return FM_SCRIPT.test(first) ? 0 : -1;
3933
+ };
3621
3934
  var fmFindings = (argv) => {
3622
- const start = argv[0] === "npx" || argv[0] === "pnpm" || argv[0] === "bunx" ? argv.findIndex((arg) => FM_NAMES.has(arg.replace(/@.*$/, ""))) : 0;
3623
- const name = (argv[start] ?? "").replace(/@.*$/, "");
3624
- return start >= 0 && FM_NAMES.has(name) && argv[start + 1] === "allow" ? [{ kind: "fm_allow", detail: "finitemonkey allow" }] : [];
3935
+ const start = fmStart(argv);
3936
+ if (start < 0) return [];
3937
+ const sub = argv[start + 1];
3938
+ if (sub === "allow") return [{ kind: "fm_allow", detail: "finitemonkey allow" }];
3939
+ if (sub === "off" || sub === "on") return [{ kind: "fm_pause", detail: `finitemonkey ${sub}` }];
3940
+ return [];
3625
3941
  };
3626
3942
  var detectBypass = (commandLine) => parseShell(commandLine).flatMap((command) => [
3627
3943
  ...envFindings(command),
@@ -3678,16 +3994,16 @@ var GIT_WRITERS = /* @__PURE__ */ new Set(["checkout", "restore", "rm", "mv", "c
3678
3994
  var FM_NAMES2 = /* @__PURE__ */ new Set(["finitemonkey", "fm"]);
3679
3995
  var inline = (argv) => argv.some((arg) => arg === "-e" || arg === "-c" || arg === "--eval");
3680
3996
  var writtenPaths = (command) => {
3681
- const [name = "", ...args] = command.argv;
3682
- const operands2 = args.filter((arg) => !arg.startsWith("-"));
3683
- const ddTargets = args.filter((arg) => arg.startsWith("of=")).map((arg) => arg.slice(3));
3997
+ const [name = "", ...args2] = command.argv;
3998
+ const operands2 = args2.filter((arg) => !arg.startsWith("-"));
3999
+ const ddTargets = args2.filter((arg) => arg.startsWith("of=")).map((arg) => arg.slice(3));
3684
4000
  if (FM_NAMES2.has(name)) return command.redirects;
3685
4001
  if (COPIERS.has(name)) return [...command.redirects, ...operands2.slice(-1)];
3686
4002
  if (WRITERS.has(name)) return [...command.redirects, ...operands2, ...ddTargets];
3687
- if (IN_PLACE.has(name) && args.some((arg) => /^-[a-zA-Z]*i/.test(arg) || arg.startsWith("--in-place"))) {
4003
+ if (IN_PLACE.has(name) && args2.some((arg) => /^-[a-zA-Z]*i/.test(arg) || arg.startsWith("--in-place"))) {
3688
4004
  return [...command.redirects, ...operands2];
3689
4005
  }
3690
- if (INTERPRETERS.has(name) && inline(args)) return [...command.redirects, ...args];
4006
+ if (INTERPRETERS.has(name) && inline(args2)) return [...command.redirects, ...args2];
3691
4007
  if (name === "git" && GIT_WRITERS.has(operands2[0] ?? "")) return [...command.redirects, ...operands2.slice(1)];
3692
4008
  return command.redirects;
3693
4009
  };
@@ -3706,14 +4022,14 @@ var bashSelfTargets = (root, commandLine) => parseShell(commandLine).flatMap(
3706
4022
  // src/guards/self/hook.ts
3707
4023
  var targets = (input) => {
3708
4024
  const tool = toolName(input.payload.raw);
3709
- const args = toolInput(input.payload.raw);
4025
+ const args2 = toolInput(input.payload.raw);
3710
4026
  const root = guardRoot(input);
3711
4027
  if (FILE_TOOLS.has(tool)) {
3712
- const path = textField(args, "file_path", "notebook_path") ?? "";
4028
+ const path = textField(args2, "file_path", "notebook_path") ?? "";
3713
4029
  const kind = path === "" ? void 0 : selfTarget(root, path);
3714
4030
  return kind === void 0 ? [] : [{ kind, path }];
3715
4031
  }
3716
- return SHELL_TOOLS.has(tool) ? bashSelfTargets(root, textField(args, "command") ?? "") : [];
4032
+ return SHELL_TOOLS.has(tool) ? bashSelfTargets(root, textField(args2, "command") ?? "") : [];
3717
4033
  };
3718
4034
  var selfGuard = (input) => {
3719
4035
  const found = targets(input);
@@ -3897,19 +4213,19 @@ var read = (input, path) => {
3897
4213
  const text6 = input.runtime.readText(path);
3898
4214
  return text6.ok ? text6.value : void 0;
3899
4215
  };
3900
- var proposed = (tool, args, before) => {
3901
- if (tool === "Write") return textField(args, "content") ?? "";
3902
- return before === void 0 ? void 0 : applyEdits(before, editsOf(args));
4216
+ var proposed = (tool, args2, before) => {
4217
+ if (tool === "Write") return textField(args2, "content") ?? "";
4218
+ return before === void 0 ? void 0 : applyEdits(before, editsOf(args2));
3903
4219
  };
3904
4220
  var fileViolations = (input, root) => {
3905
4221
  const tool = toolName(input.payload.raw);
3906
- const args = toolInput(input.payload.raw);
3907
- const file = textField(args, "file_path") ?? "";
4222
+ const args2 = toolInput(input.payload.raw);
4223
+ const file = textField(args2, "file_path") ?? "";
3908
4224
  if (tool === "NotebookEdit" || file === "") return [];
3909
4225
  const local = localPath(root, file);
3910
4226
  if (!isTestFile(local) && !isProtectedConfig(local)) return [];
3911
4227
  const before = read(input, toAbsolutePath(file, root));
3912
- const after = proposed(tool, args, before);
4228
+ const after = proposed(tool, args2, before);
3913
4229
  if (after === void 0) return [];
3914
4230
  const accepted2 = readTrust(input.runtime, fmPaths(root)).configs[local];
3915
4231
  if (before === void 0) return compareNewTestFile(local, after);
@@ -3939,9 +4255,6 @@ var testsGuard = (input) => {
3939
4255
  return deny(t("gate.guard.tests.model", { detail: reason }), t("gate.guard.tests.human", { detail: reason }));
3940
4256
  };
3941
4257
 
3942
- // src/gate/hooks/config.ts
3943
- import { join as join13 } from "node:path";
3944
-
3945
4258
  // src/gate/hooks/common.ts
3946
4259
  import { dirname as dirname5, join as join12 } from "node:path";
3947
4260
  var DEFAULT_GATE_BUDGET_MS = 3e5;
@@ -3972,7 +4285,90 @@ var contextOutput = (event, additionalContext, systemMessage) => ({
3972
4285
  });
3973
4286
  var human = (text6) => `finitemonkey: ${text6}`;
3974
4287
 
4288
+ // src/guards/release/markers.ts
4289
+ var LINE = /^\s*(?:finitemonkey|fm):\s+(allow|approve|trust)\s+(\S+)(?:\s+(.*))?$/i;
4290
+ var NAME = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,80}$/;
4291
+ var NOT_FROM_PERSON = [
4292
+ /<task-notification\b/i,
4293
+ /<agent-message\b/i,
4294
+ /^\s*\[julius2claude\]/,
4295
+ /\[Subagent hand-back\]/i
4296
+ ];
4297
+ var comesFromPerson = (prompt) => !NOT_FROM_PERSON.some((pattern) => pattern.test(prompt));
4298
+ var toMarker = (verb, target, rest) => {
4299
+ if (!NAME.test(target)) return void 0;
4300
+ if (verb === "trust") return { type: "trust", task: target };
4301
+ if (verb === "allow") {
4302
+ return rest.length >= 3 ? { type: "allow", guard: target.toLowerCase(), reason: rest } : void 0;
4303
+ }
4304
+ const slash = target.indexOf("/");
4305
+ const task = slash < 0 ? void 0 : target.slice(0, slash);
4306
+ const check2 = slash < 0 ? target : target.slice(slash + 1);
4307
+ if (check2 === "") return void 0;
4308
+ return { type: "approve", check: check2, reason: rest, ...task === void 0 || task === "" ? {} : { task } };
4309
+ };
4310
+ var parseMarkers = (prompt) => {
4311
+ if (!comesFromPerson(prompt)) return [];
4312
+ return prompt.split(/\r?\n/).flatMap((line2) => {
4313
+ const match = LINE.exec(line2);
4314
+ if (match === null) return [];
4315
+ const marker = toMarker((match[1] ?? "").toLowerCase(), match[2] ?? "", (match[3] ?? "").trim());
4316
+ return marker === void 0 ? [] : [marker];
4317
+ });
4318
+ };
4319
+
4320
+ // src/policy/pause.ts
4321
+ var PAUSE_FILE = "pause.json";
4322
+ var readPause = (runtime, paths) => {
4323
+ const text6 = runtime.readText(paths.cache(PAUSE_FILE));
4324
+ const parsed = text6.ok ? parseJsonObject(text6.value) : void 0;
4325
+ if (parsed?.ok !== true || !isRecord(parsed.value)) return void 0;
4326
+ const { sessionId, since, by } = parsed.value;
4327
+ if (typeof sessionId !== "string" || typeof since !== "string") return void 0;
4328
+ return { sessionId, since, by: by === "terminal" ? "terminal" : "prompt" };
4329
+ };
4330
+ var pausedFor = (runtime, paths, sessionId) => {
4331
+ const pause = readPause(runtime, paths);
4332
+ return pause !== void 0 && sessionId !== void 0 && pause.sessionId === sessionId ? pause : void 0;
4333
+ };
4334
+ var setPause = (runtime, paths, sessionId, by) => {
4335
+ const pause = { sessionId, since: runtime.now(), by };
4336
+ runtime.writeText(paths.cache(PAUSE_FILE), `${JSON.stringify(pause)}
4337
+ `);
4338
+ logEvent(runtime, paths, "gate_paused", { sessionId, by });
4339
+ return pause;
4340
+ };
4341
+ var clearPause = (runtime, paths, by) => {
4342
+ const pause = readPause(runtime, paths);
4343
+ if (pause === void 0) return void 0;
4344
+ runtime.remove(paths.cache(PAUSE_FILE));
4345
+ logEvent(runtime, paths, "gate_resumed", { sessionId: pause.sessionId, since: pause.since, by });
4346
+ return pause;
4347
+ };
4348
+
4349
+ // src/policy/pause-hooks.ts
4350
+ var COMMAND = /^\s*(fm|finitemonkey)\s+(off|on)\s*$/i;
4351
+ var pausePrompt = (input) => {
4352
+ const prompt = textField(input.payload.raw, "prompt", "prompt_text") ?? "";
4353
+ const match = COMMAND.exec(prompt);
4354
+ if (match === null || !comesFromPerson(prompt)) return { kind: "allow" };
4355
+ const paths = projectPaths(input);
4356
+ const sessionId = input.payload.sessionId;
4357
+ if (paths === void 0 || sessionId === void 0) return { kind: "allow" };
4358
+ const off = (match[2] ?? "").toLowerCase() === "off";
4359
+ if (off) setPause(input.runtime, paths, sessionId, "prompt");
4360
+ else clearPause(input.runtime, paths, "prompt");
4361
+ return { kind: "block", reason: human(input.t(off ? "pause.off" : "pause.on")) };
4362
+ };
4363
+ var unlessPaused = (handler) => (input) => {
4364
+ const paths = projectPaths(input);
4365
+ const pause = paths === void 0 ? void 0 : pausedFor(input.runtime, paths, input.payload.sessionId);
4366
+ if (pause === void 0) return handler(input);
4367
+ return allowWith({ systemMessage: human(input.t("pause.report", { since: pause.since })) });
4368
+ };
4369
+
3975
4370
  // src/gate/hooks/config.ts
4371
+ import { join as join13 } from "node:path";
3976
4372
  var CONFIG_FILE3 = "config-hooks.json";
3977
4373
  var GUARDED_SOURCES = /* @__PURE__ */ new Set(["user_settings", "project_settings", "local_settings"]);
3978
4374
  var countIn = (value) => {
@@ -4038,38 +4434,6 @@ var configChange = (input) => {
4038
4434
  };
4039
4435
  };
4040
4436
 
4041
- // src/guards/release/markers.ts
4042
- var LINE = /^\s*(?:finitemonkey|fm):\s+(allow|approve|trust)\s+(\S+)(?:\s+(.*))?$/i;
4043
- var NAME = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,80}$/;
4044
- var NOT_FROM_PERSON = [
4045
- /<task-notification\b/i,
4046
- /<agent-message\b/i,
4047
- /^\s*\[julius2claude\]/,
4048
- /\[Subagent hand-back\]/i
4049
- ];
4050
- var comesFromPerson = (prompt) => !NOT_FROM_PERSON.some((pattern) => pattern.test(prompt));
4051
- var toMarker = (verb, target, rest) => {
4052
- if (!NAME.test(target)) return void 0;
4053
- if (verb === "trust") return { type: "trust", task: target };
4054
- if (verb === "allow") {
4055
- return rest.length >= 3 ? { type: "allow", guard: target.toLowerCase(), reason: rest } : void 0;
4056
- }
4057
- const slash = target.indexOf("/");
4058
- const task = slash < 0 ? void 0 : target.slice(0, slash);
4059
- const check2 = slash < 0 ? target : target.slice(slash + 1);
4060
- if (check2 === "") return void 0;
4061
- return { type: "approve", check: check2, reason: rest, ...task === void 0 || task === "" ? {} : { task } };
4062
- };
4063
- var parseMarkers = (prompt) => {
4064
- if (!comesFromPerson(prompt)) return [];
4065
- return prompt.split(/\r?\n/).flatMap((line2) => {
4066
- const match = LINE.exec(line2);
4067
- if (match === null) return [];
4068
- const marker = toMarker((match[1] ?? "").toLowerCase(), match[2] ?? "", (match[3] ?? "").trim());
4069
- return marker === void 0 ? [] : [marker];
4070
- });
4071
- };
4072
-
4073
4437
  // src/task/implicit.ts
4074
4438
  var IMPLICIT_ID = "implicit";
4075
4439
  var LOCKFILES2 = [
@@ -4426,7 +4790,7 @@ var str = (record3, key, at, issues, required) => {
4426
4790
  }
4427
4791
  return value;
4428
4792
  };
4429
- var positive = (record3, key, at, issues) => {
4793
+ var positive2 = (record3, key, at, issues) => {
4430
4794
  const value = record3[key];
4431
4795
  if (value === void 0 || value === null) return void 0;
4432
4796
  if (typeof value === "number" && Number.isInteger(value) && value > 0) return value;
@@ -4446,11 +4810,26 @@ var gitRequire = (value, at, issues) => {
4446
4810
  if (known2.some((item) => item === void 0)) issues.push(`${at}: use ${GIT_REQUIREMENTS.join(", ")}`);
4447
4811
  return known2.filter((item) => item !== void 0);
4448
4812
  };
4813
+ var appFields = (record3, kind, at, issues) => {
4814
+ const port = positive2(record3, "port", at, issues);
4815
+ if (kind === "runtime" && port === void 0) issues.push(`${at}port: required`);
4816
+ if (port !== void 0 && port > 65535) issues.push(`${at}port: must be at most 65535`);
4817
+ const status = positive2(record3, "status", at, issues);
4818
+ const route = str(record3, "route", at, issues, false);
4819
+ const body2 = str(record3, "body", at, issues, false);
4820
+ return {
4821
+ ...port === void 0 ? {} : { port },
4822
+ ...route === void 0 ? {} : { route },
4823
+ ...status === void 0 ? {} : { status },
4824
+ ...body2 === void 0 ? {} : { body: body2 }
4825
+ };
4826
+ };
4449
4827
  var checkFields = (record3, kind, at, issues) => {
4450
4828
  const run4 = str(record3, "run", at, issues, kind === "command");
4451
- const timeoutS = positive(record3, "timeout_s", at, issues);
4829
+ const timeoutS = positive2(record3, "timeout_s", at, issues);
4452
4830
  const description = str(record3, "description", at, issues, kind === "manual");
4453
4831
  const require2 = kind === "git" ? gitRequire(record3["require"] ?? ["committed"], `${at}require`, issues) : void 0;
4832
+ const app = appFields(record3, kind, at, issues);
4454
4833
  const open = record3["require_open"];
4455
4834
  if (open !== void 0 && typeof open !== "boolean") issues.push(`${at}require_open: must be true or false`);
4456
4835
  return {
@@ -4458,7 +4837,8 @@ var checkFields = (record3, kind, at, issues) => {
4458
4837
  ...timeoutS === void 0 ? {} : { timeoutS },
4459
4838
  ...description === void 0 ? {} : { description },
4460
4839
  ...require2 === void 0 ? {} : { require: require2 },
4461
- ...typeof open === "boolean" ? { requireOpen: open } : {}
4840
+ ...typeof open === "boolean" ? { requireOpen: open } : {},
4841
+ ...app
4462
4842
  };
4463
4843
  };
4464
4844
  var acceptanceItem = (value, index, issues) => {
@@ -4496,8 +4876,8 @@ var scope = (value, issues) => {
4496
4876
  ...files === void 0 ? {} : { files },
4497
4877
  ...lines3 === void 0 ? {} : { lines: lines3 }
4498
4878
  }))({
4499
- files: positive(budget, "files", "scope.diff_budget.", issues),
4500
- lines: positive(budget, "lines", "scope.diff_budget.", issues)
4879
+ files: positive2(budget, "files", "scope.diff_budget.", issues),
4880
+ lines: positive2(budget, "lines", "scope.diff_budget.", issues)
4501
4881
  }) : void 0;
4502
4882
  if (budget !== void 0 && budget !== null && !isRecord(budget)) issues.push("scope.diff_budget: must be a mapping");
4503
4883
  const protectedValue = value["tests_protected"];
@@ -4622,12 +5002,12 @@ var EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
4622
5002
  var GIT_TIMEOUT_MS = 15e3;
4623
5003
  var EXCLUDE_FM = `:(exclude)${FM_DIR}`;
4624
5004
  var gitAt = (runtime, cwd) => {
4625
- const run4 = (args) => runtime.exec({ command: "git", args: ["-c", "core.quotepath=off", ...args], cwd, timeoutMs: GIT_TIMEOUT_MS });
5005
+ const run4 = (args2) => runtime.exec({ command: "git", args: ["-c", "core.quotepath=off", ...args2], cwd, timeoutMs: GIT_TIMEOUT_MS });
4626
5006
  return {
4627
5007
  cwd,
4628
5008
  run: run4,
4629
- out: (args) => {
4630
- const result = run4(args);
5009
+ out: (args2) => {
5010
+ const result = run4(args2);
4631
5011
  return result.found && result.exitCode === 0 ? result.stdout : void 0;
4632
5012
  }
4633
5013
  };
@@ -4774,7 +5154,7 @@ var parseMergeTreeConflicts = (stdout) => {
4774
5154
  var PR_FIELDS = "number,url,state,mergeable,mergeStateStatus,baseRefName,headRefOid,statusCheckRollup";
4775
5155
  var PR_CACHE_TTL_MS = 6e4;
4776
5156
  var GH_TIMEOUT_MS = 8e3;
4777
- var gh = (runtime, cwd, args, timeoutMs = GH_TIMEOUT_MS) => runtime.exec({ command: "gh", args, cwd, timeoutMs });
5157
+ var gh = (runtime, cwd, args2, timeoutMs = GH_TIMEOUT_MS) => runtime.exec({ command: "gh", args: args2, cwd, timeoutMs });
4778
5158
  var ghAuthenticated = (runtime, cwd, timeoutMs = 4e3) => {
4779
5159
  const result = gh(runtime, cwd, ["auth", "status"], timeoutMs);
4780
5160
  if (result.timedOut) return "timeout";
@@ -5632,6 +6012,7 @@ var en_default = {
5632
6012
  "gate.guard.bypass.hooks_path": "Finite Monkey: blocked: {detail} points git away from the project hooks. requires: core.hooksPath stays as configured.",
5633
6013
  "gate.guard.bypass.hook_env": "Finite Monkey: blocked: {detail} turns hooks or guards off. requires: hooks and guards stay on.",
5634
6014
  "gate.guard.bypass.fm_allow": "Finite Monkey: blocked: releases are made by the person, not by a tool call.",
6015
+ "gate.guard.bypass.fm_pause": "Finite Monkey: blocked: only the person pauses or resumes the gate, by typing `fm off` or `fm on` as the whole prompt.",
5635
6016
  "gate.guard.bypass.human": "Blocked {detail}: it would switch the safety net off. If you really want it, type on its own line: finitemonkey: allow bypass <reason>",
5636
6017
  "gate.guard.self.policy": "Finite Monkey: blocked: {file} is written only by the finitemonkey executable (evidence, policy and state). requires: change it through finitemonkey commands.",
5637
6018
  "gate.guard.self.allow_store": "Finite Monkey: blocked: {file} records what the person released; tools never write it.",
@@ -5787,7 +6168,35 @@ var en_default = {
5787
6168
  "gate.callers.warning": "warning only: {percent}% of callers findings were released with callers_ignore ({released} of {total}), above the 5% ceiling",
5788
6169
  "command.policy_false_positive.summary": "Record a false positive of a guard in shadow so it is not promoted",
5789
6170
  "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)",
5790
- "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."
6171
+ "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.",
6172
+ "command.app_check.summary": "Start the app or reuse the running one, call one route and check the answer",
6173
+ "app.check.ok": "{url} answered {status}",
6174
+ "app.check.crashed": "the app exited with code {code} before opening the port",
6175
+ "app.check.no_port": "the port of {url} did not open before the timeout",
6176
+ "app.check.no_start": "nothing listens on {url} and no start command was given",
6177
+ "app.check.wrong_status": "{url} answered {status}",
6178
+ "app.check.body_mismatch": "{url} answered {status} without the expected text",
6179
+ "app.check.request_failed": "the request to {url} failed",
6180
+ "app.check.usage": "give --port as a number from 1 to 65535; --status and --timeout must be positive numbers",
6181
+ "gate.require.runtime": "the app starts and `{command}` answers as declared",
6182
+ "gate.runtime.ok": "started, {route} answered {status}, stopped",
6183
+ "gate.runtime.reused": "reused the running app, {route} answered {status}",
6184
+ "gate.runtime.crashed": "the app exited with code {code} before opening its port",
6185
+ "gate.runtime.no_port": "port {port} did not open before the timeout",
6186
+ "gate.runtime.no_start": "nothing listens on port {port} and the check has no `run` to start the app",
6187
+ "gate.runtime.no_port_declared": "the runtime check needs a `port`",
6188
+ "gate.runtime.wrong_status": "{route} answered {status}, expected {expected}",
6189
+ "gate.runtime.body_mismatch": '{route} did not contain "{body}"',
6190
+ "gate.runtime.request_failed": "the request to {route} failed: {detail}",
6191
+ "gate.runtime.orphan": "the app on port {port} could not be stopped",
6192
+ "gate.runtime.probe_failed": "the app check did not report a result: {line}",
6193
+ "command.off.summary": "Pause the gate and warnings for the current Claude Code session (the person only)",
6194
+ "command.on.summary": "Resume the gate after fm off",
6195
+ "pause.off": "Gate paused for this session by the person. Secrets, production and destructive git guards stay on. Type `fm on` to resume.",
6196
+ "pause.on": "Gate resumed.",
6197
+ "pause.report": "The gate did not check this stop: the person paused it with fm off at {since}.",
6198
+ "pause.no_session": "No Claude Code session is recorded for this project yet; start one, then run this again or type `fm off` there.",
6199
+ "pause.error.person_only": "Only the person can pause or resume the gate, and this does not look like their terminal (no TTY, or running inside Claude Code)"
5791
6200
  };
5792
6201
 
5793
6202
  // src/i18n/es.json
@@ -6164,6 +6573,7 @@ var es_default = {
6164
6573
  "gate.guard.bypass.hooks_path": "Finite Monkey: bloqueado: {detail} desvía git de los hooks del proyecto. exige: core.hooksPath queda como está configurado.",
6165
6574
  "gate.guard.bypass.hook_env": "Finite Monkey: bloqueado: {detail} apaga hooks o guardas. exige: hooks y guardas quedan encendidos.",
6166
6575
  "gate.guard.bypass.fm_allow": "Finite Monkey: bloqueado: las liberaciones las hace la persona, no una llamada de herramienta.",
6576
+ "gate.guard.bypass.fm_pause": "Finite Monkey: bloqueado: solo la persona pausa o reanuda la compuerta, escribiendo `fm off` o `fm on` como el prompt completo.",
6167
6577
  "gate.guard.bypass.human": "Bloqueé {detail}: apagaría la red de seguridad. Si de verdad lo quiere, escriba en una línea: finitemonkey: allow bypass <motivo>",
6168
6578
  "gate.guard.self.policy": "Finite Monkey: bloqueado: {file} solo lo escribe el ejecutable finitemonkey (evidencia, política y estado). exige: cambiarlo con los comandos de finitemonkey.",
6169
6579
  "gate.guard.self.allow_store": "Finite Monkey: bloqueado: {file} registra lo que la persona liberó; ninguna herramienta lo escribe.",
@@ -6319,7 +6729,35 @@ var es_default = {
6319
6729
  "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%",
6320
6730
  "command.policy_false_positive.summary": "Registra un falso positivo de un guard en shadow para que no se promueva",
6321
6731
  "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)",
6322
- "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."
6732
+ "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.",
6733
+ "command.app_check.summary": "Arranca la app o reutiliza la que ya corre, llama a una ruta y comprueba la respuesta",
6734
+ "app.check.ok": "{url} respondió {status}",
6735
+ "app.check.crashed": "la app salió con código {code} antes de abrir el puerto",
6736
+ "app.check.no_port": "el puerto de {url} no se abrió antes del tiempo límite",
6737
+ "app.check.no_start": "nada escucha en {url} y no se indicó comando de arranque",
6738
+ "app.check.wrong_status": "{url} respondió {status}",
6739
+ "app.check.body_mismatch": "{url} respondió {status} sin el texto esperado",
6740
+ "app.check.request_failed": "la petición a {url} falló",
6741
+ "app.check.usage": "indica --port como número de 1 a 65535; --status y --timeout deben ser números positivos",
6742
+ "gate.require.runtime": "la app arranca y `{command}` responde como se declaró",
6743
+ "gate.runtime.ok": "arrancó, {route} respondió {status}, detenida",
6744
+ "gate.runtime.reused": "reutilizó la app en marcha, {route} respondió {status}",
6745
+ "gate.runtime.crashed": "la app salió con código {code} antes de abrir su puerto",
6746
+ "gate.runtime.no_port": "el puerto {port} no se abrió antes del tiempo límite",
6747
+ "gate.runtime.no_start": "nada escucha en el puerto {port} y la comprobación no tiene `run` para arrancar la app",
6748
+ "gate.runtime.no_port_declared": "la comprobación runtime necesita `port`",
6749
+ "gate.runtime.wrong_status": "{route} respondió {status}, se esperaba {expected}",
6750
+ "gate.runtime.body_mismatch": '{route} no contenía "{body}"',
6751
+ "gate.runtime.request_failed": "la petición a {route} falló: {detail}",
6752
+ "gate.runtime.orphan": "no se pudo detener la app del puerto {port}",
6753
+ "gate.runtime.probe_failed": "la comprobación de la app no devolvió resultado: {line}",
6754
+ "command.off.summary": "Pausa la compuerta y los avisos en la sesión actual de Claude Code (solo la persona)",
6755
+ "command.on.summary": "Reanuda la compuerta después de fm off",
6756
+ "pause.off": "Compuerta pausada en esta sesión por la persona. Los bloqueos de secretos, producción y git destructivo siguen activos. Escribe `fm on` para reanudar.",
6757
+ "pause.on": "Compuerta reanudada.",
6758
+ "pause.report": "La compuerta no revisó esta parada: la persona la pausó con fm off a las {since}.",
6759
+ "pause.no_session": "Todavía no hay una sesión de Claude Code registrada en este proyecto; abre una y vuelve a ejecutarlo, o escribe `fm off` allí.",
6760
+ "pause.error.person_only": "Solo la persona puede pausar o reanudar la compuerta, y esto no parece su terminal (sin TTY, o dentro de Claude Code)"
6323
6761
  };
6324
6762
 
6325
6763
  // src/i18n/pt-BR.json
@@ -6696,6 +7134,7 @@ var pt_BR_default = {
6696
7134
  "gate.guard.bypass.hooks_path": "Finite Monkey: barrado: {detail} desvia o git dos hooks do projeto. exige: core.hooksPath fica como está configurado.",
6697
7135
  "gate.guard.bypass.hook_env": "Finite Monkey: barrado: {detail} desliga hooks ou travas. exige: hooks e travas ficam ligados.",
6698
7136
  "gate.guard.bypass.fm_allow": "Finite Monkey: barrado: liberação é feita pela pessoa, não por chamada de ferramenta.",
7137
+ "gate.guard.bypass.fm_pause": "Finite Monkey: bloqueado: só a pessoa pausa ou retoma o portão, digitando `fm off` ou `fm on` como o prompt inteiro.",
6699
7138
  "gate.guard.bypass.human": "Barrei {detail}: desligaria a rede de proteção. Se quiser mesmo, digite numa linha: finitemonkey: allow bypass <motivo>",
6700
7139
  "gate.guard.self.policy": "Finite Monkey: barrado: {file} só é escrito pelo executável finitemonkey (evidência, política e estado). exige: mudar pelos comandos do finitemonkey.",
6701
7140
  "gate.guard.self.allow_store": "Finite Monkey: barrado: {file} registra o que a pessoa liberou; ferramenta nunca escreve nele.",
@@ -6851,7 +7290,35 @@ var pt_BR_default = {
6851
7290
  "gate.callers.warning": "só aviso: {percent}% dos achados de callers foram liberados com callers_ignore ({released} de {total}), acima do teto de 5%",
6852
7291
  "command.policy_false_positive.summary": "Registra um falso positivo de um guard em shadow para ele não ser promovido",
6853
7292
  "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)",
6854
- "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."
7293
+ "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.",
7294
+ "command.app_check.summary": "Sobe o app ou reaproveita o que está rodando, chama uma rota e confere a resposta",
7295
+ "app.check.ok": "{url} respondeu {status}",
7296
+ "app.check.crashed": "o app saiu com código {code} antes de abrir a porta",
7297
+ "app.check.no_port": "a porta de {url} não abriu antes do tempo limite",
7298
+ "app.check.no_start": "nada escuta em {url} e nenhum comando de início foi dado",
7299
+ "app.check.wrong_status": "{url} respondeu {status}",
7300
+ "app.check.body_mismatch": "{url} respondeu {status} sem o texto esperado",
7301
+ "app.check.request_failed": "a requisição para {url} falhou",
7302
+ "app.check.usage": "passe --port como número de 1 a 65535; --status e --timeout precisam ser números positivos",
7303
+ "gate.require.runtime": "o app sobe e `{command}` responde como declarado",
7304
+ "gate.runtime.ok": "subiu, {route} respondeu {status}, parado",
7305
+ "gate.runtime.reused": "reaproveitou o app rodando, {route} respondeu {status}",
7306
+ "gate.runtime.crashed": "o app saiu com código {code} antes de abrir a porta",
7307
+ "gate.runtime.no_port": "a porta {port} não abriu antes do tempo limite",
7308
+ "gate.runtime.no_start": "nada escuta na porta {port} e a checagem não tem `run` para subir o app",
7309
+ "gate.runtime.no_port_declared": "a checagem runtime precisa de `port`",
7310
+ "gate.runtime.wrong_status": "{route} respondeu {status}, esperado {expected}",
7311
+ "gate.runtime.body_mismatch": '{route} não continha "{body}"',
7312
+ "gate.runtime.request_failed": "a requisição para {route} falhou: {detail}",
7313
+ "gate.runtime.orphan": "o app na porta {port} não pôde ser parado",
7314
+ "gate.runtime.probe_failed": "a checagem do app não devolveu resultado: {line}",
7315
+ "command.off.summary": "Pausa o portão e os avisos na sessão atual do Claude Code (só a pessoa)",
7316
+ "command.on.summary": "Retoma o portão depois de fm off",
7317
+ "pause.off": "Portão pausado nesta sessão pela pessoa. As travas de segredos, produção e git destrutivo continuam ligadas. Digite `fm on` para retomar.",
7318
+ "pause.on": "Portão retomado.",
7319
+ "pause.report": "O portão não conferiu esta parada: a pessoa pausou com fm off em {since}.",
7320
+ "pause.no_session": "Ainda não há sessão do Claude Code registrada neste projeto; abra uma e rode de novo, ou digite `fm off` nela.",
7321
+ "pause.error.person_only": "Só a pessoa pode pausar ou retomar o portão, e isto não parece o terminal dela (sem TTY, ou rodando dentro do Claude Code)"
6855
7322
  };
6856
7323
 
6857
7324
  // src/i18n/review.json
@@ -7229,6 +7696,7 @@ var review_default = {
7229
7696
  "gate.guard.bypass.hooks_path": "machine",
7230
7697
  "gate.guard.bypass.hook_env": "machine",
7231
7698
  "gate.guard.bypass.fm_allow": "machine",
7699
+ "gate.guard.bypass.fm_pause": "machine",
7232
7700
  "gate.guard.bypass.human": "machine",
7233
7701
  "gate.guard.self.policy": "machine",
7234
7702
  "gate.guard.self.allow_store": "machine",
@@ -7384,7 +7852,35 @@ var review_default = {
7384
7852
  "gate.callers.warning": "machine",
7385
7853
  "command.policy_false_positive.summary": "machine",
7386
7854
  "policy.error.person_only": "machine",
7387
- "policy.false_positive.recorded": "machine"
7855
+ "policy.false_positive.recorded": "machine",
7856
+ "command.app_check.summary": "machine",
7857
+ "app.check.ok": "machine",
7858
+ "app.check.crashed": "machine",
7859
+ "app.check.no_port": "machine",
7860
+ "app.check.no_start": "machine",
7861
+ "app.check.wrong_status": "machine",
7862
+ "app.check.body_mismatch": "machine",
7863
+ "app.check.request_failed": "machine",
7864
+ "app.check.usage": "machine",
7865
+ "gate.require.runtime": "machine",
7866
+ "gate.runtime.ok": "machine",
7867
+ "gate.runtime.reused": "machine",
7868
+ "gate.runtime.crashed": "machine",
7869
+ "gate.runtime.no_port": "machine",
7870
+ "gate.runtime.no_start": "machine",
7871
+ "gate.runtime.no_port_declared": "machine",
7872
+ "gate.runtime.wrong_status": "machine",
7873
+ "gate.runtime.body_mismatch": "machine",
7874
+ "gate.runtime.request_failed": "machine",
7875
+ "gate.runtime.orphan": "machine",
7876
+ "gate.runtime.probe_failed": "machine",
7877
+ "command.off.summary": "machine",
7878
+ "command.on.summary": "machine",
7879
+ "pause.off": "machine",
7880
+ "pause.on": "machine",
7881
+ "pause.report": "machine",
7882
+ "pause.no_session": "machine",
7883
+ "pause.error.person_only": "machine"
7388
7884
  },
7389
7885
  es: {
7390
7886
  "command.init.summary": "machine",
@@ -7759,6 +8255,7 @@ var review_default = {
7759
8255
  "gate.guard.bypass.hooks_path": "machine",
7760
8256
  "gate.guard.bypass.hook_env": "machine",
7761
8257
  "gate.guard.bypass.fm_allow": "machine",
8258
+ "gate.guard.bypass.fm_pause": "machine",
7762
8259
  "gate.guard.bypass.human": "machine",
7763
8260
  "gate.guard.self.policy": "machine",
7764
8261
  "gate.guard.self.allow_store": "machine",
@@ -7914,7 +8411,35 @@ var review_default = {
7914
8411
  "gate.callers.warning": "machine",
7915
8412
  "command.policy_false_positive.summary": "machine",
7916
8413
  "policy.error.person_only": "machine",
7917
- "policy.false_positive.recorded": "machine"
8414
+ "policy.false_positive.recorded": "machine",
8415
+ "command.app_check.summary": "machine",
8416
+ "app.check.ok": "machine",
8417
+ "app.check.crashed": "machine",
8418
+ "app.check.no_port": "machine",
8419
+ "app.check.no_start": "machine",
8420
+ "app.check.wrong_status": "machine",
8421
+ "app.check.body_mismatch": "machine",
8422
+ "app.check.request_failed": "machine",
8423
+ "app.check.usage": "machine",
8424
+ "gate.require.runtime": "machine",
8425
+ "gate.runtime.ok": "machine",
8426
+ "gate.runtime.reused": "machine",
8427
+ "gate.runtime.crashed": "machine",
8428
+ "gate.runtime.no_port": "machine",
8429
+ "gate.runtime.no_start": "machine",
8430
+ "gate.runtime.no_port_declared": "machine",
8431
+ "gate.runtime.wrong_status": "machine",
8432
+ "gate.runtime.body_mismatch": "machine",
8433
+ "gate.runtime.request_failed": "machine",
8434
+ "gate.runtime.orphan": "machine",
8435
+ "gate.runtime.probe_failed": "machine",
8436
+ "command.off.summary": "machine",
8437
+ "command.on.summary": "machine",
8438
+ "pause.off": "machine",
8439
+ "pause.on": "machine",
8440
+ "pause.report": "machine",
8441
+ "pause.no_session": "machine",
8442
+ "pause.error.person_only": "machine"
7918
8443
  }
7919
8444
  };
7920
8445
 
@@ -9294,9 +9819,9 @@ var isFlagged = (report2) => report2.spans.length > 0;
9294
9819
  var MARKDOWN = /\.(?:md|markdown|mdx)$/i;
9295
9820
  var RECORD_FILE = /(?:^|\/)(?:CHANGELOG|RELEASE[-_]?NOTES|HISTORY)[^/]*$/i;
9296
9821
  var contextOf = (file) => RECORD_FILE.test(toPosixPath(file)) ? "record" : "docs";
9297
- var listed = (runtime, root, args) => {
9298
- const result = runtime.git(args, root);
9299
- if (!result.ok) return reject(`git ${args.join(" ")} failed`);
9822
+ var listed = (runtime, root, args2) => {
9823
+ const result = runtime.git(args2, root);
9824
+ if (!result.ok) return reject(`git ${args2.join(" ")} failed`);
9300
9825
  return accept(result.stdout.split("\0").map(toPosixPath));
9301
9826
  };
9302
9827
  var changedMarkdown = (runtime, root) => {
@@ -9376,6 +9901,121 @@ var reviewCheck = (check2, ctx) => {
9376
9901
  return failed([...sizeNotes(ctx), note("gate.review.missing", { check: check2.id })]);
9377
9902
  };
9378
9903
 
9904
+ // src/app/parse.ts
9905
+ var KINDS2 = [
9906
+ "ok",
9907
+ "crashed",
9908
+ "no_port",
9909
+ "wrong_status",
9910
+ "body_mismatch",
9911
+ "request_failed",
9912
+ "no_start"
9913
+ ];
9914
+ var optionalNumber = (value) => typeof value === "number" ? value : void 0;
9915
+ var parseAppCheck = (stdout) => {
9916
+ const envelope = parseJsonObject(stdout.trim());
9917
+ if (!envelope.ok) return void 0;
9918
+ const data = envelope.value["data"];
9919
+ if (!isRecord(data)) return void 0;
9920
+ const kind = KINDS2.find((item) => item === data["kind"]);
9921
+ const { output, reused, stopped, durationMs, exitCode, bodyExcerpt } = data;
9922
+ if (kind === void 0 || typeof output !== "string" || typeof reused !== "boolean" || typeof stopped !== "boolean")
9923
+ return void 0;
9924
+ const status = optionalNumber(data["status"]);
9925
+ return {
9926
+ kind,
9927
+ output,
9928
+ reused,
9929
+ stopped,
9930
+ durationMs: optionalNumber(durationMs) ?? 0,
9931
+ ...status === void 0 ? {} : { status },
9932
+ ...typeof exitCode === "number" || exitCode === null ? { exitCode } : {},
9933
+ ...typeof bodyExcerpt === "string" ? { bodyExcerpt } : {}
9934
+ };
9935
+ };
9936
+
9937
+ // src/gate/checks/runtime.ts
9938
+ var PROBE_MARGIN_MS = 15e3;
9939
+ var MIN_REMAINING_MS2 = 5e3;
9940
+ var TAIL2 = 160;
9941
+ var lastLine = (text6) => {
9942
+ const rows = text6.split(/\r?\n/).filter((row) => row.trim() !== "" && !/^\s*at\s|^Node\.js v\d/.test(row));
9943
+ const line2 = (rows.findLast((row) => /error/i.test(row)) ?? rows.at(-1))?.trim() ?? "";
9944
+ return line2.length > TAIL2 ? `${line2.slice(0, TAIL2)}…` : line2;
9945
+ };
9946
+ var args = (check2, timeoutS) => [
9947
+ "app",
9948
+ "check",
9949
+ "--json",
9950
+ "--port",
9951
+ String(check2.port ?? 0),
9952
+ "--route",
9953
+ check2.route ?? DEFAULT_ROUTE,
9954
+ "--status",
9955
+ String(check2.status ?? DEFAULT_STATUS),
9956
+ "--timeout",
9957
+ String(timeoutS),
9958
+ ...check2.run === void 0 ? [] : ["--start", check2.run],
9959
+ ...check2.body === void 0 ? [] : ["--body", check2.body]
9960
+ ];
9961
+ var resultNotes = (check2, result, ctx) => {
9962
+ const route = check2.route ?? DEFAULT_ROUTE;
9963
+ const line2 = ctx.mask(lastLine(result.output));
9964
+ const output = line2 === "" ? [] : [note("gate.note.output", { line: line2 })];
9965
+ switch (result.kind) {
9966
+ case "ok":
9967
+ return [note(result.reused ? "gate.runtime.reused" : "gate.runtime.ok", { route, status: result.status ?? 0 })];
9968
+ case "crashed":
9969
+ return [note("gate.runtime.crashed", { code: result.exitCode ?? -1 }), ...output];
9970
+ case "no_port":
9971
+ return [note("gate.runtime.no_port", { port: check2.port ?? 0 }), ...output];
9972
+ case "no_start":
9973
+ return [note("gate.runtime.no_start", { port: check2.port ?? 0 })];
9974
+ case "wrong_status":
9975
+ return [
9976
+ note("gate.runtime.wrong_status", {
9977
+ route,
9978
+ status: result.status ?? 0,
9979
+ expected: check2.status ?? DEFAULT_STATUS
9980
+ })
9981
+ ];
9982
+ case "body_mismatch":
9983
+ return [note("gate.runtime.body_mismatch", { route, body: ctx.mask(check2.body ?? "") })];
9984
+ case "request_failed":
9985
+ return [note("gate.runtime.request_failed", { route, detail: ctx.mask(result.bodyExcerpt ?? "") })];
9986
+ }
9987
+ };
9988
+ var outcome = (check2, result, ctx) => {
9989
+ const notes2 = [
9990
+ ...resultNotes(check2, result, ctx),
9991
+ ...result.stopped ? [] : [note("gate.runtime.orphan", { port: check2.port ?? 0 })]
9992
+ ];
9993
+ const base = result.kind === "ok" && result.stopped ? passed(notes2) : failed(notes2);
9994
+ return {
9995
+ ...base,
9996
+ ...check2.run === void 0 ? {} : { command: check2.run },
9997
+ exitCode: result.exitCode ?? (result.kind === "ok" ? 0 : null),
9998
+ stdoutSha256: sha256Hex(result.output),
9999
+ durationMs: result.durationMs
10000
+ };
10001
+ };
10002
+ var runtimeCheck = (check2, ctx) => {
10003
+ if (check2.port === void 0) return failed([note("gate.runtime.no_port_declared")]);
10004
+ if (check2.run !== void 0 && !ctx.trusted)
10005
+ return { ...unverified("untrusted", [note("gate.note.untrusted", { task: ctx.contract.id })]), command: check2.run };
10006
+ const remaining = ctx.deadline - nowMs(ctx.runtime) - PROBE_MARGIN_MS;
10007
+ if (remaining < MIN_REMAINING_MS2) return unverified("budget", [note("gate.note.budget")]);
10008
+ const timeoutS = Math.max(1, Math.min(check2.timeoutS ?? DEFAULT_APP_TIMEOUT_S, Math.floor(remaining / 1e3)));
10009
+ const run4 = ctx.runtime.execSelf(args(check2, timeoutS), ctx.paths.root, timeoutS * 1e3 + PROBE_MARGIN_MS);
10010
+ if (run4.timedOut) return unverified("timeout", [note("gate.note.timeout", { seconds: timeoutS })]);
10011
+ const result = parseAppCheck(run4.stdout);
10012
+ if (result === void 0) {
10013
+ return failed([note("gate.runtime.probe_failed", { line: ctx.mask(lastLine(`${run4.stdout}
10014
+ ${run4.stderr}`)) })]);
10015
+ }
10016
+ return outcome(check2, result, ctx);
10017
+ };
10018
+
9379
10019
  // src/secrets/baseline.ts
9380
10020
  import { join as join18 } from "node:path";
9381
10021
  var BASELINE_FILE2 = ".finitemonkey/findings/baseline.json";
@@ -9670,15 +10310,15 @@ var diffFindings = (diff) => {
9670
10310
  return scanner.end().filter((finding3) => !SKIPPED.test(finding3.file));
9671
10311
  };
9672
10312
  var scanStaged = (runtime, root, includeUnstaged = false) => {
9673
- const args = includeUnstaged ? ["diff", "HEAD", ...DIFF_FLAGS] : ["diff", "--cached", ...DIFF_FLAGS];
9674
- const git = runtime.git(args, root);
10313
+ const args2 = includeUnstaged ? ["diff", "HEAD", ...DIFF_FLAGS] : ["diff", "--cached", ...DIFF_FLAGS];
10314
+ const git = runtime.git(args2, root);
9675
10315
  if (!git.ok) return reject(git.found ? "git diff failed" : "git not found");
9676
10316
  return accept(diffFindings(git.stdout));
9677
10317
  };
9678
10318
  var scanHistory = async (runtime, root) => {
9679
10319
  const scanner = diffScanner();
9680
- const args = ["log", "-p", "--all", "--format=commit %H", ...DIFF_FLAGS];
9681
- const git = await runtime.gitLines(args, root, scanner.push);
10320
+ const args2 = ["log", "-p", "--all", "--format=commit %H", ...DIFF_FLAGS];
10321
+ const git = await runtime.gitLines(args2, root, scanner.push);
9682
10322
  if (!git.ok) return reject(git.found ? "git log failed" : "git not found");
9683
10323
  return accept(scanner.end().filter((finding3) => !SKIPPED.test(finding3.file)));
9684
10324
  };
@@ -9694,11 +10334,11 @@ var secretsGateStatus = (runtime, root, since) => {
9694
10334
  if (!changed.ok) return changed;
9695
10335
  const baseline = loadBaseline(runtime, root);
9696
10336
  if (!baseline.ok) return baseline;
9697
- const outcome = ratchet([...changed.value, ...untracked2(runtime, root)], baseline.value, runtime.now(), void 0);
10337
+ const outcome2 = ratchet([...changed.value, ...untracked2(runtime, root)], baseline.value, runtime.now(), void 0);
9698
10338
  const openIncidents = listIncidents(runtime, root).filter(
9699
10339
  (incident) => incident.createdAt >= since && incident.state !== "rotacionado" && incident.state !== "fechado"
9700
10340
  );
9701
- return accept({ findings: [...outcome.fresh, ...outcome.expired], openIncidents });
10341
+ return accept({ findings: [...outcome2.fresh, ...outcome2.expired], openIncidents });
9702
10342
  };
9703
10343
 
9704
10344
  // src/gate/checks/secrets.ts
@@ -9770,8 +10410,8 @@ var testsCheck = (check2, ctx) => {
9770
10410
  const released2 = grant === void 0 ? {} : { releasedBy: `person@${grant.grantedAt}` };
9771
10411
  const releaseNotes = grant === void 0 ? [] : [...notes2, note("gate.note.released", { guard: "tests", reason: grant.reason })];
9772
10412
  if (check2.run === void 0) return { ...passed([note("gate.tests.clean"), ...releaseNotes]), ...released2 };
9773
- const outcome = runShellCheck(check2, check2.run, ctx);
9774
- return { ...outcome, notes: [...outcome.notes, ...releaseNotes], ...released2 };
10413
+ const outcome2 = runShellCheck(check2, check2.run, ctx);
10414
+ return { ...outcome2, notes: [...outcome2.notes, ...releaseNotes], ...released2 };
9775
10415
  };
9776
10416
 
9777
10417
  // src/git/tree.ts
@@ -9797,13 +10437,13 @@ var IDENTITY_ENV = {
9797
10437
  GIT_COMMITTER_EMAIL: "finitemonkey@localhost"
9798
10438
  };
9799
10439
  var failure = (step3, result) => result.timedOut ? `${step3}: timed out` : `${step3}: ${result.stderr.trim() || "git failed"}`;
9800
- var run = (runtime, repo, args, env, options, input) => {
9801
- const result = runtime.gitWith(args, repo.root, {
10440
+ var run = (runtime, repo, args2, env, options, input) => {
10441
+ const result = runtime.gitWith(args2, repo.root, {
9802
10442
  env,
9803
10443
  timeoutMs: options.timeoutMs,
9804
10444
  ...input === void 0 ? {} : { input }
9805
10445
  });
9806
- return result.ok ? accept(result.stdout.trim()) : reject(failure(args[0] ?? "git", result));
10446
+ return result.ok ? accept(result.stdout.trim()) : reject(failure(args2[0] ?? "git", result));
9807
10447
  };
9808
10448
  var dropSensitive = (runtime, repo, env, options) => {
9809
10449
  const listed4 = run(runtime, repo, ["ls-files", "-z"], env, options);
@@ -9866,7 +10506,7 @@ var flushHunk = (draft) => {
9866
10506
  if (draft.current !== void 0) draft.hunks.push(draft.current);
9867
10507
  draft.current = void 0;
9868
10508
  };
9869
- var finish = (draft, out) => {
10509
+ var finish2 = (draft, out) => {
9870
10510
  if (draft === void 0) return;
9871
10511
  flushHunk(draft);
9872
10512
  out.push({
@@ -9926,7 +10566,7 @@ var parseUnifiedDiff = (text6) => {
9926
10566
  const line2 = withoutCarriageReturn(raw);
9927
10567
  const start = DIFF_GIT.exec(line2);
9928
10568
  if (start !== null) {
9929
- finish(draft, out);
10569
+ finish2(draft, out);
9930
10570
  draft = {
9931
10571
  oldPath: start[1],
9932
10572
  newPath: start[2],
@@ -9937,7 +10577,7 @@ var parseUnifiedDiff = (text6) => {
9937
10577
  };
9938
10578
  } else if (draft !== void 0) bodyLine(draft, line2);
9939
10579
  }
9940
- finish(draft, out);
10580
+ finish2(draft, out);
9941
10581
  return out;
9942
10582
  };
9943
10583
  var DIFF_ARGS = [
@@ -10654,8 +11294,8 @@ var openRepo = (runtime, dir) => {
10654
11294
  };
10655
11295
  var globRegex2 = (pattern) => new RegExp(`^${pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`);
10656
11296
  var matchesBranch = (branch, patterns) => patterns.some((pattern) => globRegex2(pattern).test(branch));
10657
- var line = (runtime, root, args) => {
10658
- const result = runtime.git(args, root);
11297
+ var line = (runtime, root, args2) => {
11298
+ const result = runtime.git(args2, root);
10659
11299
  const value = result.stdout.trim();
10660
11300
  return result.ok && value !== "" ? value : void 0;
10661
11301
  };
@@ -10781,20 +11421,21 @@ var DEFAULT_CHECKS = {
10781
11421
  worktree: worktreeCheck,
10782
11422
  merge: mergeCheck,
10783
11423
  review: reviewCheck,
10784
- callers: callersCheck
11424
+ callers: callersCheck,
11425
+ runtime: runtimeCheck
10785
11426
  };
10786
11427
 
10787
11428
  // src/gate/run.ts
10788
11429
  var FAST_PATH_MS = 10 * 6e4;
10789
- var toEvidence = (ctx, check2, outcome) => ({
11430
+ var toEvidence = (ctx, check2, outcome2) => ({
10790
11431
  schema: EVIDENCE_SCHEMA,
10791
11432
  task: ctx.contract.id,
10792
11433
  check: check2.id,
10793
11434
  kind: check2.kind,
10794
11435
  treeHash: ctx.treeHash,
10795
11436
  at: ctx.runtime.now(),
10796
- ...outcome,
10797
- ...check2.run === void 0 || outcome.command !== void 0 ? {} : { command: check2.run }
11437
+ ...outcome2,
11438
+ ...check2.run === void 0 || outcome2.command !== void 0 ? {} : { command: check2.run }
10798
11439
  });
10799
11440
  var reusable = (ledger, ctx, check2) => {
10800
11441
  if (!REUSABLE_KINDS.includes(check2.kind)) return false;
@@ -10990,7 +11631,8 @@ var KNOWN_KINDS = /* @__PURE__ */ new Set([
10990
11631
  "worktree",
10991
11632
  "merge",
10992
11633
  "review",
10993
- "callers"
11634
+ "callers",
11635
+ "runtime"
10994
11636
  ]);
10995
11637
  var requirementText = (t, check2) => {
10996
11638
  const key = `gate.require.${check2.kind}`;
@@ -11023,9 +11665,9 @@ var subagentStart = (input) => {
11023
11665
  // src/gate/hooks/index.ts
11024
11666
  var PORTAO_HANDLERS = {
11025
11667
  PreToolUse: [selfGuard, bypassGuard, testsGuard],
11026
- UserPromptSubmit: [promptRelease],
11027
- Stop: [stopGate],
11028
- SubagentStop: [subagentStopGate],
11668
+ UserPromptSubmit: [pausePrompt, promptRelease],
11669
+ Stop: [unlessPaused(stopGate)],
11670
+ SubagentStop: [unlessPaused(subagentStopGate)],
11029
11671
  SubagentStart: [subagentStart],
11030
11672
  PreCompact: [preCompact],
11031
11673
  SessionStart: [sessionStart],
@@ -11183,10 +11825,10 @@ var HELM_READ = /* @__PURE__ */ new Set([
11183
11825
  "env"
11184
11826
  ]);
11185
11827
  var CURL_WRITE = /^(?:-X|--request|-d|--data.*|-F|--form|-T|--upload-file|-o|--output|-O|--remote-name)$/;
11186
- var firstPositional = (args, valueFlags = /* @__PURE__ */ new Set()) => {
11828
+ var firstPositional = (args2, valueFlags = /* @__PURE__ */ new Set()) => {
11187
11829
  const out = [];
11188
- for (let index = 0; index < args.length; index += 1) {
11189
- const arg = args[index] ?? "";
11830
+ for (let index = 0; index < args2.length; index += 1) {
11831
+ const arg = args2[index] ?? "";
11190
11832
  if (arg.startsWith("-")) {
11191
11833
  if (valueFlags.has(arg)) index += 1;
11192
11834
  } else out.push(arg);
@@ -11221,16 +11863,16 @@ var KUBE_VALUE_FLAGS = /* @__PURE__ */ new Set([
11221
11863
  "--container",
11222
11864
  "--kube-context"
11223
11865
  ]);
11224
- var dockerReadOnly = (args) => {
11225
- const words = firstPositional(args, DOCKER_VALUE_FLAGS);
11866
+ var dockerReadOnly = (args2) => {
11867
+ const words = firstPositional(args2, DOCKER_VALUE_FLAGS);
11226
11868
  const [first, second] = words;
11227
11869
  if (first === void 0) return true;
11228
11870
  if (DOCKER_GROUPS.has(first))
11229
11871
  return second === void 0 || DOCKER_READ.has(second) || first === "compose" && second === "top";
11230
11872
  return DOCKER_READ.has(first);
11231
11873
  };
11232
- var kubectlReadOnly = (program, args) => {
11233
- const [verb, object] = firstPositional(args, KUBE_VALUE_FLAGS);
11874
+ var kubectlReadOnly = (program, args2) => {
11875
+ const [verb, object] = firstPositional(args2, KUBE_VALUE_FLAGS);
11234
11876
  if (verb === void 0) return true;
11235
11877
  if (program === "helm") return HELM_READ.has(verb);
11236
11878
  if (verb === "config") return object === "view" || object === "get-contexts" || object === "current-context";
@@ -11240,24 +11882,24 @@ var kubectlReadOnly = (program, args) => {
11240
11882
  var writesFile = (command) => command.redirects.some(
11241
11883
  (redirect) => (redirect.op === ">" || redirect.op === ">>" || redirect.op === ">|") && !/^(?:\/dev\/null|&?\d)$/.test(redirect.target) && !redirect.op.startsWith("<")
11242
11884
  );
11243
- var gitReadOnly = (args) => {
11244
- const [verb, ...rest] = firstPositional(args, /* @__PURE__ */ new Set(["-C", "-c"]));
11885
+ var gitReadOnly = (args2) => {
11886
+ const [verb, ...rest] = firstPositional(args2, /* @__PURE__ */ new Set(["-C", "-c"]));
11245
11887
  if (verb === void 0) return true;
11246
11888
  if (GIT_READ.has(verb)) return true;
11247
11889
  return GIT_LIST.has(verb) && (rest.length === 0 || verb === "stash" && rest[0] === "list");
11248
11890
  };
11249
11891
  var programReadOnly = (command) => {
11250
- const { program, args } = command;
11251
- if (program === "git") return gitReadOnly(args);
11892
+ const { program, args: args2 } = command;
11893
+ if (program === "git") return gitReadOnly(args2);
11252
11894
  if (program === "docker" || program === "docker-compose")
11253
- return dockerReadOnly(program === "docker-compose" ? ["compose", ...args] : args);
11254
- if (program === "systemctl") return SYSTEMCTL_READ.has(firstPositional(args)[0] ?? "status");
11255
- if (program === "kubectl" || program === "helm") return kubectlReadOnly(program, args);
11256
- if (program === "curl") return !args.some((arg) => CURL_WRITE.test(arg));
11895
+ return dockerReadOnly(program === "docker-compose" ? ["compose", ...args2] : args2);
11896
+ if (program === "systemctl") return SYSTEMCTL_READ.has(firstPositional(args2)[0] ?? "status");
11897
+ if (program === "kubectl" || program === "helm") return kubectlReadOnly(program, args2);
11898
+ if (program === "curl") return !args2.some((arg) => CURL_WRITE.test(arg));
11257
11899
  if (program === "find")
11258
- return !args.some((arg) => arg === "-delete" || arg.startsWith("-exec") || arg.startsWith("-ok"));
11259
- if (program === "sed") return !args.some((arg) => arg.startsWith("-i") || arg === "--in-place");
11260
- if (program === "awk") return !args.some((arg) => arg.includes("system(") || arg.includes(">"));
11900
+ return !args2.some((arg) => arg === "-delete" || arg.startsWith("-exec") || arg.startsWith("-ok"));
11901
+ if (program === "sed") return !args2.some((arg) => arg.startsWith("-i") || arg === "--in-place");
11902
+ if (program === "awk") return !args2.some((arg) => arg.includes("system(") || arg.includes(">"));
11261
11903
  return READ_PROGRAMS.has(program);
11262
11904
  };
11263
11905
  var readOnlyCommand = (command) => !writesFile(command) && programReadOnly(command);
@@ -11317,11 +11959,11 @@ var RSYNC_LONG_VALUE = /* @__PURE__ */ new Set([
11317
11959
  "--files-from"
11318
11960
  ]);
11319
11961
  var parseSsh = (command) => {
11320
- const { args } = command;
11962
+ const { args: args2 } = command;
11321
11963
  let index = 0;
11322
11964
  let noCommand = false;
11323
- while (index < args.length && (args[index] ?? "").startsWith("-")) {
11324
- const arg = args[index] ?? "";
11965
+ while (index < args2.length && (args2[index] ?? "").startsWith("-")) {
11966
+ const arg = args2[index] ?? "";
11325
11967
  if (arg === "--") {
11326
11968
  index += 1;
11327
11969
  break;
@@ -11330,17 +11972,17 @@ var parseSsh = (command) => {
11330
11972
  const last = arg.charAt(arg.length - 1);
11331
11973
  index += arg.length === 2 && SSH_VALUE.includes(last) ? 2 : 1;
11332
11974
  }
11333
- const destination = args[index];
11975
+ const destination = args2[index];
11334
11976
  if (destination === void 0) return void 0;
11335
- return { destination: destination.replace(/^ssh:\/\//, ""), remote: args.slice(index + 1), noCommand };
11977
+ return { destination: destination.replace(/^ssh:\/\//, ""), remote: args2.slice(index + 1), noCommand };
11336
11978
  };
11337
11979
  var REMOTE_PATH = /^(?:(?:scp|rsync|sftp):\/\/)?(?:[^@/:\s]+@)?(\[[^\]]+\]|[A-Za-z0-9][A-Za-z0-9.-]*)(?::|::|\/)/;
11338
11980
  var remoteHostOf = (operand) => {
11339
11981
  if (/^[A-Za-z]:[\\/]/.test(operand) || operand.startsWith("/") || operand.startsWith(".")) return void 0;
11340
- const url = /^(?:scp|rsync|sftp):\/\//.test(operand);
11982
+ const url2 = /^(?:scp|rsync|sftp):\/\//.test(operand);
11341
11983
  const match = REMOTE_PATH.exec(operand);
11342
11984
  if (match === null) return void 0;
11343
- if (!url && !operand.slice(match[0].length - 1).startsWith(":")) return void 0;
11985
+ if (!url2 && !operand.slice(match[0].length - 1).startsWith(":")) return void 0;
11344
11986
  return (match[1] ?? "").replace(/^\[|\]$/g, "");
11345
11987
  };
11346
11988
  var copyOperands = (command, shortValue) => {
@@ -11445,12 +12087,12 @@ var copyHit = (command, isProd) => {
11445
12087
  const source = call.sources.map(remoteHostOf).find((host) => host !== void 0 && isProd(host));
11446
12088
  return source === void 0 ? void 0 : hit2("read", hostKey(source), command.program);
11447
12089
  };
11448
- var valueAfter = (args, ...names) => {
11449
- for (let index = 0; index < args.length; index += 1) {
11450
- const arg = args[index] ?? "";
12090
+ var valueAfter = (args2, ...names) => {
12091
+ for (let index = 0; index < args2.length; index += 1) {
12092
+ const arg = args2[index] ?? "";
11451
12093
  const equals = names.find((name) => arg.startsWith(`${name}=`));
11452
12094
  if (equals !== void 0) return arg.slice(equals.length + 1);
11453
- if (names.includes(arg)) return args[index + 1];
12095
+ if (names.includes(arg)) return args2[index + 1];
11454
12096
  }
11455
12097
  return void 0;
11456
12098
  };
@@ -11492,8 +12134,8 @@ var DUMPS = /* @__PURE__ */ new Set(["pg_dump", "mysqldump"]);
11492
12134
  var databaseHost = (command) => {
11493
12135
  const flag2 = valueAfter(command.args, "-h", "--host");
11494
12136
  if (flag2 !== void 0) return flag2;
11495
- const url = [...command.args, command.env["DATABASE_URL"] ?? ""].map((arg) => URL_HOST.exec(arg)?.[1]).find((host) => host !== void 0);
11496
- return url ?? command.env["PGHOST"];
12137
+ const url2 = [...command.args, command.env["DATABASE_URL"] ?? ""].map((arg) => URL_HOST.exec(arg)?.[1]).find((host) => host !== void 0);
12138
+ return url2 ?? command.env["PGHOST"];
11497
12139
  };
11498
12140
  var databaseReadOnly = (command) => {
11499
12141
  if (DUMPS.has(command.program)) return true;
@@ -11667,14 +12309,14 @@ var globalOption = (globals, name, value) => {
11667
12309
  var gitInvocation = (command) => {
11668
12310
  if (command.program !== "git") return void 0;
11669
12311
  const globals = { dirs: [], config: {} };
11670
- const { args } = command;
12312
+ const { args: args2 } = command;
11671
12313
  let index = 0;
11672
- while (index < args.length && (args[index] ?? "").startsWith("-")) {
11673
- const arg = args[index] ?? "";
12314
+ while (index < args2.length && (args2[index] ?? "").startsWith("-")) {
12315
+ const arg = args2[index] ?? "";
11674
12316
  const equals = arg.indexOf("=");
11675
12317
  const name = equals > 2 && arg.startsWith("--") ? arg.slice(0, equals) : arg;
11676
12318
  if (WITH_VALUE.has(name) && equals < 0) {
11677
- globalOption(globals, name, args[index + 1] ?? "");
12319
+ globalOption(globals, name, args2[index + 1] ?? "");
11678
12320
  index += 2;
11679
12321
  } else {
11680
12322
  if (equals > 0) globalOption(globals, name, arg.slice(equals + 1));
@@ -11684,8 +12326,8 @@ var gitInvocation = (command) => {
11684
12326
  return {
11685
12327
  dirs: globals.dirs,
11686
12328
  config: globals.config,
11687
- subcommand: args[index] ?? "",
11688
- args: args.slice(index + 1),
12329
+ subcommand: args2[index] ?? "",
12330
+ args: args2.slice(index + 1),
11689
12331
  command
11690
12332
  };
11691
12333
  };
@@ -11819,14 +12461,14 @@ var shortCluster = (building, arg, next2, spec2) => {
11819
12461
  }
11820
12462
  return 1;
11821
12463
  };
11822
- var parseOptions = (args, spec2) => {
12464
+ var parseOptions = (args2, spec2) => {
11823
12465
  const building = { flags: /* @__PURE__ */ new Set(), values: /* @__PURE__ */ new Map(), positionals: [] };
11824
12466
  let index = 0;
11825
- while (index < args.length) {
11826
- const arg = args[index] ?? "";
11827
- const next2 = args[index + 1];
12467
+ while (index < args2.length) {
12468
+ const arg = args2[index] ?? "";
12469
+ const next2 = args2[index + 1];
11828
12470
  if (arg === "--") {
11829
- return { ...building, paths: args.slice(index + 1), dashdash: true };
12471
+ return { ...building, paths: args2.slice(index + 1), dashdash: true };
11830
12472
  }
11831
12473
  if (arg.startsWith("--")) index += longOption(building, arg, next2, spec2);
11832
12474
  else if (arg.startsWith("-") && arg.length > 1 && !/^-\d+$/.test(arg)) {
@@ -12264,9 +12906,9 @@ var gitVerdict = (invocation, facts) => {
12264
12906
 
12265
12907
  // src/git/switch-target.ts
12266
12908
  var CREATES = /* @__PURE__ */ new Set(["-c", "-C", "-b", "-B", "--create", "--force-create", "--orphan"]);
12267
- var operands = (args) => {
12268
- if (args.includes("--")) return void 0;
12269
- return args.filter((arg) => !arg.startsWith("-"));
12909
+ var operands = (args2) => {
12910
+ if (args2.includes("--")) return void 0;
12911
+ return args2.filter((arg) => !arg.startsWith("-"));
12270
12912
  };
12271
12913
  var switchTarget = (invocation, facts) => {
12272
12914
  if (invocation.subcommand !== "switch" && invocation.subcommand !== "checkout") return void 0;
@@ -12460,8 +13102,8 @@ var BACKUP_PREFIX = "refs/finitemonkey/backup/";
12460
13102
  var DEFAULT_SNAPSHOT_TIMEOUT_MS = 2500;
12461
13103
  var stampOf = (iso) => iso.replace(/[:.]/g, "-");
12462
13104
  var slug = (op) => op.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "manual";
12463
- var optionalLine = (runtime, repo, args) => {
12464
- const result = runtime.git(args, repo.root);
13105
+ var optionalLine = (runtime, repo, args2) => {
13106
+ const result = runtime.git(args2, repo.root);
12465
13107
  const value = result.stdout.trim();
12466
13108
  return result.ok && value !== "" ? value : void 0;
12467
13109
  };
@@ -12566,17 +13208,17 @@ var fileList = (items) => {
12566
13208
  return items.length > MAX_FILES2 ? `${head} (+${String(items.length - MAX_FILES2)})` : head;
12567
13209
  };
12568
13210
  var foreignLabel = (t, file) => t(file.reason === "before-session" ? "git.foreign.before" : "git.foreign.other", { path: file.path });
12569
- var outcomeMessage = (t, outcome) => {
12570
- switch (outcome.kind) {
13211
+ var outcomeMessage = (t, outcome2) => {
13212
+ switch (outcome2.kind) {
12571
13213
  case "block":
12572
- return ruleMessage(t, outcome.rule, outcome.command, outcome.subject);
13214
+ return ruleMessage(t, outcome2.rule, outcome2.command, outcome2.subject);
12573
13215
  case "foreign":
12574
13216
  return t("git.block.foreign", {
12575
- command: outcome.command,
12576
- files: fileList(outcome.files.map((file) => foreignLabel(t, file)))
13217
+ command: outcome2.command,
13218
+ files: fileList(outcome2.files.map((file) => foreignLabel(t, file)))
12577
13219
  });
12578
13220
  case "unknown":
12579
- return t("git.block.unknown", { command: outcome.command });
13221
+ return t("git.block.unknown", { command: outcome2.command });
12580
13222
  case "pass":
12581
13223
  case "snapshot":
12582
13224
  return void 0;
@@ -12620,10 +13262,10 @@ var review = (input, commands, owner) => {
12620
13262
  const decision = PROD_PROGRAMS.has(command.program) ? prod().decide(command) : void 0;
12621
13263
  if (decision?.kind === "block") return { blocked: decision.reason, snapshots, reads };
12622
13264
  if (decision !== void 0) reads.push(decision);
12623
- for (const outcome of guardGitCommand({ runtime: input.runtime, cwd: input.cwd, owner, chain }, command)) {
12624
- const message = outcomeMessage(input.t, outcome);
13265
+ for (const outcome2 of guardGitCommand({ runtime: input.runtime, cwd: input.cwd, owner, chain }, command)) {
13266
+ const message = outcomeMessage(input.t, outcome2);
12625
13267
  if (message !== void 0) return { blocked: message, snapshots, reads };
12626
- if (outcome.kind === "snapshot") snapshots.push(outcome);
13268
+ if (outcome2.kind === "snapshot") snapshots.push(outcome2);
12627
13269
  }
12628
13270
  }
12629
13271
  return { blocked: void 0, snapshots, reads };
@@ -12746,6 +13388,7 @@ var recordShadow = (input, paths, set, policy, reason) => {
12746
13388
  var governed = (set, handler) => async (input) => {
12747
13389
  const paths = projectPaths(input);
12748
13390
  if (paths === void 0) return handler(input);
13391
+ if (pausedFor(input.runtime, paths, input.payload.sessionId) !== void 0) return { kind: "allow" };
12749
13392
  const policy = loadPolicy(input.runtime, paths.root);
12750
13393
  const mode = modeOf2(set, policy, readShadow(input.runtime, paths));
12751
13394
  if (mode === "off") return { kind: "allow" };
@@ -12869,7 +13512,7 @@ var addsMarker = (input, tool, inputs, file) => {
12869
13512
  return false;
12870
13513
  }
12871
13514
  };
12872
- var isReadOnly = (program, args) => READ_ONLY.has(program) || program === "git" && GIT_READ_ONLY.has(args[0] ?? "");
13515
+ var isReadOnly = (program, args2) => READ_ONLY.has(program) || program === "git" && GIT_READ_ONLY.has(args2[0] ?? "");
12873
13516
  var shellAddsMarker = (command) => markerCount(command) > 0 && shellWords(command).segments.some((segment) => !isReadOnly(segment.program, segment.args));
12874
13517
  var shown2 = (cwd, file) => {
12875
13518
  const rel = toPosixPath(relative6(cwd, file));
@@ -12907,17 +13550,17 @@ var GIT_FLAGS = { title: [], body: ["-m", "--message"], file: ["-F", "--file"] }
12907
13550
  var GH_FLAGS = { title: ["-t", "--title"], body: ["-b", "--body"], file: ["-F", "--body-file"] };
12908
13551
  var GIT_GLOBAL_WITH_VALUE = /* @__PURE__ */ new Set(["-C", "-c", "--git-dir", "--work-tree", "--namespace"]);
12909
13552
  var CLUSTER_WITH_MESSAGE = /^-[a-zA-Z]*m$/;
12910
- var flagValues = (args, spec2) => {
13553
+ var flagValues = (args2, spec2) => {
12911
13554
  const known2 = [...spec2.title, ...spec2.body, ...spec2.file];
12912
13555
  const out = [];
12913
- for (let index = 0; index < args.length; index += 1) {
12914
- const arg = args[index] ?? "";
13556
+ for (let index = 0; index < args2.length; index += 1) {
13557
+ const arg = args2[index] ?? "";
12915
13558
  const equals = arg.indexOf("=");
12916
13559
  const name = arg.startsWith("--") && equals > 0 ? arg.slice(0, equals) : arg;
12917
13560
  if (name !== arg && known2.includes(name)) {
12918
13561
  out.push({ flag: name, value: arg.slice(equals + 1) });
12919
13562
  } else if (known2.includes(arg) || spec2 === GIT_FLAGS && CLUSTER_WITH_MESSAGE.test(arg)) {
12920
- out.push({ flag: known2.includes(arg) ? arg : "-m", value: args[index + 1] ?? "" });
13563
+ out.push({ flag: known2.includes(arg) ? arg : "-m", value: args2[index + 1] ?? "" });
12921
13564
  index += 1;
12922
13565
  }
12923
13566
  }
@@ -12931,17 +13574,17 @@ var sourceOf = (value, isFile, segment, heredocs) => {
12931
13574
  const piped = segment.args.map((arg) => heredocIn(arg, heredocs)).find((text6) => text6 !== void 0);
12932
13575
  return { kind: "inline", text: piped ?? "" };
12933
13576
  };
12934
- var collect3 = (target, segment, args, spec2, heredocs) => {
12935
- const values = flagValues(args, spec2);
13577
+ var collect3 = (target, segment, args2, spec2, heredocs) => {
13578
+ const values = flagValues(args2, spec2);
12936
13579
  if (values.length === 0) return void 0;
12937
13580
  const title = values.find((item) => spec2.title.includes(item.flag));
12938
13581
  const bodies = values.filter((item) => !spec2.title.includes(item.flag)).map((item) => sourceOf(item.value, spec2.file.includes(item.flag), segment, heredocs));
12939
13582
  return { target, title: title === void 0 ? void 0 : sourceOf(title.value, false, segment, heredocs), bodies };
12940
13583
  };
12941
- var gitSubcommand = (args) => {
13584
+ var gitSubcommand = (args2) => {
12942
13585
  let index = 0;
12943
- while (index < args.length && (args[index] ?? "").startsWith("-")) {
12944
- index += GIT_GLOBAL_WITH_VALUE.has(args[index] ?? "") ? 2 : 1;
13586
+ while (index < args2.length && (args2[index] ?? "").startsWith("-")) {
13587
+ index += GIT_GLOBAL_WITH_VALUE.has(args2[index] ?? "") ? 2 : 1;
12945
13588
  }
12946
13589
  return index;
12947
13590
  };
@@ -13157,7 +13800,7 @@ var READERS = /* @__PURE__ */ new Set([
13157
13800
  var SECRET_NAME = /(?:KEY|SECRET|TOKEN|PASSWORD|PASSWD|PASS|PWD|CREDENTIAL)/i;
13158
13801
  var PERSON_ONLY = /\b(?:finitemonkey|fm)(?:\.mjs)?\s+secrets\s+(?:accept\b|incident\s+(?:rotated|close)\b)/;
13159
13802
  var LISTS_ENV = /* @__PURE__ */ new Set(["get-childitem", "gci", "dir", "ls"]);
13160
- var onlyFlags = (args) => args.every((arg) => arg.startsWith("-"));
13803
+ var onlyFlags = (args2) => args2.every((arg) => arg.startsWith("-"));
13161
13804
  var readTarget = (segment) => {
13162
13805
  const redirected = segment.redirectedFrom.find(isSensitivePath);
13163
13806
  if (redirected !== void 0) return redirected;
@@ -13291,17 +13934,17 @@ var secretsPreToolUse = (input) => {
13291
13934
  // src/secrets/remotes.ts
13292
13935
  var USER_AND_SECRET = /^[a-z][a-z0-9+.-]*:\/\/[^\s/@:]+:[^\s/@]+@/i;
13293
13936
  var USERINFO = /^[a-z][a-z0-9+.-]*:\/\/(?<user>[^\s/@]+)@/i;
13294
- var hasCredential = (url) => {
13295
- if (USER_AND_SECRET.test(url)) return true;
13296
- const user = USERINFO.exec(url)?.groups?.["user"];
13937
+ var hasCredential = (url2) => {
13938
+ if (USER_AND_SECRET.test(url2)) return true;
13939
+ const user = USERINFO.exec(url2)?.groups?.["user"];
13297
13940
  return user !== void 0 && scanText(user, { rules: CREDENTIAL_RULES }).length > 0;
13298
13941
  };
13299
13942
  var parseRemotes = (output) => {
13300
13943
  const remotes = /* @__PURE__ */ new Map();
13301
13944
  for (const line2 of output.split(/\r?\n/)) {
13302
- const [name, url] = line2.trim().split(/\s+/);
13303
- if (name === void 0 || url === void 0 || name === "") continue;
13304
- remotes.set(name, [...remotes.get(name) ?? [], url]);
13945
+ const [name, url2] = line2.trim().split(/\s+/);
13946
+ if (name === void 0 || url2 === void 0 || name === "") continue;
13947
+ remotes.set(name, [...remotes.get(name) ?? [], url2]);
13305
13948
  }
13306
13949
  return remotes;
13307
13950
  };
@@ -13550,9 +14193,9 @@ var invocationOf = (entry2, vars) => {
13550
14193
  const seconds = typeof timeout === "number" && timeout > 0 ? timeout : DEFAULT_TIMEOUT_S;
13551
14194
  const timeoutMs = Math.min(seconds * 1e3, PROBE_TIMEOUT_MS2);
13552
14195
  const command = expand(raw, vars);
13553
- const args = strings3(entry2.handler["args"]).map((arg) => expand(arg, vars));
13554
- const shell = args.length === 0;
13555
- return { command, args, shell, timeoutMs, display: [command, ...args].join(" ") };
14196
+ const args2 = strings3(entry2.handler["args"]).map((arg) => expand(arg, vars));
14197
+ const shell = args2.length === 0;
14198
+ return { command, args: args2, shell, timeoutMs, display: [command, ...args2].join(" ") };
13556
14199
  };
13557
14200
  var versionInvocation = (invocation) => {
13558
14201
  if (invocation.shell) {
@@ -13561,8 +14204,8 @@ var versionInvocation = (invocation) => {
13561
14204
  }
13562
14205
  const at = invocation.args.indexOf("hook");
13563
14206
  if (at < 0) return void 0;
13564
- const args = [...invocation.args.slice(0, at), "version", "--json"];
13565
- return { ...invocation, args, display: [invocation.command, ...args].join(" ") };
14207
+ const args2 = [...invocation.args.slice(0, at), "version", "--json"];
14208
+ return { ...invocation, args: args2, display: [invocation.command, ...args2].join(" ") };
13566
14209
  };
13567
14210
  var syntheticPayload = (event, cwd) => {
13568
14211
  const base = { session_id: "finitemonkey-doctor", transcript_path: "", cwd, hook_event_name: event };
@@ -13763,10 +14406,10 @@ var handlerProblems = (at, handler, claude) => {
13763
14406
  const type = handler["type"];
13764
14407
  if (typeof type !== "string" || !HANDLER_TYPES.some((known2) => known2 === type)) return [`${at}.type: unknown`];
13765
14408
  const problems = [];
13766
- const { command, args, timeout, onFailure } = handler;
14409
+ const { command, args: args2, timeout, onFailure } = handler;
13767
14410
  if (type === "command" && (typeof command !== "string" || command.trim() === ""))
13768
14411
  problems.push(`${at}.command: empty`);
13769
- if (args !== void 0 && !(Array.isArray(args) && args.every((arg) => typeof arg === "string"))) {
14412
+ if (args2 !== void 0 && !(Array.isArray(args2) && args2.every((arg) => typeof arg === "string"))) {
13770
14413
  problems.push(`${at}.args: not a list of strings`);
13771
14414
  }
13772
14415
  if (timeout !== void 0 && !(typeof timeout === "number" && timeout > 0))
@@ -14185,9 +14828,9 @@ var CHECKS = [
14185
14828
  ];
14186
14829
  var probeOnce = (probe) => {
14187
14830
  const seen = /* @__PURE__ */ new Map();
14188
- return (command, args, timeoutMs) => {
14189
- const key = [command, ...args].join(" ");
14190
- const known2 = seen.get(key) ?? probe(command, args, timeoutMs);
14831
+ return (command, args2, timeoutMs) => {
14832
+ const key = [command, ...args2].join(" ");
14833
+ const known2 = seen.get(key) ?? probe(command, args2, timeoutMs);
14191
14834
  seen.set(key, known2);
14192
14835
  return known2;
14193
14836
  };
@@ -14298,93 +14941,7 @@ var doctorCommand = {
14298
14941
  usage: "finitemonkey doctor [--fix] [--report] [--dry-run] [--json]",
14299
14942
  status: "ready",
14300
14943
  options: { fix: { type: "boolean" }, report: { type: "boolean" } },
14301
- run: (ctx, args) => Promise.resolve(runDoctor(ctx, args.values["fix"] === true, args.values["report"] === true))
14302
- };
14303
-
14304
- // src/core/args.ts
14305
- import { parseArgs } from "node:util";
14306
-
14307
- // src/core/problem.ts
14308
- var usageProblem = (code, messageKey, fix, params2 = {}) => ({
14309
- code,
14310
- messageKey,
14311
- params: params2,
14312
- fix,
14313
- exitCode: ExitCode.Usage
14314
- });
14315
- var needsPersonProblem = (messageKey, fix, params2 = {}) => ({
14316
- code: "needs_confirmation",
14317
- messageKey,
14318
- params: params2,
14319
- fix,
14320
- exitCode: ExitCode.NeedsPerson
14321
- });
14322
- var FmError = class extends Error {
14323
- problem;
14324
- constructor(problem4) {
14325
- super(problem4.code);
14326
- this.name = "FmError";
14327
- this.problem = problem4;
14328
- }
14329
- };
14330
-
14331
- // src/core/args.ts
14332
- var GLOBAL_OPTIONS = {
14333
- json: { type: "boolean" },
14334
- lang: { type: "string" },
14335
- yes: { type: "boolean", short: "y" },
14336
- "dry-run": { type: "boolean" },
14337
- version: { type: "boolean", short: "v" },
14338
- help: { type: "boolean", short: "h" }
14339
- };
14340
- var flag = (values, name) => values[name] === true;
14341
- var looseArgs = (argv) => {
14342
- const { values, positionals } = parseArgs({
14343
- args: [...argv],
14344
- options: GLOBAL_OPTIONS,
14345
- strict: false,
14346
- allowPositionals: true
14347
- });
14348
- const lang = values.lang;
14349
- return {
14350
- positionals,
14351
- json: flag(values, "json"),
14352
- yes: flag(values, "yes"),
14353
- dryRun: flag(values, "dry-run"),
14354
- version: flag(values, "version"),
14355
- help: flag(values, "help"),
14356
- lang: typeof lang === "string" ? lang : void 0
14357
- };
14358
- };
14359
- var resolveCommand = (registry, positionals) => registry.filter((spec2) => spec2.path.every((part2, index) => positionals[index] === part2)).reduce(
14360
- (best, spec2) => best === void 0 || spec2.path.length > best.consumed ? { spec: spec2, consumed: spec2.path.length } : best,
14361
- void 0
14362
- );
14363
- var strictArgs = (argv, spec2) => {
14364
- const fix = `finitemonkey help ${spec2.path.join(" ")}`;
14365
- let parsed;
14366
- try {
14367
- parsed = parseArgs({
14368
- args: [...argv],
14369
- options: { ...GLOBAL_OPTIONS, ...spec2.options },
14370
- strict: true,
14371
- allowPositionals: true
14372
- });
14373
- } catch (error) {
14374
- const detail = error instanceof Error ? error.message : String(error);
14375
- return reject(usageProblem("invalid_arguments", "error.invalid_arguments", fix, { detail }));
14376
- }
14377
- const rest = parsed.positionals.slice(spec2.path.length);
14378
- const max = spec2.maxPositionals ?? 0;
14379
- const extra = rest[max];
14380
- if (extra !== void 0) {
14381
- return reject(usageProblem("unexpected_argument", "error.unexpected_argument", fix, { argument: extra }));
14382
- }
14383
- return accept({ values: parsed.values, positionals: rest });
14384
- };
14385
- var stringOption = (values, name) => {
14386
- const value = values[name];
14387
- return typeof value === "string" ? value : void 0;
14944
+ run: (ctx, args2) => Promise.resolve(runDoctor(ctx, args2.values["fix"] === true, args2.values["report"] === true))
14388
14945
  };
14389
14946
 
14390
14947
  // src/gate/baseline-run.ts
@@ -14399,8 +14956,8 @@ var runBaseline = (options) => {
14399
14956
  if (commands.length === 0) return { kind: "skipped", reason: "no_commands" };
14400
14957
  const checks = {};
14401
14958
  for (const check2 of commands) {
14402
- const outcome = runShellCheck(check2, check2.run ?? "", ctx);
14403
- checks[check2.id] = { exitCode: outcome.exitCode ?? null, failures: outcome.failures ?? [] };
14959
+ const outcome2 = runShellCheck(check2, check2.run ?? "", ctx);
14960
+ checks[check2.id] = { exitCode: outcome2.exitCode ?? null, failures: outcome2.failures ?? [] };
14404
14961
  }
14405
14962
  if (treeHash(gitAt(ctx.runtime, ctx.paths.root)) !== ctx.treeHash) return { kind: "skipped", reason: "tree_moved" };
14406
14963
  writeBaseline(ctx.runtime, ctx.paths, ctx.treeHash, checks);
@@ -14459,19 +15016,19 @@ var gateCommand = {
14459
15016
  baseline: { type: "boolean" },
14460
15017
  prs: { type: "boolean" }
14461
15018
  },
14462
- run: (ctx, args) => {
14463
- const taskId = stringOption(args.values, "task");
15019
+ run: (ctx, args2) => {
15020
+ const taskId = stringOption(args2.values, "task");
14464
15021
  const options = {
14465
15022
  runtime: ctx.runtime,
14466
15023
  cwd: ctx.cwd,
14467
15024
  env: ctx.env,
14468
15025
  sessionId: void 0,
14469
- budgetMs: budgetMs(stringOption(args.values, "budget")),
14470
- fast: args.values["fast"] === true,
15026
+ budgetMs: budgetMs(stringOption(args2.values, "budget")),
15027
+ fast: args2.values["fast"] === true,
14471
15028
  ...taskId === void 0 ? {} : { taskId }
14472
15029
  };
14473
- if (args.values["prs"] === true) return Promise.resolve(prsResult(ctx));
14474
- if (args.values["baseline"] === true) {
15030
+ if (args2.values["prs"] === true) return Promise.resolve(prsResult(ctx));
15031
+ if (args2.values["baseline"] === true) {
14475
15032
  const baseline = runBaseline(options);
14476
15033
  return Promise.resolve(
14477
15034
  success(baseline, [
@@ -14481,7 +15038,7 @@ var gateCommand = {
14481
15038
  ])
14482
15039
  );
14483
15040
  }
14484
- return Promise.resolve(runResult(ctx, runGate(options), args.values["strict"] === true));
15041
+ return Promise.resolve(runResult(ctx, runGate(options), args2.values["strict"] === true));
14485
15042
  }
14486
15043
  };
14487
15044
 
@@ -14562,11 +15119,11 @@ var mergeVerifyCommand = {
14562
15119
  status: "ready",
14563
15120
  options: { since: { type: "string" }, dropped: { type: "string" } },
14564
15121
  maxPositionals: 1,
14565
- run: (ctx, args) => {
15122
+ run: (ctx, args2) => {
14566
15123
  const repo = repoOf2(ctx);
14567
15124
  if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
14568
- const dropped = stringOption(args.values, "dropped");
14569
- const targets2 = targetsOf2(ctx, repo, args.positionals[0], stringOption(args.values, "since"));
15125
+ const dropped = stringOption(args2.values, "dropped");
15126
+ const targets2 = targetsOf2(ctx, repo, args2.positionals[0], stringOption(args2.values, "since"));
14570
15127
  if (targets2.length === 0) {
14571
15128
  const data = { schema: MERGE_VERIFY_RUN_SCHEMA, merges: [] };
14572
15129
  return Promise.resolve(success(data, [ctx.t("git.merge.none", { detail: "HEAD" })]));
@@ -14674,11 +15231,11 @@ var rescueCommand = {
14674
15231
  usage: "finitemonkey rescue [--restore <sha> --branch <name>] [--json]",
14675
15232
  status: "ready",
14676
15233
  options: { restore: { type: "string" }, branch: { type: "string" } },
14677
- run: (ctx, args) => {
15234
+ run: (ctx, args2) => {
14678
15235
  const repo = repoOf2(ctx);
14679
15236
  if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
14680
- const sha = stringOption(args.values, "restore");
14681
- const branch = stringOption(args.values, "branch");
15237
+ const sha = stringOption(args2.values, "restore");
15238
+ const branch = stringOption(args2.values, "branch");
14682
15239
  if (sha === void 0 && branch === void 0) {
14683
15240
  const report2 = rescueReport(ctx.runtime, repo);
14684
15241
  return Promise.resolve(success({ schema: RESCUE_SCHEMA, action: "list", ...report2 }, listing(ctx.t, report2)));
@@ -14709,8 +15266,8 @@ var rescueCommand = {
14709
15266
  // src/git/undo.ts
14710
15267
  import { basename as basename3, dirname as dirname8, join as join36 } from "node:path";
14711
15268
  var INDEX_SUBJECT = "finitemonkey index:";
14712
- var gitLine = (runtime, repo, args) => {
14713
- const result = runtime.git(args, repo.root);
15269
+ var gitLine = (runtime, repo, args2) => {
15270
+ const result = runtime.git(args2, repo.root);
14714
15271
  const value = result.stdout.trim();
14715
15272
  return result.ok && value !== "" ? value : void 0;
14716
15273
  };
@@ -14758,9 +15315,9 @@ var intoWorktree = (runtime, repo, view2) => {
14758
15315
  const added = runtime.gitWith(["worktree", "add", "--detach", path, view2.commit], repo.root, {});
14759
15316
  return added.ok ? accept(path) : reject(failure2("worktree add", added));
14760
15317
  };
14761
- var step2 = (runtime, repo, args) => {
14762
- const result = runtime.gitWith(args, repo.root, {});
14763
- return result.ok ? accept(void 0) : reject(failure2(args.join(" "), result));
15318
+ var step2 = (runtime, repo, args2) => {
15319
+ const result = runtime.gitWith(args2, repo.root, {});
15320
+ return result.ok ? accept(void 0) : reject(failure2(args2.join(" "), result));
14764
15321
  };
14765
15322
  var inPlace = (runtime, repo, view2) => {
14766
15323
  const current2 = gitLine(runtime, repo, ["rev-parse", "--verify", "--quiet", "HEAD"]);
@@ -14793,10 +15350,10 @@ var snapshotCommand = {
14793
15350
  usage: "finitemonkey snapshot [--message <text>] [--json]",
14794
15351
  status: "ready",
14795
15352
  options: { message: { type: "string", short: "m" } },
14796
- run: (ctx, args) => {
15353
+ run: (ctx, args2) => {
14797
15354
  const repo = repoOf2(ctx);
14798
15355
  if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
14799
- const message = args.values["message"];
15356
+ const message = args2.values["message"];
14800
15357
  const op = typeof message === "string" && message.trim() !== "" ? message.trim() : "manual";
14801
15358
  const taken = takeSnapshot(ctx.runtime, repo, { op });
14802
15359
  if (!taken.ok) {
@@ -14826,12 +15383,12 @@ var undoCommand = {
14826
15383
  status: "ready",
14827
15384
  options: { list: { type: "boolean" }, "in-place": { type: "boolean" } },
14828
15385
  maxPositionals: 1,
14829
- run: (ctx, args) => {
15386
+ run: (ctx, args2) => {
14830
15387
  const repo = repoOf2(ctx);
14831
15388
  if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
14832
15389
  const views = snapshotViews(ctx.runtime, repo);
14833
- if (args.values["list"] === true) return Promise.resolve(listResult(ctx, views));
14834
- const wanted = args.positionals[0];
15390
+ if (args2.values["list"] === true) return Promise.resolve(listResult(ctx, views));
15391
+ const wanted = args2.positionals[0];
14835
15392
  const view2 = findSnapshot(views, wanted);
14836
15393
  if (view2 === void 0) {
14837
15394
  return Promise.resolve(
@@ -14844,7 +15401,7 @@ var undoCommand = {
14844
15401
  })
14845
15402
  );
14846
15403
  }
14847
- const inPlace2 = args.values["in-place"] === true;
15404
+ const inPlace2 = args2.values["in-place"] === true;
14848
15405
  if (inPlace2 && !(ctx.yes && isPerson(ctx))) {
14849
15406
  return Promise.resolve(
14850
15407
  failureFrom(ctx.t, needsPersonProblem("git.undo.confirm", `finitemonkey undo ${view2.ref} --in-place --yes`))
@@ -14933,8 +15490,8 @@ var helpCommand = {
14933
15490
  usage: "finitemonkey help [command]",
14934
15491
  status: "ready",
14935
15492
  maxPositionals: 3,
14936
- run: (ctx, args, registry) => Promise.resolve(
14937
- args.positionals.length === 0 ? overview(ctx, registry) : topicHelp(ctx, registry, args.positionals)
15493
+ run: (ctx, args2, registry) => Promise.resolve(
15494
+ args2.positionals.length === 0 ? overview(ctx, registry) : topicHelp(ctx, registry, args2.positionals)
14938
15495
  )
14939
15496
  };
14940
15497
 
@@ -14946,8 +15503,8 @@ var hookCommand = {
14946
15503
  status: "ready",
14947
15504
  maxPositionals: 1,
14948
15505
  options: { via: { type: "string" } },
14949
- run: async (ctx, args) => {
14950
- const raw = args.positionals[0] ?? "";
15506
+ run: async (ctx, args2) => {
15507
+ const raw = args2.positionals[0] ?? "";
14951
15508
  const event = parseHookEvent(raw);
14952
15509
  if (event === void 0) {
14953
15510
  const params2 = { event: raw, events: HOOK_EVENTS.join(", ") };
@@ -14956,10 +15513,10 @@ var hookCommand = {
14956
15513
  usageProblem("unknown_hook_event", "error.unknown_hook_event", "finitemonkey help hook", params2)
14957
15514
  );
14958
15515
  }
14959
- if (stringOption(args.values, "via") === PLUGIN_VIA && projectOwnsHooks(ctx)) {
15516
+ if (stringOption(args2.values, "via") === PLUGIN_VIA && projectOwnsHooks(ctx)) {
14960
15517
  return { ok: true, exitCode: 0, data: { event, verdict: "allow" }, raw: { stdout: YIELD_STDOUT, stderr: "" } };
14961
15518
  }
14962
- const outcome = await runHook(event, {
15519
+ const outcome2 = await runHook(event, {
14963
15520
  stdin: ctx.io.stdin,
14964
15521
  deadlineMs: deadlineFor(event, ctx.env["FINITEMONKEY_HOOK_DEADLINE_MS"]),
14965
15522
  handlers: DEFAULT_HANDLERS,
@@ -14969,11 +15526,11 @@ var hookCommand = {
14969
15526
  runtime: ctx.runtime,
14970
15527
  env: ctx.env
14971
15528
  });
14972
- logHookFailure(ctx, event, outcome);
14973
- const data = { event, verdict: outcome.verdict };
14974
- const output = { stdout: outcome.stdout, stderr: outcome.stderr };
14975
- if (outcome.exitCode === 0) return { ok: true, exitCode: 0, data, raw: output };
14976
- const message = outcome.stderr.trim();
15529
+ logHookFailure(ctx, event, outcome2);
15530
+ const data = { event, verdict: outcome2.verdict };
15531
+ const output = { stdout: outcome2.stdout, stderr: outcome2.stderr };
15532
+ if (outcome2.exitCode === 0) return { ok: true, exitCode: 0, data, raw: output };
15533
+ const message = outcome2.stderr.trim();
14977
15534
  return {
14978
15535
  ok: false,
14979
15536
  exitCode: 2,
@@ -15244,8 +15801,8 @@ var i18nCheckCommand = {
15244
15801
  usage: "finitemonkey i18n check [--dir <path>]",
15245
15802
  status: "ready",
15246
15803
  options: { dir: { type: "string" } },
15247
- run: (ctx, args) => {
15248
- const dir = stringOption(args.values, "dir");
15804
+ run: (ctx, args2) => {
15805
+ const dir = stringOption(args2.values, "dir");
15249
15806
  return Promise.resolve(runI18nCheck(ctx, dir === void 0 ? void 0 : toAbsolutePath(dir, ctx.cwd)));
15250
15807
  }
15251
15808
  };
@@ -15589,11 +16146,11 @@ var runInstall = (ctx, request, presetFallback) => {
15589
16146
  if (ctx.dryRun || !ctx.yes || !needsWrite(plan.value)) return presentPlan(ctx, request, plan.value);
15590
16147
  return applyAndReport(ctx, request, plan.value);
15591
16148
  };
15592
- var initRequest = (args) => ({
16149
+ var initRequest = (args2) => ({
15593
16150
  mode: "init",
15594
- preset: stringOption(args.values, "preset"),
15595
- bin: stringOption(args.values, "bin"),
15596
- marketplace: args.values["marketplace"] === true,
16151
+ preset: stringOption(args2.values, "preset"),
16152
+ bin: stringOption(args2.values, "bin"),
16153
+ marketplace: args2.values["marketplace"] === true,
15597
16154
  showDiff: false
15598
16155
  });
15599
16156
  var withLookBack = (ctx, result) => {
@@ -15618,12 +16175,41 @@ var initCommand = {
15618
16175
  marketplace: { type: "boolean" },
15619
16176
  "look-back": { type: "boolean" }
15620
16177
  },
15621
- run: (ctx, args) => {
15622
- const result = runInstall(ctx, initRequest(args), "neutral");
15623
- return Promise.resolve(args.values["look-back"] === true ? withLookBack(ctx, result) : result);
16178
+ run: (ctx, args2) => {
16179
+ const result = runInstall(ctx, initRequest(args2), "neutral");
16180
+ return Promise.resolve(args2.values["look-back"] === true ? withLookBack(ctx, result) : result);
15624
16181
  }
15625
16182
  };
15626
16183
 
16184
+ // src/commands/pause.ts
16185
+ var toggle = (ctx, off) => {
16186
+ const command = off ? "finitemonkey off" : "finitemonkey on";
16187
+ if (!isPerson(ctx)) return failureFrom(ctx.t, needsPersonProblem("pause.error.person_only", command));
16188
+ const paths = fmPaths(findRepoRoot(ctx.runtime, ctx.cwd) ?? ctx.cwd);
16189
+ if (!off) {
16190
+ const ended = clearPause(ctx.runtime, paths, "terminal");
16191
+ return success({ paused: false, sessionId: ended?.sessionId ?? null }, [ctx.t("pause.on")]);
16192
+ }
16193
+ const session = readSession(ctx.runtime, paths);
16194
+ if (session === void 0) return success({ paused: false, sessionId: null }, [ctx.t("pause.no_session")]);
16195
+ setPause(ctx.runtime, paths, session.sessionId, "terminal");
16196
+ return success({ paused: true, sessionId: session.sessionId }, [ctx.t("pause.off")]);
16197
+ };
16198
+ var offCommand = {
16199
+ path: ["off"],
16200
+ summaryKey: "command.off.summary",
16201
+ usage: "finitemonkey off",
16202
+ status: "ready",
16203
+ run: (ctx) => Promise.resolve(toggle(ctx, true))
16204
+ };
16205
+ var onCommand = {
16206
+ path: ["on"],
16207
+ summaryKey: "command.on.summary",
16208
+ usage: "finitemonkey on",
16209
+ status: "ready",
16210
+ run: (ctx) => Promise.resolve(toggle(ctx, false))
16211
+ };
16212
+
15627
16213
  // src/commands/policy.ts
15628
16214
  var FIX = `finitemonkey policy false-positive <${OPTIONAL_GUARDS.join("|")}>`;
15629
16215
  var recordFalsePositive = (ctx, guard) => {
@@ -15647,7 +16233,7 @@ var policyFalsePositiveCommand = {
15647
16233
  usage: FIX,
15648
16234
  status: "ready",
15649
16235
  maxPositionals: 1,
15650
- run: (ctx, args) => Promise.resolve(recordFalsePositive(ctx, args.positionals[0] ?? ""))
16236
+ run: (ctx, args2) => Promise.resolve(recordFalsePositive(ctx, args2.positionals[0] ?? ""))
15651
16237
  };
15652
16238
 
15653
16239
  // src/commands/prose-check.ts
@@ -15773,10 +16359,10 @@ var proseCheckCommand = {
15773
16359
  status: "ready",
15774
16360
  options: { stdin: { type: "boolean" }, record: { type: "boolean" } },
15775
16361
  maxPositionals: 1e3,
15776
- run: async (ctx, args) => {
16362
+ run: async (ctx, args2) => {
15777
16363
  const root = projectRoot(ctx);
15778
- const mode = { stdin: args.values["stdin"] === true, record: args.values["record"] === true };
15779
- const reports = await reportsOf(ctx, root, args.positionals, mode);
16364
+ const mode = { stdin: args2.values["stdin"] === true, record: args2.values["record"] === true };
16365
+ const reports = await reportsOf(ctx, root, args2.positionals, mode);
15780
16366
  if (!reports.ok) return failureFrom(ctx.t, reports.error);
15781
16367
  return resultOf2(ctx, reports.value);
15782
16368
  }
@@ -15831,7 +16417,7 @@ var inputProblem = (ctx, id, reason, days) => {
15831
16417
  }
15832
16418
  return void 0;
15833
16419
  };
15834
- var entryOf3 = (ctx, finding3, args) => {
16420
+ var entryOf3 = (ctx, finding3, args2) => {
15835
16421
  const now = ctx.runtime.now();
15836
16422
  return {
15837
16423
  id: finding3.id,
@@ -15839,30 +16425,30 @@ var entryOf3 = (ctx, finding3, args) => {
15839
16425
  file: finding3.file,
15840
16426
  line: finding3.line,
15841
16427
  fingerprint: finding3.fingerprint,
15842
- state: args.falsePositive ? "falso-positivo" : "aceito",
16428
+ state: args2.falsePositive ? "falso-positivo" : "aceito",
15843
16429
  owner: personName(ctx),
15844
- reason: args.reason,
16430
+ reason: args2.reason,
15845
16431
  acceptedAt: now,
15846
- expiresAt: plusDays(now, args.days)
16432
+ expiresAt: plusDays(now, args2.days)
15847
16433
  };
15848
16434
  };
15849
- var acceptEntry = (ctx, args) => {
16435
+ var acceptEntry = (ctx, args2) => {
15850
16436
  const root = projectRoot(ctx);
15851
16437
  const baseline = loadBaseline(ctx.runtime, root);
15852
16438
  if (!baseline.ok) {
15853
16439
  return failure3(ctx, baselineInvalid(baseline.error));
15854
16440
  }
15855
16441
  const scanned = scanPath(ctx.runtime, root, ".");
15856
- const finding3 = scanned.ok ? scanned.value.find((item) => item.id === args.id) : void 0;
16442
+ const finding3 = scanned.ok ? scanned.value.find((item) => item.id === args2.id) : void 0;
15857
16443
  if (finding3 === void 0) {
15858
16444
  return failure3(
15859
16445
  ctx,
15860
16446
  usageProblem("secrets_finding_not_found", "secrets.error.finding_not_found", "finitemonkey secrets scan", {
15861
- id: args.id
16447
+ id: args2.id
15862
16448
  })
15863
16449
  );
15864
16450
  }
15865
- const entry2 = entryOf3(ctx, finding3, args);
16451
+ const entry2 = entryOf3(ctx, finding3, args2);
15866
16452
  const entries = [...baseline.value.entries.filter((item) => item.id !== entry2.id), entry2];
15867
16453
  const saved = saveBaseline(ctx.runtime, root, { schema: baseline.value.schema, entries });
15868
16454
  if (!saved.ok) {
@@ -15885,10 +16471,10 @@ var secretsAcceptCommand = {
15885
16471
  status: "ready",
15886
16472
  maxPositionals: 1,
15887
16473
  options: { reason: { type: "string" }, days: { type: "string" }, "false-positive": { type: "boolean" } },
15888
- run: (ctx, args) => {
15889
- const id = args.positionals[0] ?? "";
15890
- const reason = stringOption(args.values, "reason");
15891
- const days = daysOf(args.values);
16474
+ run: (ctx, args2) => {
16475
+ const id = args2.positionals[0] ?? "";
16476
+ const reason = stringOption(args2.values, "reason");
16477
+ const days = daysOf(args2.values);
15892
16478
  const problem4 = inputProblem(ctx, id, reason, days);
15893
16479
  if (problem4 !== void 0 || reason === void 0 || days === void 0) {
15894
16480
  return Promise.resolve(
@@ -15899,7 +16485,7 @@ var secretsAcceptCommand = {
15899
16485
  );
15900
16486
  }
15901
16487
  return Promise.resolve(
15902
- acceptEntry(ctx, { id, reason, days, falsePositive: args.values["false-positive"] === true })
16488
+ acceptEntry(ctx, { id, reason, days, falsePositive: args2.values["false-positive"] === true })
15903
16489
  );
15904
16490
  }
15905
16491
  };
@@ -15980,18 +16566,18 @@ var secretsIncidentCommand = {
15980
16566
  usage: USAGE,
15981
16567
  status: "ready",
15982
16568
  maxPositionals: 2,
15983
- run: (ctx, args) => {
15984
- const action = actionOf(args.positionals[0]);
16569
+ run: (ctx, args2) => {
16570
+ const action = actionOf(args2.positionals[0]);
15985
16571
  const root = projectRoot(ctx);
15986
16572
  if (action === void 0)
15987
16573
  return Promise.resolve(
15988
16574
  fail2(
15989
16575
  ctx,
15990
- usageProblem("invalid_arguments", "error.invalid_arguments", USAGE, { detail: args.positionals[0] ?? "" })
16576
+ usageProblem("invalid_arguments", "error.invalid_arguments", USAGE, { detail: args2.positionals[0] ?? "" })
15991
16577
  )
15992
16578
  );
15993
16579
  if (action === "list") return Promise.resolve(list2(ctx, root));
15994
- const id = args.positionals[1] ?? "";
16580
+ const id = args2.positionals[1] ?? "";
15995
16581
  const incident = readIncident(ctx.runtime, root, id);
15996
16582
  if (!incident.ok) return Promise.resolve(notFound2(ctx, id));
15997
16583
  return Promise.resolve(action === "purge" ? purge(ctx, incident.value) : move(ctx, root, incident.value, action));
@@ -16041,25 +16627,25 @@ var historyIncidents = (ctx, root, fresh3) => fresh3.flatMap((finding3) => {
16041
16627
  const saved = recordIncident(ctx.runtime, root, draft, HOOK_ACTOR);
16042
16628
  return saved.ok ? [saved.value.id] : [];
16043
16629
  });
16044
- var dataOf = (mode, outcome, incidents) => {
16630
+ var dataOf = (mode, outcome2, incidents) => {
16045
16631
  const findings = [
16046
- ...outcome.fresh.map((finding3) => view(finding3, "new")),
16047
- ...outcome.known.map((finding3) => view(finding3, "known")),
16048
- ...outcome.expired.map((finding3) => view(finding3, "expired"))
16632
+ ...outcome2.fresh.map((finding3) => view(finding3, "new")),
16633
+ ...outcome2.known.map((finding3) => view(finding3, "known")),
16634
+ ...outcome2.expired.map((finding3) => view(finding3, "expired"))
16049
16635
  ];
16050
16636
  return {
16051
16637
  schema: SECRETS_SCAN_SCHEMA,
16052
16638
  mode,
16053
16639
  baseline: BASELINE_FILE2,
16054
16640
  findings,
16055
- fixed: outcome.fixed.map((entry2) => entry2.id),
16641
+ fixed: outcome2.fixed.map((entry2) => entry2.id),
16056
16642
  incidents,
16057
16643
  summary: {
16058
16644
  total: findings.length,
16059
- new: outcome.fresh.length,
16060
- known: outcome.known.length,
16061
- expired: outcome.expired.length,
16062
- fixed: outcome.fixed.length
16645
+ new: outcome2.fresh.length,
16646
+ known: outcome2.known.length,
16647
+ expired: outcome2.expired.length,
16648
+ fixed: outcome2.fixed.length
16063
16649
  }
16064
16650
  };
16065
16651
  };
@@ -16093,8 +16679,8 @@ var resultOf3 = (ctx, data) => {
16093
16679
  human2
16094
16680
  );
16095
16681
  };
16096
- var prune = (ctx, root, baseline, outcome) => {
16097
- if (outcome.fixed.length > 0) saveBaseline(ctx.runtime, root, withoutFixed(baseline, outcome.fixed));
16682
+ var prune = (ctx, root, baseline, outcome2) => {
16683
+ if (outcome2.fixed.length > 0) saveBaseline(ctx.runtime, root, withoutFixed(baseline, outcome2.fixed));
16098
16684
  };
16099
16685
  var secretsScanCommand = {
16100
16686
  path: ["secrets", "scan"],
@@ -16102,25 +16688,25 @@ var secretsScanCommand = {
16102
16688
  usage: "finitemonkey secrets scan [--staged | --history | --path <dir>] [--json]",
16103
16689
  status: "ready",
16104
16690
  options: { staged: { type: "boolean" }, history: { type: "boolean" }, path: { type: "string" } },
16105
- run: async (ctx, args) => {
16106
- const mode = modeOf3(args.values);
16691
+ run: async (ctx, args2) => {
16692
+ const mode = modeOf3(args2.values);
16107
16693
  const fix = "finitemonkey help secrets scan";
16108
16694
  if (mode === void 0) return failureFrom(ctx.t, usageProblem("invalid_arguments", "secrets.error.one_mode", fix));
16109
16695
  const root = projectRoot(ctx);
16110
- const target = stringOption(args.values, "path") ?? ".";
16696
+ const target = stringOption(args2.values, "path") ?? ".";
16111
16697
  const scanned = await runScan(ctx, root, mode, target);
16112
16698
  if (!scanned.ok) return failureFrom(ctx.t, gitUnavailable(scanned.error));
16113
16699
  const baseline = loadBaseline(ctx.runtime, root);
16114
16700
  if (!baseline.ok) return failureFrom(ctx.t, baselineInvalid(baseline.error));
16115
- const outcome = ratchet(
16701
+ const outcome2 = ratchet(
16116
16702
  scanned.value,
16117
16703
  baseline.value,
16118
16704
  ctx.runtime.now(),
16119
16705
  mode === "path" ? scopeOf(target) : void 0
16120
16706
  );
16121
- if (mode === "path") prune(ctx, root, baseline.value, outcome);
16122
- const incidents = mode === "history" ? historyIncidents(ctx, root, outcome.fresh) : [];
16123
- return resultOf3(ctx, dataOf(mode, outcome, incidents));
16707
+ if (mode === "path") prune(ctx, root, baseline.value, outcome2);
16708
+ const incidents = mode === "history" ? historyIncidents(ctx, root, outcome2.fresh) : [];
16709
+ return resultOf3(ctx, dataOf(mode, outcome2, incidents));
16124
16710
  }
16125
16711
  };
16126
16712
 
@@ -16171,7 +16757,11 @@ var checkYaml = (check2) => ({
16171
16757
  ...check2.timeoutS === void 0 ? {} : { timeout_s: check2.timeoutS },
16172
16758
  ...check2.require === void 0 ? {} : { require: [...check2.require] },
16173
16759
  ...check2.requireOpen === void 0 ? {} : { require_open: check2.requireOpen },
16174
- ...check2.description === void 0 ? {} : { description: check2.description }
16760
+ ...check2.description === void 0 ? {} : { description: check2.description },
16761
+ ...check2.port === void 0 ? {} : { port: check2.port },
16762
+ ...check2.route === void 0 ? {} : { route: check2.route },
16763
+ ...check2.status === void 0 ? {} : { status: check2.status },
16764
+ ...check2.body === void 0 ? {} : { body: check2.body }
16175
16765
  });
16176
16766
  var contractYaml = (contract) => ({
16177
16767
  id: contract.id,
@@ -16287,13 +16877,13 @@ var list3 = (values, name) => {
16287
16877
  return raw.filter((item) => typeof item === "string" && item.trim() !== "");
16288
16878
  };
16289
16879
  var csv = (values, name) => list3(values, name).flatMap((item) => item.split(",").map((part2) => part2.trim())).filter((part2) => part2 !== "");
16290
- var positive2 = (values, name) => {
16880
+ var positive3 = (values, name) => {
16291
16881
  const parsed = Number.parseInt(stringOption(values, name) ?? "", 10);
16292
16882
  return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
16293
16883
  };
16294
16884
  var draftFrom = (ctx, paths, id, values) => {
16295
- const files = positive2(values, "budget-files");
16296
- const lines3 = positive2(values, "budget-lines");
16885
+ const files = positive3(values, "budget-files");
16886
+ const lines3 = positive3(values, "budget-lines");
16297
16887
  const size = SIZES.find((item) => item === stringOption(values, "size")) ?? "S";
16298
16888
  return {
16299
16889
  id,
@@ -16396,7 +16986,7 @@ var taskNewCommand = {
16396
16986
  status: "ready",
16397
16987
  options: TASK_OPTIONS,
16398
16988
  maxPositionals: 1,
16399
- run: (ctx, args) => Promise.resolve(newTask(ctx, args.positionals[0], args.values))
16989
+ run: (ctx, args2) => Promise.resolve(newTask(ctx, args2.positionals[0], args2.values))
16400
16990
  };
16401
16991
  var taskShowCommand = {
16402
16992
  path: ["task", "show"],
@@ -16404,7 +16994,7 @@ var taskShowCommand = {
16404
16994
  usage: "finitemonkey task show [<id>]",
16405
16995
  status: "ready",
16406
16996
  maxPositionals: 1,
16407
- run: (ctx, args) => Promise.resolve(showTask(ctx, args.positionals[0]))
16997
+ run: (ctx, args2) => Promise.resolve(showTask(ctx, args2.positionals[0]))
16408
16998
  };
16409
16999
  var taskDoneCommand = {
16410
17000
  path: ["task", "done"],
@@ -16412,7 +17002,7 @@ var taskDoneCommand = {
16412
17002
  usage: "finitemonkey task done [<id>]",
16413
17003
  status: "ready",
16414
17004
  maxPositionals: 1,
16415
- run: (ctx, args) => Promise.resolve(doneTask(ctx, args.positionals[0]))
17005
+ run: (ctx, args2) => Promise.resolve(doneTask(ctx, args2.positionals[0]))
16416
17006
  };
16417
17007
 
16418
17008
  // src/install/uninstall.ts
@@ -16462,7 +17052,7 @@ var isEmptyDir = (project, relative9) => {
16462
17052
  const listed4 = project.runtime.listDir(project.abs(relative9));
16463
17053
  return listed4.ok && listed4.value.length === 0;
16464
17054
  };
16465
- var finish2 = (project, plan) => {
17055
+ var finish3 = (project, plan) => {
16466
17056
  const target = plan.purge ? STATE_DIR : LOCK_FILE;
16467
17057
  const removed = project.runtime.remove(project.abs(target));
16468
17058
  if (!removed.ok) return reject({ path: target, detail: removed.error });
@@ -16477,7 +17067,7 @@ var applyRemoval = (project, plan) => {
16477
17067
  }
16478
17068
  return accept(void 0);
16479
17069
  });
16480
- return changed.ok ? finish2(project, plan) : changed;
17070
+ return changed.ok ? finish3(project, plan) : changed;
16481
17071
  };
16482
17072
 
16483
17073
  // src/commands/uninstall.ts
@@ -16508,22 +17098,22 @@ var linesOf2 = (ctx, data) => [
16508
17098
  ctx.t("install.uninstall.outside"),
16509
17099
  ...OUTSIDE.map(({ key, command }) => ` ${ctx.t(key, { command })}`)
16510
17100
  ];
16511
- var fixFor = (args) => [
17101
+ var fixFor = (args2) => [
16512
17102
  "finitemonkey uninstall --yes",
16513
- ...args.values["purge"] === true ? ["--purge"] : [],
16514
- ...args.values["keep-generated"] === true ? ["--keep-generated"] : []
17103
+ ...args2.values["purge"] === true ? ["--purge"] : [],
17104
+ ...args2.values["keep-generated"] === true ? ["--keep-generated"] : []
16515
17105
  ].join(" ");
16516
- var runUninstall = (ctx, args) => {
17106
+ var runUninstall = (ctx, args2) => {
16517
17107
  const { project } = openProject(ctx);
16518
17108
  const lock = readPreviousLock(project);
16519
17109
  if (!lock.ok) return failureFrom(ctx.t, planProblem(lock.error));
16520
17110
  if (lock.value === void 0) return failureFrom(ctx.t, notInstalled());
16521
- const options = { purge: args.values["purge"] === true, keepGenerated: args.values["keep-generated"] === true };
17111
+ const options = { purge: args2.values["purge"] === true, keepGenerated: args2.values["keep-generated"] === true };
16522
17112
  const plan = planRemoval(project, lock.value, options);
16523
17113
  const preview = dataOf2(project.root, plan, false);
16524
17114
  if (ctx.dryRun) return success(preview, [...linesOf2(ctx, preview), ctx.t("install.dry_run")]);
16525
17115
  if (!ctx.yes) {
16526
- const next2 = fixFor(args);
17116
+ const next2 = fixFor(args2);
16527
17117
  const problem4 = confirmProblem("uninstall", ctx.t("install.uninstall.question"), next2);
16528
17118
  return { ...failureFrom(ctx.t, { ...problem4, data: preview }, linesOf2(ctx, preview)), next: next2 };
16529
17119
  }
@@ -16538,7 +17128,7 @@ var uninstallCommand = {
16538
17128
  usage: "finitemonkey uninstall [--purge] [--keep-generated] [--dry-run] [--yes]",
16539
17129
  status: "ready",
16540
17130
  options: { purge: { type: "boolean" }, "keep-generated": { type: "boolean" } },
16541
- run: (ctx, args) => Promise.resolve(runUninstall(ctx, args))
17131
+ run: (ctx, args2) => Promise.resolve(runUninstall(ctx, args2))
16542
17132
  };
16543
17133
 
16544
17134
  // src/commands/update.ts
@@ -16561,14 +17151,14 @@ var check = (ctx, bin) => {
16561
17151
  error: { code: "install_outdated", message: ctx.t("error.install_outdated"), fix: "finitemonkey update --yes" }
16562
17152
  };
16563
17153
  };
16564
- var runUpdate = (ctx, args) => {
16565
- const bin = stringOption(args.values, "bin");
16566
- if (args.values["check"] === true) return check(ctx, bin);
17154
+ var runUpdate = (ctx, args2) => {
17155
+ const bin = stringOption(args2.values, "bin");
17156
+ if (args2.values["check"] === true) return check(ctx, bin);
16567
17157
  const { project } = openProject(ctx);
16568
17158
  const previous = readPreviousLock(project);
16569
17159
  if (!previous.ok) return failureFrom(ctx.t, planProblem(previous.error));
16570
17160
  if (previous.value === void 0) return failureFrom(ctx.t, notInstalled());
16571
- const preset = stringOption(args.values, "preset");
17161
+ const preset = stringOption(args2.values, "preset");
16572
17162
  const request = { mode: "update", preset, bin, marketplace: false, showDiff: true };
16573
17163
  return runInstall(ctx, request, previous.value.preset);
16574
17164
  };
@@ -16578,7 +17168,7 @@ var updateCommand = {
16578
17168
  usage: "finitemonkey update [--check] [--preset neutral|gabriel] [--bin <path>] [--yes]",
16579
17169
  status: "ready",
16580
17170
  options: { check: { type: "boolean" }, preset: { type: "string" }, bin: { type: "string" } },
16581
- run: (ctx, args) => Promise.resolve(runUpdate(ctx, args))
17171
+ run: (ctx, args2) => Promise.resolve(runUpdate(ctx, args2))
16582
17172
  };
16583
17173
 
16584
17174
  // src/commands/version.ts
@@ -16608,12 +17198,15 @@ var REGISTRY = [
16608
17198
  secretsScanCommand,
16609
17199
  secretsAcceptCommand,
16610
17200
  policyFalsePositiveCommand,
17201
+ offCommand,
17202
+ onCommand,
16611
17203
  secretsIncidentCommand,
16612
17204
  proseCheckCommand,
16613
17205
  snapshotCommand,
16614
17206
  undoCommand,
16615
17207
  rescueCommand,
16616
17208
  mergeVerifyCommand,
17209
+ appCheckCommand,
16617
17210
  versionCommand,
16618
17211
  helpCommand
16619
17212
  ];
@@ -16692,9 +17285,9 @@ var failWith = (ctx, name, problem4) => ({
16692
17285
  });
16693
17286
  var invoke = (ctx, argv, spec2, registry) => {
16694
17287
  const name = commandName(spec2);
16695
- const args = strictArgs(argv, spec2);
16696
- if (!args.ok) return failWith(ctx, name, args.error);
16697
- return { name, execute: () => spec2.run(ctx, args.value, registry) };
17288
+ const args2 = strictArgs(argv, spec2);
17289
+ if (!args2.ok) return failWith(ctx, name, args2.error);
17290
+ return { name, execute: () => spec2.run(ctx, args2.value, registry) };
16698
17291
  };
16699
17292
  var builtin = (registry, name) => registry.find((spec2) => commandName(spec2) === name);
16700
17293
  var dispatch = (ctx, argv, loose, registry) => {