finitemonkey 0.1.0-next.28 → 0.1.0-next.29

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.
@@ -40,8 +40,8 @@ var parseJsonObject = (text9) => {
40
40
  if (!parsed.ok) return parsed;
41
41
  return isRecord(parsed.value) ? accept(parsed.value) : reject("expected a JSON object");
42
42
  };
43
- var optionalString = (record3, key) => {
44
- const value = record3[key];
43
+ var optionalString = (record4, key) => {
44
+ const value = record4[key];
45
45
  return typeof value === "string" ? value : void 0;
46
46
  };
47
47
 
@@ -620,7 +620,7 @@ var GLOBAL_OPTIONS = {
620
620
  };
621
621
  var flag = (values, name) => values[name] === true;
622
622
  var looseArgs = (argv) => {
623
- const { values, positionals } = parseArgs({
623
+ const { values, positionals: positionals2 } = parseArgs({
624
624
  args: [...argv],
625
625
  options: GLOBAL_OPTIONS,
626
626
  strict: false,
@@ -628,7 +628,7 @@ var looseArgs = (argv) => {
628
628
  });
629
629
  const lang = values.lang;
630
630
  return {
631
- positionals,
631
+ positionals: positionals2,
632
632
  json: flag(values, "json"),
633
633
  yes: flag(values, "yes"),
634
634
  dryRun: flag(values, "dry-run"),
@@ -637,7 +637,7 @@ var looseArgs = (argv) => {
637
637
  lang: typeof lang === "string" ? lang : void 0
638
638
  };
639
639
  };
640
- var resolveCommand = (registry, positionals) => registry.filter((spec2) => spec2.path.every((part2, index) => positionals[index] === part2)).reduce(
640
+ var resolveCommand = (registry, positionals2) => registry.filter((spec2) => spec2.path.every((part2, index) => positionals2[index] === part2)).reduce(
641
641
  (best, spec2) => best === void 0 || spec2.path.length > best.consumed ? { spec: spec2, consumed: spec2.path.length } : best,
642
642
  void 0
643
643
  );
@@ -796,20 +796,20 @@ var entryOf = (kind, xy, path) => ({
796
796
  staged: kind !== "untracked" && !xy.startsWith("."),
797
797
  unstaged: kind === "untracked" || xy.charAt(1) !== "."
798
798
  });
799
- var afterFields = (record3, count4) => record3.split(" ").slice(count4).join(" ");
799
+ var afterFields = (record4, count4) => record4.split(" ").slice(count4).join(" ");
800
800
  var parsePorcelainV2 = (stdout) => {
801
801
  const records = stdout.split("\0");
802
802
  const out = [];
803
803
  for (let index = 0; index < records.length; index += 1) {
804
- const record3 = records[index] ?? "";
805
- const xy = record3.split(" ")[1] ?? "..";
806
- if (record3.startsWith("1 ")) out.push(entryOf("changed", xy, afterFields(record3, PORCELAIN_FIELDS.ordinary)));
807
- else if (record3.startsWith("2 ")) {
808
- out.push(entryOf("changed", xy, afterFields(record3, PORCELAIN_FIELDS.renamed)));
804
+ const record4 = records[index] ?? "";
805
+ const xy = record4.split(" ")[1] ?? "..";
806
+ if (record4.startsWith("1 ")) out.push(entryOf("changed", xy, afterFields(record4, PORCELAIN_FIELDS.ordinary)));
807
+ else if (record4.startsWith("2 ")) {
808
+ out.push(entryOf("changed", xy, afterFields(record4, PORCELAIN_FIELDS.renamed)));
809
809
  index += 1;
810
- } else if (record3.startsWith("u ")) {
811
- out.push(entryOf("unmerged", xy, afterFields(record3, PORCELAIN_FIELDS.unmerged)));
812
- } else if (record3.startsWith("? ")) out.push(entryOf("untracked", "??", record3.slice(2)));
810
+ } else if (record4.startsWith("u ")) {
811
+ out.push(entryOf("unmerged", xy, afterFields(record4, PORCELAIN_FIELDS.unmerged)));
812
+ } else if (record4.startsWith("? ")) out.push(entryOf("untracked", "??", record4.slice(2)));
813
813
  }
814
814
  return out.filter((entry2) => entry2.path !== "" && !entry2.path.startsWith(STATE_PREFIX));
815
815
  };
@@ -1023,8 +1023,8 @@ var withDeadline = (work, ms, startTimer) => new Promise((resolve2, reject2) =>
1023
1023
  // src/review/findings.ts
1024
1024
  var GROUPS = ["must", "should", "nice"];
1025
1025
  var VERDICTS = ["approve", "approve-with-changes", "request-changes"];
1026
- var text2 = (record3, key) => {
1027
- const value = record3[key];
1026
+ var text2 = (record4, key) => {
1027
+ const value = record4[key];
1028
1028
  return typeof value === "string" ? value.trim() : "";
1029
1029
  };
1030
1030
  var reproOf = (value) => {
@@ -1422,11 +1422,11 @@ var parseEntries = (value) => {
1422
1422
  const entries = value.map(parseEntry);
1423
1423
  return entries.every((entry2) => entry2 !== void 0) ? entries : void 0;
1424
1424
  };
1425
- var fromRecord = (record3) => {
1426
- const { schema, version, preset, installedAt, updatedAt } = record3;
1425
+ var fromRecord = (record4) => {
1426
+ const { schema, version, preset, installedAt, updatedAt } = record4;
1427
1427
  const known2 = PRESETS.find((item2) => item2 === preset);
1428
- const entries = parseEntries(record3["entries"]);
1429
- const createdDirs = strings(record3["createdDirs"]);
1428
+ const entries = parseEntries(record4["entries"]);
1429
+ const createdDirs = strings(record4["createdDirs"]);
1430
1430
  if (schema !== LOCK_SCHEMA) return reject(`schema is not ${LOCK_SCHEMA}`);
1431
1431
  if (typeof version !== "string" || typeof installedAt !== "string" || typeof updatedAt !== "string") {
1432
1432
  return reject("version, installedAt and updatedAt must be text");
@@ -1682,7 +1682,7 @@ var GUARD_MODES = ["off", "shadow", "active"];
1682
1682
  var LEVELS = ["light", "standard", "strict"];
1683
1683
  var SEVERITIES = ["low", "medium", "high"];
1684
1684
  var DEFAULT_PROMOTE_AFTER = 20;
1685
- var OPTIONAL_GUARDS = ["prose"];
1685
+ var OPTIONAL_GUARDS = ["prose", "deps"];
1686
1686
  var DEFAULT_POLICY = {
1687
1687
  exists: false,
1688
1688
  level: "standard",
@@ -1904,10 +1904,10 @@ var renderJson = (value, style) => `${JSON.stringify(value, null, style.indent)}
1904
1904
  var parseSettings = (text9) => text9 === void 0 || text9.trim() === "" ? { ok: true, value: {} } : parseJsonObject(text9);
1905
1905
  var asList = (value) => Array.isArray(value) ? value : [];
1906
1906
  var asRecord = (value) => isRecord(value) ? value : {};
1907
- var setKey = (record3, key, value) => {
1908
- if (!(key in record3)) return value === void 0 ? record3 : { ...record3, [key]: value };
1907
+ var setKey = (record4, key, value) => {
1908
+ if (!(key in record4)) return value === void 0 ? record4 : { ...record4, [key]: value };
1909
1909
  return Object.fromEntries(
1910
- Object.entries(record3).flatMap(([name, current2]) => {
1910
+ Object.entries(record4).flatMap(([name, current2]) => {
1911
1911
  if (name !== key) return [[name, current2]];
1912
1912
  return value === void 0 ? [] : [[name, value]];
1913
1913
  })
@@ -3697,68 +3697,8 @@ var deadlineFor = (event, raw) => {
3697
3697
  return EVENT_DEADLINES[event] ?? DEFAULT_HOOK_DEADLINE_MS;
3698
3698
  };
3699
3699
 
3700
- // src/gate/hooks/common.ts
3701
- import { dirname as dirname5, join as join13 } from "node:path";
3702
- var DEFAULT_GATE_BUDGET_MS = 3e5;
3703
- var sessionCwd = (input) => input.payload.cwd === void 0 ? input.cwd : toAbsolutePath(input.payload.cwd, input.cwd);
3704
- var MAX_DEPTH = 64;
3705
- var findRepoRoot = (runtime, start) => {
3706
- let current2 = start;
3707
- for (let depth = 0; depth < MAX_DEPTH; depth += 1) {
3708
- if (runtime.stat(toAbsolutePath(join13(current2, ".git"))) !== void 0) return current2;
3709
- const parent = toAbsolutePath(dirname5(current2));
3710
- if (parent === current2) return void 0;
3711
- current2 = parent;
3712
- }
3713
- return void 0;
3714
- };
3715
- var projectPaths = (input) => {
3716
- const root = findRepoRoot(input.runtime, sessionCwd(input));
3717
- return root === void 0 ? void 0 : fmPaths(root);
3718
- };
3719
- var gateBudget = (input) => {
3720
- const parsed = Number.parseInt(input.env["FINITEMONKEY_GATE_BUDGET_MS"] ?? "", 10);
3721
- return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_GATE_BUDGET_MS;
3722
- };
3723
- var allowWith = (output) => ({ kind: "allow", output });
3724
- var contextOutput = (event, additionalContext, systemMessage) => ({
3725
- hookSpecificOutput: { hookEventName: event, additionalContext },
3726
- ...systemMessage === void 0 ? {} : { systemMessage }
3727
- });
3728
- var human = (text9) => `finitemonkey: ${text9}`;
3729
-
3730
- // src/hooks/fields.ts
3731
- var textField = (record3, ...names) => {
3732
- for (const name of names) {
3733
- const value = record3[name];
3734
- if (typeof value === "string") return value;
3735
- }
3736
- return void 0;
3737
- };
3738
- var toolName = (raw) => textField(raw, "tool_name") ?? "";
3739
- var toolInput = (raw) => {
3740
- const value = raw["tool_input"];
3741
- return isRecord(value) ? value : {};
3742
- };
3743
- var editsOf = (input) => {
3744
- const single2 = textField(input, "old_string");
3745
- const replacement = textField(input, "new_string");
3746
- if (single2 !== void 0 && replacement !== void 0) {
3747
- return [{ oldText: single2, newText: replacement, all: input["replace_all"] === true }];
3748
- }
3749
- const edits = input["edits"];
3750
- if (!Array.isArray(edits)) return [];
3751
- return edits.flatMap((edit) => {
3752
- if (!isRecord(edit)) return [];
3753
- const oldText = textField(edit, "old_string");
3754
- const newText = textField(edit, "new_string");
3755
- return oldText === void 0 || newText === void 0 ? [] : [{ oldText, newText, all: edit["replace_all"] === true }];
3756
- });
3757
- };
3758
- var applyEdits = (before, edits) => edits.reduce(
3759
- (text9, edit) => edit.all ? text9.split(edit.oldText).join(edit.newText) : text9.replace(edit.oldText, () => edit.newText),
3760
- before
3761
- );
3700
+ // src/core/clock.ts
3701
+ var nowMs = (runtime) => Date.parse(runtime.now());
3762
3702
 
3763
3703
  // src/hooks/reason.ts
3764
3704
  var REASON_LIMIT = 1e4;
@@ -3791,9 +3731,6 @@ var modelReason = (t, base, params2 = {}) => {
3791
3731
  );
3792
3732
  };
3793
3733
 
3794
- // src/core/clock.ts
3795
- var nowMs = (runtime) => Date.parse(runtime.now());
3796
-
3797
3734
  // src/task/state.ts
3798
3735
  var toCounter = (value) => {
3799
3736
  if (!isRecord(value)) return void 0;
@@ -3856,8 +3793,8 @@ var closeTask = (state, id) => assemble({
3856
3793
 
3857
3794
  // src/guards/release/grants.ts
3858
3795
  var GRANT_MINUTES = 30;
3859
- var text3 = (record3, key) => {
3860
- const value = record3[key];
3796
+ var text3 = (record4, key) => {
3797
+ const value = record4[key];
3861
3798
  return typeof value === "string" ? value : void 0;
3862
3799
  };
3863
3800
  var toGrant = (value) => {
@@ -3915,6 +3852,47 @@ var deny = (fullReason, humanText) => {
3915
3852
  var FILE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
3916
3853
  var SHELL_TOOLS = /* @__PURE__ */ new Set(["Bash", "PowerShell"]);
3917
3854
 
3855
+ // src/secrets/guards/tool-input.ts
3856
+ var toolName = (payload) => optionalString(payload.raw, "tool_name") ?? "";
3857
+ var toolInput = (payload) => {
3858
+ const input = payload.raw["tool_input"];
3859
+ return isRecord(input) ? input : {};
3860
+ };
3861
+ var editTexts = (input, file) => {
3862
+ const edits = input["edits"];
3863
+ if (!Array.isArray(edits)) return [];
3864
+ return edits.flatMap((edit) => {
3865
+ if (!isRecord(edit)) return [];
3866
+ const text9 = optionalString(edit, "new_string");
3867
+ return text9 === void 0 ? [] : [{ file, text: text9, whole: false }];
3868
+ });
3869
+ };
3870
+ var writtenTexts = (tool, input) => {
3871
+ const file = optionalString(input, "file_path") ?? optionalString(input, "notebook_path") ?? "";
3872
+ const single2 = (key, whole) => {
3873
+ const text9 = optionalString(input, key);
3874
+ return text9 === void 0 ? [] : [{ file, text: text9, whole }];
3875
+ };
3876
+ switch (tool) {
3877
+ case "Write":
3878
+ return single2("content", true);
3879
+ case "Edit":
3880
+ return single2("new_string", false);
3881
+ case "MultiEdit":
3882
+ return editTexts(input, file);
3883
+ case "NotebookEdit":
3884
+ return single2("new_source", false);
3885
+ default:
3886
+ return [];
3887
+ }
3888
+ };
3889
+ var stringLeaves = (value, out = []) => {
3890
+ if (typeof value === "string") out.push(value);
3891
+ else if (Array.isArray(value)) for (const item2 of value) stringLeaves(item2, out);
3892
+ else if (isRecord(value)) for (const item2 of Object.values(value)) stringLeaves(item2, out);
3893
+ return out;
3894
+ };
3895
+
3918
3896
  // src/shell/normalize.ts
3919
3897
  import { posix } from "node:path";
3920
3898
 
@@ -4322,9 +4300,9 @@ var ALIASES = {
4322
4300
  var powershellProgram = (name) => ALIASES[name.toLowerCase()] ?? name.toLowerCase();
4323
4301
 
4324
4302
  // src/shell/wrappers.ts
4325
- var spec = (withValue, positionals = 0, queries = []) => ({
4303
+ var spec = (withValue, positionals2 = 0, queries = []) => ({
4326
4304
  withValue: new Set(withValue),
4327
- positionals,
4305
+ positionals: positionals2,
4328
4306
  queries: new Set(queries)
4329
4307
  });
4330
4308
  var WRAPPERS = {
@@ -4494,7 +4472,7 @@ var unwrap = (initial, stdin) => {
4494
4472
  };
4495
4473
 
4496
4474
  // src/shell/normalize.ts
4497
- var MAX_DEPTH2 = 8;
4475
+ var MAX_DEPTH = 8;
4498
4476
  var ASSIGNMENT2 = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/s;
4499
4477
  var PS_ENV = /^\$env:([A-Za-z_][A-Za-z0-9_]*)=?(.*)$/is;
4500
4478
  var CHANGES_DIR = /* @__PURE__ */ new Set(["cd", "pushd", "set-location", "push-location"]);
@@ -4570,7 +4548,7 @@ var visit = (raw, language, context, depth, collector) => {
4570
4548
  for (const extra of result.extra) emit(extra, raw, language, context, collector);
4571
4549
  };
4572
4550
  var collect = (text9, language, context, depth, collector) => {
4573
- if (depth > MAX_DEPTH2) {
4551
+ if (depth > MAX_DEPTH) {
4574
4552
  collector.incomplete = true;
4575
4553
  return;
4576
4554
  }
@@ -4593,6 +4571,572 @@ var segmentsOf = (command) => normalizeCommand(command).commands.map((item2) =>
4593
4571
  redirectedFrom: item2.redirects.filter((redirect) => redirect.op === "<").map((redirect) => redirect.target)
4594
4572
  }));
4595
4573
 
4574
+ // src/deps/popular.ts
4575
+ var NPM = [
4576
+ "react",
4577
+ "react-dom",
4578
+ "preact",
4579
+ "next",
4580
+ "vue",
4581
+ "svelte",
4582
+ "angular",
4583
+ "express",
4584
+ "fastify",
4585
+ "koa",
4586
+ "lodash",
4587
+ "underscore",
4588
+ "axios",
4589
+ "request",
4590
+ "chalk",
4591
+ "commander",
4592
+ "yargs",
4593
+ "debug",
4594
+ "dotenv",
4595
+ "moment",
4596
+ "dayjs",
4597
+ "date-fns",
4598
+ "uuid",
4599
+ "nanoid",
4600
+ "zod",
4601
+ "yup",
4602
+ "joi",
4603
+ "typescript",
4604
+ "eslint",
4605
+ "prettier",
4606
+ "webpack",
4607
+ "vite",
4608
+ "rollup",
4609
+ "esbuild",
4610
+ "babel-core",
4611
+ "jest",
4612
+ "mocha",
4613
+ "vitest",
4614
+ "chai",
4615
+ "sinon",
4616
+ "cross-env",
4617
+ "rimraf",
4618
+ "glob",
4619
+ "minimist",
4620
+ "semver",
4621
+ "async",
4622
+ "bluebird",
4623
+ "classnames",
4624
+ "jquery",
4625
+ "redux",
4626
+ "mobx",
4627
+ "graphql",
4628
+ "mongoose",
4629
+ "sequelize",
4630
+ "prisma",
4631
+ "drizzle-orm",
4632
+ "pg",
4633
+ "mysql",
4634
+ "mysql2",
4635
+ "redis",
4636
+ "ioredis",
4637
+ "socket.io",
4638
+ "ws",
4639
+ "jsonwebtoken",
4640
+ "bcrypt",
4641
+ "bcryptjs",
4642
+ "passport",
4643
+ "cors",
4644
+ "helmet",
4645
+ "body-parser",
4646
+ "cookie-parser",
4647
+ "multer",
4648
+ "nodemon",
4649
+ "tailwindcss",
4650
+ "postcss",
4651
+ "autoprefixer",
4652
+ "sass",
4653
+ "styled-components",
4654
+ "openai",
4655
+ "stripe",
4656
+ "puppeteer",
4657
+ "playwright",
4658
+ "cheerio",
4659
+ "node-fetch",
4660
+ "form-data",
4661
+ "colors",
4662
+ "inquirer",
4663
+ "ora",
4664
+ "execa",
4665
+ "fs-extra",
4666
+ "tslib",
4667
+ "core-js",
4668
+ "rxjs",
4669
+ "electron",
4670
+ "three",
4671
+ "d3",
4672
+ "socket.io-client"
4673
+ ];
4674
+ var PYPI = [
4675
+ "requests",
4676
+ "numpy",
4677
+ "pandas",
4678
+ "django",
4679
+ "flask",
4680
+ "fastapi",
4681
+ "uvicorn",
4682
+ "pydantic",
4683
+ "sqlalchemy",
4684
+ "alembic",
4685
+ "celery",
4686
+ "redis",
4687
+ "boto3",
4688
+ "botocore",
4689
+ "urllib3",
4690
+ "certifi",
4691
+ "setuptools",
4692
+ "wheel",
4693
+ "pip",
4694
+ "six",
4695
+ "python-dateutil",
4696
+ "pytz",
4697
+ "pyyaml",
4698
+ "jinja2",
4699
+ "click",
4700
+ "rich",
4701
+ "typer",
4702
+ "httpx",
4703
+ "aiohttp",
4704
+ "beautifulsoup4",
4705
+ "lxml",
4706
+ "scipy",
4707
+ "matplotlib",
4708
+ "seaborn",
4709
+ "scikit-learn",
4710
+ "tensorflow",
4711
+ "torch",
4712
+ "keras",
4713
+ "transformers",
4714
+ "openai",
4715
+ "anthropic",
4716
+ "langchain",
4717
+ "pillow",
4718
+ "opencv-python",
4719
+ "psycopg2",
4720
+ "psycopg2-binary",
4721
+ "pymongo",
4722
+ "pytest",
4723
+ "black",
4724
+ "flake8",
4725
+ "mypy",
4726
+ "ruff",
4727
+ "cryptography",
4728
+ "pyjwt",
4729
+ "paramiko",
4730
+ "selenium",
4731
+ "colorama",
4732
+ "tqdm",
4733
+ "python-dotenv",
4734
+ "gunicorn"
4735
+ ];
4736
+ var POPULAR = { npm: NPM, pypi: PYPI };
4737
+ var distance = (left, right) => {
4738
+ const rows = Array.from(
4739
+ { length: left.length + 1 },
4740
+ (_, row) => Array.from({ length: right.length + 1 }, (_2, column) => row === 0 ? column : column === 0 ? row : 0)
4741
+ );
4742
+ const cell = (row, column) => rows[row]?.[column] ?? Number.POSITIVE_INFINITY;
4743
+ for (let row = 1; row <= left.length; row += 1) {
4744
+ const current2 = rows[row] ?? [];
4745
+ for (let column = 1; column <= right.length; column += 1) {
4746
+ const cost = left[row - 1] === right[column - 1] ? 0 : 1;
4747
+ let best = Math.min(cell(row - 1, column) + 1, cell(row, column - 1) + 1, cell(row - 1, column - 1) + cost);
4748
+ const swapped = left[row - 1] === right[column - 2] && left[row - 2] === right[column - 1];
4749
+ if (row > 1 && column > 1 && swapped) best = Math.min(best, cell(row - 2, column - 2) + 1);
4750
+ current2[column] = best;
4751
+ }
4752
+ }
4753
+ return cell(left.length, right.length);
4754
+ };
4755
+ var squash2 = (name) => name.replace(/^@[^/]+\//, "").replace(/[-_.]/g, "");
4756
+ var closePopular = (ecosystem, name) => {
4757
+ const list4 = POPULAR[ecosystem];
4758
+ if (list4.includes(name)) return void 0;
4759
+ const limit = name.length <= 4 ? 0 : name.length <= 8 ? 1 : 2;
4760
+ return list4.find((popular) => {
4761
+ if (squash2(popular) === squash2(name)) return true;
4762
+ return Math.abs(popular.length - name.length) <= limit && distance(popular, name) <= limit;
4763
+ });
4764
+ };
4765
+
4766
+ // src/deps/assess.ts
4767
+ var MIN_HISTORY_DAYS = 30;
4768
+ var MIN_VERSION_AGE_DAYS = 3;
4769
+ var DAY_MS2 = 864e5;
4770
+ var HOUR_MS = 36e5;
4771
+ var registryFindings = (pkg, lookup2, nowMs2) => {
4772
+ if (lookup2.kind === "missing") return [{ kind: "missing", name: pkg.name, ecosystem: pkg.ecosystem }];
4773
+ if (lookup2.kind === "unreachable") return [{ kind: "unchecked", name: pkg.name, ecosystem: pkg.ecosystem }];
4774
+ const { facts } = lookup2;
4775
+ const out = [];
4776
+ if (facts.createdMs !== void 0 && nowMs2 - facts.createdMs < MIN_HISTORY_DAYS * DAY_MS2) {
4777
+ const days = Math.max(0, Math.floor((nowMs2 - facts.createdMs) / DAY_MS2));
4778
+ out.push({ kind: "young", name: pkg.name, days, count: facts.versionCount });
4779
+ }
4780
+ const { version, versionMs } = facts;
4781
+ if (version !== void 0 && versionMs !== void 0 && nowMs2 - versionMs < MIN_VERSION_AGE_DAYS * DAY_MS2) {
4782
+ out.push({ kind: "fresh", name: pkg.name, version, hours: Math.max(0, Math.floor((nowMs2 - versionMs) / HOUR_MS)) });
4783
+ }
4784
+ if (version !== void 0 && facts.installScript && !facts.previousInstallScript) {
4785
+ out.push({ kind: "script", name: pkg.name, version });
4786
+ }
4787
+ return out;
4788
+ };
4789
+ var assessPackage = (pkg, lookup2, nowMs2) => {
4790
+ const popular = closePopular(pkg.ecosystem, pkg.name);
4791
+ const typo = popular === void 0 ? [] : [{ kind: "typo", name: pkg.name, popular }];
4792
+ return [...typo, ...registryFindings(pkg, lookup2, nowMs2)];
4793
+ };
4794
+
4795
+ // src/deps/install-command.ts
4796
+ var NPM_ADD = {
4797
+ npm: /* @__PURE__ */ new Set(["i", "install", "add", "in", "ins", "inst", "insta", "instal", "isnt", "isntall"]),
4798
+ pnpm: /* @__PURE__ */ new Set(["add"]),
4799
+ yarn: /* @__PURE__ */ new Set(["add"]),
4800
+ bun: /* @__PURE__ */ new Set(["add", "a", "i", "install"])
4801
+ };
4802
+ var NPM_VALUE_FLAGS = /* @__PURE__ */ new Set([
4803
+ "--tag",
4804
+ "--registry",
4805
+ "--prefix",
4806
+ "-w",
4807
+ "--workspace",
4808
+ "--filter",
4809
+ "--cwd",
4810
+ "--omit"
4811
+ ]);
4812
+ var PIP_VALUE_FLAGS = /* @__PURE__ */ new Set([
4813
+ "-r",
4814
+ "--requirement",
4815
+ "-c",
4816
+ "--constraint",
4817
+ "-e",
4818
+ "--editable",
4819
+ "-i",
4820
+ "--index-url",
4821
+ "--extra-index-url",
4822
+ "-t",
4823
+ "--target",
4824
+ "--prefix",
4825
+ "-f",
4826
+ "--find-links",
4827
+ "--python",
4828
+ "-p",
4829
+ "--group",
4830
+ "--optional",
4831
+ "--extra",
4832
+ "--index",
4833
+ "--default-index",
4834
+ "--package",
4835
+ "--directory",
4836
+ "--project"
4837
+ ]);
4838
+ var PYTHONS = /^(?:python(?:3(?:\.\d+)?)?|py)$/;
4839
+ var pipArgs = (command) => {
4840
+ const { program, args: args2 } = command;
4841
+ if ((program === "pip" || program === "pip3") && args2[0] === "install") return args2.slice(1);
4842
+ if (PYTHONS.test(program) && args2[0] === "-m" && (args2[1] === "pip" || args2[1] === "pip3") && args2[2] === "install")
4843
+ return args2.slice(3);
4844
+ if (program === "uv" && args2[0] === "add") return args2.slice(1);
4845
+ if (program === "uv" && args2[0] === "pip" && args2[1] === "install") return args2.slice(2);
4846
+ return void 0;
4847
+ };
4848
+ var installerOf = (command) => {
4849
+ const adds = NPM_ADD[command.program];
4850
+ const [verb, ...rest] = command.args;
4851
+ if (adds !== void 0 && verb !== void 0 && adds.has(verb))
4852
+ return { ecosystem: "npm", tool: command.program, args: rest };
4853
+ const pip = pipArgs(command);
4854
+ return pip === void 0 ? void 0 : { ecosystem: "pypi", tool: command.program, args: pip };
4855
+ };
4856
+ var positionals = (args2, valueFlags) => {
4857
+ const out = [];
4858
+ for (let index = 0; index < args2.length; index += 1) {
4859
+ const arg = args2[index] ?? "";
4860
+ if (arg.startsWith("-")) {
4861
+ if (valueFlags.has(arg)) index += 1;
4862
+ continue;
4863
+ }
4864
+ out.push(arg);
4865
+ }
4866
+ return out;
4867
+ };
4868
+ var NPM_NAME = /^(@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/;
4869
+ var NOT_REGISTRY = /^(?:\.|\/|~|file:|link:|git|https?:|npm:|workspace:|github:)|\.tgz$/;
4870
+ var npmSpec = (word2) => {
4871
+ if (NOT_REGISTRY.test(word2)) return void 0;
4872
+ const at = word2.indexOf("@", 1);
4873
+ const name = at === -1 ? word2 : word2.slice(0, at);
4874
+ const version = at === -1 ? void 0 : word2.slice(at + 1);
4875
+ if (!NPM_NAME.test(name) || version?.includes(":") === true) return void 0;
4876
+ return { name, version: version === "" ? void 0 : version };
4877
+ };
4878
+ var PYPI_SPEC = /^([A-Za-z0-9][A-Za-z0-9._-]*)(?:\[[^\]]*\])?\s*(?:(==|===|>=|<=|~=|!=|>|<)\s*([^,;\s]+))?/;
4879
+ var pypiSpec = (word2) => {
4880
+ if (/^(?:\.|\/|~|https?:|git\+|file:)/.test(word2) || /\.(?:whl|tar\.gz|zip)$/.test(word2)) return void 0;
4881
+ const match = PYPI_SPEC.exec(word2);
4882
+ const name = match?.[1];
4883
+ if (match === null || name === void 0 || match[0].length === 0) return void 0;
4884
+ const exact2 = match[2] === "==" || match[2] === "===";
4885
+ return { name: name.toLowerCase().replace(/[._]+/g, "-"), version: exact2 ? match[3] : void 0 };
4886
+ };
4887
+ var requested = (installer) => {
4888
+ const npm = installer.ecosystem === "npm";
4889
+ return positionals(installer.args, npm ? NPM_VALUE_FLAGS : PIP_VALUE_FLAGS).flatMap((word2) => {
4890
+ const spec2 = npm ? npmSpec(word2) : pypiSpec(word2);
4891
+ return spec2 === void 0 ? [] : [{ ...spec2, ecosystem: installer.ecosystem, tool: installer.tool }];
4892
+ });
4893
+ };
4894
+ var packagesInCommand = (text9, dialect = "bash") => normalizeCommand(text9, dialect).commands.flatMap((command) => {
4895
+ const installer = installerOf(command);
4896
+ return installer === void 0 ? [] : requested(installer);
4897
+ });
4898
+
4899
+ // src/deps/registry.ts
4900
+ var REGISTRY_HOSTS = {
4901
+ npm: "registry.npmjs.org",
4902
+ pypi: "pypi.org"
4903
+ };
4904
+ var urlFor = (pkg) => pkg.ecosystem === "npm" ? `https://${REGISTRY_HOSTS.npm}/${pkg.name.replace("/", "%2F")}` : `https://${REGISTRY_HOSTS.pypi}/pypi/${encodeURIComponent(pkg.name)}/json`;
4905
+ var registryRequest = (pkg, cwd, timeoutMs) => ({
4906
+ command: "curl",
4907
+ args: [
4908
+ "--silent",
4909
+ "--show-error",
4910
+ "--location",
4911
+ "--proto",
4912
+ "=https",
4913
+ "--max-time",
4914
+ (Math.max(1, timeoutMs) / 1e3).toFixed(1),
4915
+ "--write-out",
4916
+ "\n%{http_code}",
4917
+ urlFor(pkg)
4918
+ ],
4919
+ cwd,
4920
+ timeoutMs: timeoutMs + 500
4921
+ });
4922
+ var timeOf = (value) => {
4923
+ if (typeof value !== "string") return void 0;
4924
+ const parsed = Date.parse(value);
4925
+ return Number.isNaN(parsed) ? void 0 : parsed;
4926
+ };
4927
+ var record = (value, key) => {
4928
+ const inner = isRecord(value) ? value[key] : void 0;
4929
+ return isRecord(inner) ? inner : {};
4930
+ };
4931
+ var INSTALL_SCRIPTS = ["preinstall", "install", "postinstall"];
4932
+ var hasInstallScript = (manifest) => {
4933
+ if (manifest["hasInstallScript"] === true) return true;
4934
+ const scripts = record(manifest, "scripts");
4935
+ return INSTALL_SCRIPTS.some((name) => typeof scripts[name] === "string");
4936
+ };
4937
+ var npmFacts = (doc, wanted) => {
4938
+ const times = record(doc, "time");
4939
+ const versions = record(doc, "versions");
4940
+ const tags = record(doc, "dist-tags");
4941
+ const tagged = wanted === void 0 ? tags["latest"] : tags[wanted] ?? wanted;
4942
+ const version = typeof tagged === "string" && tagged in versions ? tagged : void 0;
4943
+ const ordered = Object.keys(versions).sort((left, right) => (timeOf(times[left]) ?? 0) - (timeOf(times[right]) ?? 0));
4944
+ const index = version === void 0 ? -1 : ordered.indexOf(version);
4945
+ const previous = index > 0 ? ordered[index - 1] : void 0;
4946
+ return {
4947
+ createdMs: timeOf(times["created"]),
4948
+ versionCount: ordered.length,
4949
+ version,
4950
+ versionMs: version === void 0 ? void 0 : timeOf(times[version]),
4951
+ installScript: version === void 0 ? false : hasInstallScript(record(versions, version)),
4952
+ previousInstallScript: previous === void 0 ? false : hasInstallScript(record(versions, previous))
4953
+ };
4954
+ };
4955
+ var uploadMs = (files) => {
4956
+ if (!Array.isArray(files)) return void 0;
4957
+ const times = files.flatMap((file) => {
4958
+ const time = isRecord(file) ? timeOf(file["upload_time_iso_8601"] ?? file["upload_time"]) : void 0;
4959
+ return time === void 0 ? [] : [time];
4960
+ });
4961
+ return times.length === 0 ? void 0 : Math.min(...times);
4962
+ };
4963
+ var pypiFacts = (doc, wanted) => {
4964
+ const releases = record(doc, "releases");
4965
+ const latest = record(doc, "info")["version"];
4966
+ const chosen = wanted ?? (typeof latest === "string" ? latest : void 0);
4967
+ const version = chosen !== void 0 && chosen in releases ? chosen : void 0;
4968
+ const uploads = Object.values(releases).flatMap((files) => {
4969
+ const time = uploadMs(files);
4970
+ return time === void 0 ? [] : [time];
4971
+ });
4972
+ return {
4973
+ createdMs: uploads.length === 0 ? void 0 : Math.min(...uploads),
4974
+ versionCount: uploads.length,
4975
+ version,
4976
+ versionMs: version === void 0 ? void 0 : uploadMs(releases[version]),
4977
+ installScript: false,
4978
+ previousInstallScript: false
4979
+ };
4980
+ };
4981
+ var lookupOf = (pkg, result) => {
4982
+ if (!result.found || result.exitCode !== 0) return { kind: "unreachable" };
4983
+ const lines3 = result.stdout.trimEnd().split("\n");
4984
+ const status = Number.parseInt(lines3.at(-1) ?? "", 10);
4985
+ if (status === 404) return { kind: "missing" };
4986
+ if (status < 200 || status >= 300) return { kind: "unreachable" };
4987
+ const doc = parseJsonObject(lines3.slice(0, -1).join("\n"));
4988
+ if (!doc.ok) return { kind: "unreachable" };
4989
+ const facts = pkg.ecosystem === "npm" ? npmFacts(doc.value, pkg.version) : pypiFacts(doc.value, pkg.version);
4990
+ return { kind: "found", facts };
4991
+ };
4992
+
4993
+ // src/deps/guard.ts
4994
+ var NETWORK_BUDGET_MS = 2e3;
4995
+ var MAX_PACKAGES = 5;
4996
+ var MIN_REQUEST_MS = 500;
4997
+ var ALLOW = { kind: "allow" };
4998
+ var NPM_SECTIONS = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
4999
+ var PYTHON_MANIFESTS = ["pyproject.toml", "requirements.txt", "requirements-dev.txt", "Pipfile"];
5000
+ var npmDeclared = (runtime, root) => {
5001
+ const text9 = runtime.readText(toAbsolutePath("package.json", root));
5002
+ const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
5003
+ if (parsed?.ok !== true) return /* @__PURE__ */ new Set();
5004
+ return new Set(
5005
+ NPM_SECTIONS.flatMap((section2) => {
5006
+ const value = parsed.value[section2];
5007
+ return isRecord(value) ? Object.keys(value) : [];
5008
+ })
5009
+ );
5010
+ };
5011
+ var pythonText = (runtime, root) => PYTHON_MANIFESTS.map((file) => {
5012
+ const text9 = runtime.readText(toAbsolutePath(file, root));
5013
+ return text9.ok ? text9.value.toLowerCase() : "";
5014
+ }).join("\n");
5015
+ var escape = (text9) => text9.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5016
+ var newPackages = (input, packages) => {
5017
+ const root = guardRoot(input);
5018
+ const npm = npmDeclared(input.runtime, root);
5019
+ const python = packages.some((pkg) => pkg.ecosystem === "pypi") ? pythonText(input.runtime, root) : "";
5020
+ return packages.filter(
5021
+ (pkg) => pkg.ecosystem === "npm" ? !npm.has(pkg.name) : !new RegExp(`(^|[\\s"'\\[])${escape(pkg.name).replace(/-/g, "[-_.]")}($|[\\s"'=<>~!\\[;,])`, "m").test(python)
5022
+ );
5023
+ };
5024
+ var lookup = (input, pkg, deadline, offline) => {
5025
+ const remaining = deadline - Date.now();
5026
+ if (offline || remaining < MIN_REQUEST_MS) return { kind: "unreachable" };
5027
+ return lookupOf(pkg, input.runtime.exec(registryRequest(pkg, input.cwd, remaining)));
5028
+ };
5029
+ var describe = (t, finding4) => {
5030
+ switch (finding4.kind) {
5031
+ case "missing":
5032
+ return t("deps.finding.missing", { name: finding4.name, registry: REGISTRY_HOSTS[finding4.ecosystem] });
5033
+ case "typo":
5034
+ return t("deps.finding.typo", { name: finding4.name, popular: finding4.popular });
5035
+ case "young":
5036
+ return t("deps.finding.young", { name: finding4.name, days: finding4.days, count: finding4.count });
5037
+ case "fresh":
5038
+ return t("deps.finding.fresh", {
5039
+ name: finding4.name,
5040
+ version: finding4.version,
5041
+ hours: finding4.hours,
5042
+ days: MIN_VERSION_AGE_DAYS
5043
+ });
5044
+ case "script":
5045
+ return t("deps.finding.script", { name: finding4.name, version: finding4.version });
5046
+ case "unchecked":
5047
+ return t("deps.finding.unchecked", { name: finding4.name, registry: REGISTRY_HOSTS[finding4.ecosystem] });
5048
+ }
5049
+ };
5050
+ var isOffline = (env) => {
5051
+ const value = env["FINITEMONKEY_OFFLINE"];
5052
+ return value !== void 0 && value !== "" && value !== "0";
5053
+ };
5054
+ var depsGuard = (input) => {
5055
+ const tool = toolName(input.payload);
5056
+ if (!SHELL_TOOLS.has(tool)) return ALLOW;
5057
+ const command = optionalString(toolInput(input.payload), "command") ?? "";
5058
+ const packages = newPackages(input, packagesInCommand(command, dialectOf(tool))).slice(0, MAX_PACKAGES);
5059
+ if (packages.length === 0) return ALLOW;
5060
+ const deadline = Date.now() + NETWORK_BUDGET_MS;
5061
+ const offline = isOffline(input.env);
5062
+ const now = nowMs(input.runtime);
5063
+ const findings = packages.flatMap((pkg) => assessPackage(pkg, lookup(input, pkg, deadline, offline), now));
5064
+ if (findings.length === 0) return ALLOW;
5065
+ const lines3 = findings.map((finding4) => `- ${describe(input.t, finding4)}`);
5066
+ const additionalContext = `finitemonkey: ${input.t("deps.warn.header")}
5067
+ ${lines3.join("\n")}`;
5068
+ return {
5069
+ kind: "allow",
5070
+ output: {
5071
+ hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext },
5072
+ systemMessage: `finitemonkey: ${input.t("deps.warn.human", { count: findings.length })}`
5073
+ }
5074
+ };
5075
+ };
5076
+
5077
+ // src/gate/hooks/common.ts
5078
+ import { dirname as dirname5, join as join13 } from "node:path";
5079
+ var DEFAULT_GATE_BUDGET_MS = 3e5;
5080
+ var sessionCwd = (input) => input.payload.cwd === void 0 ? input.cwd : toAbsolutePath(input.payload.cwd, input.cwd);
5081
+ var MAX_DEPTH2 = 64;
5082
+ var findRepoRoot = (runtime, start) => {
5083
+ let current2 = start;
5084
+ for (let depth = 0; depth < MAX_DEPTH2; depth += 1) {
5085
+ if (runtime.stat(toAbsolutePath(join13(current2, ".git"))) !== void 0) return current2;
5086
+ const parent = toAbsolutePath(dirname5(current2));
5087
+ if (parent === current2) return void 0;
5088
+ current2 = parent;
5089
+ }
5090
+ return void 0;
5091
+ };
5092
+ var projectPaths = (input) => {
5093
+ const root = findRepoRoot(input.runtime, sessionCwd(input));
5094
+ return root === void 0 ? void 0 : fmPaths(root);
5095
+ };
5096
+ var gateBudget = (input) => {
5097
+ const parsed = Number.parseInt(input.env["FINITEMONKEY_GATE_BUDGET_MS"] ?? "", 10);
5098
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_GATE_BUDGET_MS;
5099
+ };
5100
+ var allowWith = (output) => ({ kind: "allow", output });
5101
+ var contextOutput = (event, additionalContext, systemMessage) => ({
5102
+ hookSpecificOutput: { hookEventName: event, additionalContext },
5103
+ ...systemMessage === void 0 ? {} : { systemMessage }
5104
+ });
5105
+ var human = (text9) => `finitemonkey: ${text9}`;
5106
+
5107
+ // src/hooks/fields.ts
5108
+ var textField = (record4, ...names) => {
5109
+ for (const name of names) {
5110
+ const value = record4[name];
5111
+ if (typeof value === "string") return value;
5112
+ }
5113
+ return void 0;
5114
+ };
5115
+ var toolName2 = (raw) => textField(raw, "tool_name") ?? "";
5116
+ var toolInput2 = (raw) => {
5117
+ const value = raw["tool_input"];
5118
+ return isRecord(value) ? value : {};
5119
+ };
5120
+ var editsOf = (input) => {
5121
+ const single2 = textField(input, "old_string");
5122
+ const replacement = textField(input, "new_string");
5123
+ if (single2 !== void 0 && replacement !== void 0) {
5124
+ return [{ oldText: single2, newText: replacement, all: input["replace_all"] === true }];
5125
+ }
5126
+ const edits = input["edits"];
5127
+ if (!Array.isArray(edits)) return [];
5128
+ return edits.flatMap((edit) => {
5129
+ if (!isRecord(edit)) return [];
5130
+ const oldText = textField(edit, "old_string");
5131
+ const newText = textField(edit, "new_string");
5132
+ return oldText === void 0 || newText === void 0 ? [] : [{ oldText, newText, all: edit["replace_all"] === true }];
5133
+ });
5134
+ };
5135
+ var applyEdits = (before, edits) => edits.reduce(
5136
+ (text9, edit) => edit.all ? text9.split(edit.oldText).join(edit.newText) : text9.replace(edit.oldText, () => edit.newText),
5137
+ before
5138
+ );
5139
+
4596
5140
  // src/guards/shell.ts
4597
5141
  var HEREDOC = /^<<[-<]?$/;
4598
5142
  var parseShell = (text9) => normalizeCommand(text9).commands.map((command) => ({
@@ -4688,8 +5232,8 @@ var detectBypass = (commandLine) => parseShell(commandLine).flatMap((command) =>
4688
5232
 
4689
5233
  // src/guards/bypass/hook.ts
4690
5234
  var bypassGuard = (input) => {
4691
- if (!SHELL_TOOLS.has(toolName(input.payload.raw))) return { kind: "allow" };
4692
- const command = textField(toolInput(input.payload.raw), "command") ?? "";
5235
+ if (!SHELL_TOOLS.has(toolName2(input.payload.raw))) return { kind: "allow" };
5236
+ const command = textField(toolInput2(input.payload.raw), "command") ?? "";
4693
5237
  const findings = detectBypass(command);
4694
5238
  const first = findings[0];
4695
5239
  if (first === void 0 || grantFor(input, "bypass") !== void 0) return { kind: "allow" };
@@ -4709,47 +5253,6 @@ var reviewAgentGuard = (input) => {
4709
5253
  return { kind: "block", reason: input.t("review.guard.read_only", { agent, tool }) };
4710
5254
  };
4711
5255
 
4712
- // src/secrets/guards/tool-input.ts
4713
- var toolName2 = (payload) => optionalString(payload.raw, "tool_name") ?? "";
4714
- var toolInput2 = (payload) => {
4715
- const input = payload.raw["tool_input"];
4716
- return isRecord(input) ? input : {};
4717
- };
4718
- var editTexts = (input, file) => {
4719
- const edits = input["edits"];
4720
- if (!Array.isArray(edits)) return [];
4721
- return edits.flatMap((edit) => {
4722
- if (!isRecord(edit)) return [];
4723
- const text9 = optionalString(edit, "new_string");
4724
- return text9 === void 0 ? [] : [{ file, text: text9, whole: false }];
4725
- });
4726
- };
4727
- var writtenTexts = (tool, input) => {
4728
- const file = optionalString(input, "file_path") ?? optionalString(input, "notebook_path") ?? "";
4729
- const single2 = (key, whole) => {
4730
- const text9 = optionalString(input, key);
4731
- return text9 === void 0 ? [] : [{ file, text: text9, whole }];
4732
- };
4733
- switch (tool) {
4734
- case "Write":
4735
- return single2("content", true);
4736
- case "Edit":
4737
- return single2("new_string", false);
4738
- case "MultiEdit":
4739
- return editTexts(input, file);
4740
- case "NotebookEdit":
4741
- return single2("new_source", false);
4742
- default:
4743
- return [];
4744
- }
4745
- };
4746
- var stringLeaves = (value, out = []) => {
4747
- if (typeof value === "string") out.push(value);
4748
- else if (Array.isArray(value)) for (const item2 of value) stringLeaves(item2, out);
4749
- else if (isRecord(value)) for (const item2 of Object.values(value)) stringLeaves(item2, out);
4750
- return out;
4751
- };
4752
-
4753
5256
  // src/prod/guard.ts
4754
5257
  import { join as join15 } from "node:path";
4755
5258
 
@@ -5343,7 +5846,7 @@ var toolUseId = (input) => {
5343
5846
  const id = optionalString(input.payload.raw, "tool_use_id");
5344
5847
  return id !== void 0 && /^[A-Za-z0-9_-]{1,120}$/.test(id) ? id : void 0;
5345
5848
  };
5346
- var commandOf = (input) => SHELL_TOOLS.has(toolName2(input.payload)) ? optionalString(toolInput2(input.payload), "command") : void 0;
5849
+ var commandOf = (input) => SHELL_TOOLS.has(toolName(input.payload)) ? optionalString(toolInput(input.payload), "command") : void 0;
5347
5850
  var checkedPath = (paths, id) => paths.cache(`checked-${id}.json`);
5348
5851
  var same = (left, right) => left.trim().replace(/\s+/g, " ") === right.trim().replace(/\s+/g, " ");
5349
5852
  var recordChecked = (input) => {
@@ -5351,7 +5854,7 @@ var recordChecked = (input) => {
5351
5854
  const command = commandOf(input);
5352
5855
  const paths = projectPaths(input);
5353
5856
  if (id === void 0 || command === void 0 || paths === void 0) return { kind: "allow" };
5354
- if (!isSensitiveCommand(command, dialectOf(toolName2(input.payload)))) return { kind: "allow" };
5857
+ if (!isSensitiveCommand(command, dialectOf(toolName(input.payload)))) return { kind: "allow" };
5355
5858
  input.runtime.writeText(checkedPath(paths, id), `${JSON.stringify({ command })}
5356
5859
  `);
5357
5860
  return { kind: "allow" };
@@ -5367,18 +5870,18 @@ var compareRan = (input) => {
5367
5870
  const checked = parsed.ok ? optionalString(parsed.value, "command") : void 0;
5368
5871
  const ran = commandOf(input) ?? "";
5369
5872
  if (checked === void 0 || same(checked, ran)) return { kind: "allow" };
5370
- const record3 = {
5371
- tool: toolName2(input.payload),
5873
+ const record4 = {
5874
+ tool: toolName(input.payload),
5372
5875
  checked: maskText(checked),
5373
5876
  ran: maskText(ran),
5374
5877
  sessionId: input.payload.sessionId ?? null
5375
5878
  };
5376
- logEvent(input.runtime, paths, "input_rewritten", record3);
5377
- input.runtime.writeText(paths.cache(REWRITTEN), `${JSON.stringify(record3)}
5879
+ logEvent(input.runtime, paths, "input_rewritten", record4);
5880
+ input.runtime.writeText(paths.cache(REWRITTEN), `${JSON.stringify(record4)}
5378
5881
  `);
5379
5882
  return {
5380
5883
  kind: "block",
5381
- reason: modelReason(input.t, "gate.guard.rewrite", { checked: record3.checked, ran: record3.ran }),
5884
+ reason: modelReason(input.t, "gate.guard.rewrite", { checked: record4.checked, ran: record4.ran }),
5382
5885
  human: input.t("gate.guard.rewrite.human")
5383
5886
  };
5384
5887
  };
@@ -5464,8 +5967,8 @@ var bashSelfTargets = (root, commandLine) => parseShell(commandLine).flatMap(
5464
5967
 
5465
5968
  // src/guards/self/hook.ts
5466
5969
  var targets = (input) => {
5467
- const tool = toolName(input.payload.raw);
5468
- const args2 = toolInput(input.payload.raw);
5970
+ const tool = toolName2(input.payload.raw);
5971
+ const args2 = toolInput2(input.payload.raw);
5469
5972
  const root = guardRoot(input);
5470
5973
  if (FILE_TOOLS.has(tool)) {
5471
5974
  const path = textField(args2, "file_path", "notebook_path") ?? "";
@@ -5664,8 +6167,8 @@ var proposed = (tool, args2, before) => {
5664
6167
  return before === void 0 ? void 0 : applyEdits(before, editsOf(args2));
5665
6168
  };
5666
6169
  var fileViolations = (input, root) => {
5667
- const tool = toolName(input.payload.raw);
5668
- const args2 = toolInput(input.payload.raw);
6170
+ const tool = toolName2(input.payload.raw);
6171
+ const args2 = toolInput2(input.payload.raw);
5669
6172
  const file = textField(args2, "file_path") ?? "";
5670
6173
  if (tool === "NotebookEdit" || file === "") return [];
5671
6174
  const local = localPath(root, file);
@@ -5678,7 +6181,7 @@ var fileViolations = (input, root) => {
5678
6181
  return [...compareTestFile(local, before, after), ...compareConfig(local, before, after, accepted2)];
5679
6182
  };
5680
6183
  var shellViolations = (input, root) => {
5681
- const command = textField(toolInput(input.payload.raw), "command") ?? "";
6184
+ const command = textField(toolInput2(input.payload.raw), "command") ?? "";
5682
6185
  return parseShell(command).flatMap(
5683
6186
  (simple) => writtenPaths(simple).flatMap((path) => {
5684
6187
  const local = localPath(root, path);
@@ -5690,7 +6193,7 @@ var shellViolations = (input, root) => {
5690
6193
  );
5691
6194
  };
5692
6195
  var testsGuard = (input) => {
5693
- const tool = toolName(input.payload.raw);
6196
+ const tool = toolName2(input.payload.raw);
5694
6197
  const root = guardRoot(input);
5695
6198
  const found = FILE_TOOLS.has(tool) ? fileViolations(input, root) : SHELL_TOOLS.has(tool) ? shellViolations(input, root) : [];
5696
6199
  const first = found[0];
@@ -5718,8 +6221,8 @@ var lookCaptureOf = (paths, file) => {
5718
6221
  return relativePath.startsWith(".finitemonkey/look/") && relativePath.endsWith(".png") ? relativePath : void 0;
5719
6222
  };
5720
6223
  var lookReadTracker = (input) => {
5721
- if (toolName(input.payload.raw) !== "Read") return { kind: "allow" };
5722
- const file = textField(toolInput(input.payload.raw), "file_path");
6224
+ if (toolName2(input.payload.raw) !== "Read") return { kind: "allow" };
6225
+ const file = textField(toolInput2(input.payload.raw), "file_path");
5723
6226
  const paths = projectPaths(input);
5724
6227
  if (file === void 0 || paths === void 0) return { kind: "allow" };
5725
6228
  const capture2 = lookCaptureOf(paths, file);
@@ -6220,8 +6723,8 @@ var TASK_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
6220
6723
  var TASK_SCHEMA = "finitemonkey/task@1";
6221
6724
 
6222
6725
  // src/task/validate.ts
6223
- var str = (record3, key, at, issues, required) => {
6224
- const value = record3[key];
6726
+ var str = (record4, key, at, issues, required) => {
6727
+ const value = record4[key];
6225
6728
  if (value === void 0 || value === null) {
6226
6729
  if (required) issues.push(`${at}${key}: required`);
6227
6730
  return void 0;
@@ -6232,8 +6735,8 @@ var str = (record3, key, at, issues, required) => {
6232
6735
  }
6233
6736
  return value;
6234
6737
  };
6235
- var positive2 = (record3, key, at, issues) => {
6236
- const value = record3[key];
6738
+ var positive2 = (record4, key, at, issues) => {
6739
+ const value = record4[key];
6237
6740
  if (value === void 0 || value === null) return void 0;
6238
6741
  if (typeof value === "number" && Number.isInteger(value) && value > 0) return value;
6239
6742
  issues.push(`${at}${key}: must be a positive integer`);
@@ -6252,13 +6755,13 @@ var gitRequire = (value, at, issues) => {
6252
6755
  if (known2.some((item2) => item2 === void 0)) issues.push(`${at}: use ${GIT_REQUIREMENTS.join(", ")}`);
6253
6756
  return known2.filter((item2) => item2 !== void 0);
6254
6757
  };
6255
- var appFields = (record3, kind, at, issues) => {
6256
- const port = positive2(record3, "port", at, issues);
6758
+ var appFields = (record4, kind, at, issues) => {
6759
+ const port = positive2(record4, "port", at, issues);
6257
6760
  if (kind === "runtime" && port === void 0) issues.push(`${at}port: required`);
6258
6761
  if (port !== void 0 && port > 65535) issues.push(`${at}port: must be at most 65535`);
6259
- const status = positive2(record3, "status", at, issues);
6260
- const route = str(record3, "route", at, issues, false);
6261
- const body2 = str(record3, "body", at, issues, false);
6762
+ const status = positive2(record4, "status", at, issues);
6763
+ const route = str(record4, "route", at, issues, false);
6764
+ const body2 = str(record4, "body", at, issues, false);
6262
6765
  return {
6263
6766
  ...port === void 0 ? {} : { port },
6264
6767
  ...route === void 0 ? {} : { route },
@@ -6266,13 +6769,13 @@ var appFields = (record3, kind, at, issues) => {
6266
6769
  ...body2 === void 0 ? {} : { body: body2 }
6267
6770
  };
6268
6771
  };
6269
- var checkFields = (record3, kind, at, issues) => {
6270
- const run4 = str(record3, "run", at, issues, kind === "command");
6271
- const timeoutS = positive2(record3, "timeout_s", at, issues);
6272
- const description = str(record3, "description", at, issues, kind === "manual");
6273
- const require2 = kind === "git" ? gitRequire(record3["require"] ?? ["committed"], `${at}require`, issues) : void 0;
6274
- const app = appFields(record3, kind, at, issues);
6275
- const open = record3["require_open"];
6772
+ var checkFields = (record4, kind, at, issues) => {
6773
+ const run4 = str(record4, "run", at, issues, kind === "command");
6774
+ const timeoutS = positive2(record4, "timeout_s", at, issues);
6775
+ const description = str(record4, "description", at, issues, kind === "manual");
6776
+ const require2 = kind === "git" ? gitRequire(record4["require"] ?? ["committed"], `${at}require`, issues) : void 0;
6777
+ const app = appFields(record4, kind, at, issues);
6778
+ const open = record4["require_open"];
6276
6779
  if (open !== void 0 && typeof open !== "boolean") issues.push(`${at}require_open: must be true or false`);
6277
6780
  return {
6278
6781
  ...run4 === void 0 ? {} : { run: run4 },
@@ -6571,18 +7074,18 @@ var ciChecks = (value) => Array.isArray(value) ? value.flatMap((item2) => {
6571
7074
  var parsePrView = (text9) => {
6572
7075
  const parsed = parseJsonObject(text9);
6573
7076
  if (!parsed.ok) return void 0;
6574
- const record3 = parsed.value;
6575
- const number2 = record3["number"];
7077
+ const record4 = parsed.value;
7078
+ const number2 = record4["number"];
6576
7079
  if (typeof number2 !== "number") return void 0;
6577
7080
  return {
6578
7081
  number: number2,
6579
- url: optionalString(record3, "url") ?? "",
6580
- state: optionalString(record3, "state") ?? "",
6581
- mergeable: optionalString(record3, "mergeable") ?? "UNKNOWN",
6582
- mergeStateStatus: optionalString(record3, "mergeStateStatus") ?? "UNKNOWN",
6583
- baseRefName: optionalString(record3, "baseRefName") ?? "",
6584
- headRefOid: optionalString(record3, "headRefOid") ?? "",
6585
- checks: ciChecks(record3["statusCheckRollup"])
7082
+ url: optionalString(record4, "url") ?? "",
7083
+ state: optionalString(record4, "state") ?? "",
7084
+ mergeable: optionalString(record4, "mergeable") ?? "UNKNOWN",
7085
+ mergeStateStatus: optionalString(record4, "mergeStateStatus") ?? "UNKNOWN",
7086
+ baseRefName: optionalString(record4, "baseRefName") ?? "",
7087
+ headRefOid: optionalString(record4, "headRefOid") ?? "",
7088
+ checks: ciChecks(record4["statusCheckRollup"])
6586
7089
  };
6587
7090
  };
6588
7091
  var isConflicting = (pr) => pr.mergeable === "CONFLICTING" || pr.mergeStateStatus === "DIRTY";
@@ -6643,7 +7146,7 @@ var conflictFiles = (git2, base) => {
6643
7146
  };
6644
7147
 
6645
7148
  // src/core/glob.ts
6646
- var escape = (char) => /[.+^${}()|[\]\\]/.test(char) ? `\\${char}` : char;
7149
+ var escape2 = (char) => /[.+^${}()|[\]\\]/.test(char) ? `\\${char}` : char;
6647
7150
  var globToRegExp = (glob) => {
6648
7151
  const pattern = glob.replace(/^\.\//, "");
6649
7152
  let out = "";
@@ -6655,7 +7158,7 @@ var globToRegExp = (glob) => {
6655
7158
  index += slash ? 2 : 1;
6656
7159
  } else if (char === "*") out += "[^/]*";
6657
7160
  else if (char === "?") out += "[^/]";
6658
- else out += escape(char);
7161
+ else out += escape2(char);
6659
7162
  }
6660
7163
  return new RegExp(`^${out}${pattern.endsWith("/") ? ".*" : ""}$`);
6661
7164
  };
@@ -7015,19 +7518,19 @@ var params = (value) => isRecord(value) ? Object.fromEntries(
7015
7518
  var notes = (value) => Array.isArray(value) ? value.flatMap(
7016
7519
  (item2) => isRecord(item2) && typeof item2["key"] === "string" ? [{ key: item2["key"], params: params(item2["params"]) }] : []
7017
7520
  ) : [];
7018
- var text4 = (record3, key) => {
7019
- const value = record3[key];
7521
+ var text4 = (record4, key) => {
7522
+ const value = record4[key];
7020
7523
  return typeof value === "string" ? value : void 0;
7021
7524
  };
7022
- var optional = (record3) => {
7023
- const exitCode = record3["exitCode"];
7024
- const durationMs = record3["durationMs"];
7025
- const command = text4(record3, "command");
7026
- const stdoutSha256 = text4(record3, "stdoutSha256");
7027
- const releasedBy = text4(record3, "releasedBy");
7028
- const reason = oneOf2(UNVERIFIED_REASONS, record3["reason"]);
7029
- const failures = record3["failures"];
7030
- const testCount = record3["testCount"];
7525
+ var optional = (record4) => {
7526
+ const exitCode = record4["exitCode"];
7527
+ const durationMs = record4["durationMs"];
7528
+ const command = text4(record4, "command");
7529
+ const stdoutSha256 = text4(record4, "stdoutSha256");
7530
+ const releasedBy = text4(record4, "releasedBy");
7531
+ const reason = oneOf2(UNVERIFIED_REASONS, record4["reason"]);
7532
+ const failures = record4["failures"];
7533
+ const testCount = record4["testCount"];
7031
7534
  return {
7032
7535
  ...command === void 0 ? {} : { command },
7033
7536
  ...typeof exitCode === "number" || exitCode === null ? { exitCode } : {},
@@ -7042,14 +7545,14 @@ var optional = (record3) => {
7042
7545
  var parseEvidence = (line2) => {
7043
7546
  const parsed = parseJsonObject(line2);
7044
7547
  if (!parsed.ok) return void 0;
7045
- const record3 = parsed.value;
7046
- const status = oneOf2(EVIDENCE_STATUSES, record3["status"]);
7047
- const task = text4(record3, "task");
7048
- const check2 = text4(record3, "check");
7049
- const kind = text4(record3, "kind");
7050
- const treeHash2 = text4(record3, "treeHash");
7051
- const at = text4(record3, "at");
7052
- if (record3["schema"] !== EVIDENCE_SCHEMA || status === void 0) return void 0;
7548
+ const record4 = parsed.value;
7549
+ const status = oneOf2(EVIDENCE_STATUSES, record4["status"]);
7550
+ const task = text4(record4, "task");
7551
+ const check2 = text4(record4, "check");
7552
+ const kind = text4(record4, "kind");
7553
+ const treeHash2 = text4(record4, "treeHash");
7554
+ const at = text4(record4, "at");
7555
+ if (record4["schema"] !== EVIDENCE_SCHEMA || status === void 0) return void 0;
7053
7556
  if (task === void 0 || check2 === void 0 || kind === void 0 || treeHash2 === void 0 || at === void 0) {
7054
7557
  return void 0;
7055
7558
  }
@@ -7061,8 +7564,8 @@ var parseEvidence = (line2) => {
7061
7564
  status,
7062
7565
  treeHash: treeHash2,
7063
7566
  at,
7064
- notes: notes(record3["notes"]),
7065
- ...optional(record3)
7567
+ notes: notes(record4["notes"]),
7568
+ ...optional(record4)
7066
7569
  };
7067
7570
  };
7068
7571
  var readLedger2 = (runtime, path) => {
@@ -7774,7 +8277,15 @@ var en_default = {
7774
8277
  "gate.guard.rewrite.not": "a rewrite that looks equivalent, or running the same command again through the same hook",
7775
8278
  "gate.guard.rewrite.human": "Another hook changed a sensitive command after Finite Monkey checked it; both versions are in .finitemonkey/log.jsonl.",
7776
8279
  "doctor.hooks.rewriters": "{hooks, plural, one {# other hook sees} other {# other hooks see}} shell commands before they run and can rewrite them ({list}); a sensitive command they change fails the gate",
7777
- "doctor.hooks.fix_rewriters": "If a hook rewrites commands (updatedInput), make it skip git, rm, ssh, kubectl and .env commands, or remove it"
8280
+ "doctor.hooks.fix_rewriters": "If a hook rewrites commands (updatedInput), make it skip git, rm, ssh, kubectl and .env commands, or remove it",
8281
+ "deps.warn.header": "Check these new packages before installing them (a warning, nothing was blocked):",
8282
+ "deps.warn.human": "{count, plural, one {# warning about a new package} other {# warnings about new packages}}; see the details before installing.",
8283
+ "deps.finding.missing": "{name} does not exist on {registry}; the name may be made up.",
8284
+ "deps.finding.typo": "{name} is one or two letters away from the popular package {popular}; check the spelling.",
8285
+ "deps.finding.young": "{name} has little history: first published {days, plural, one {# day} other {# days}} ago, {count, plural, one {# version} other {# versions}}.",
8286
+ "deps.finding.fresh": "{name}@{version} was published {hours, plural, one {# hour} other {# hours}} ago; prefer a version at least {days, plural, one {# day} other {# days}} old.",
8287
+ "deps.finding.script": "{name}@{version} adds an install script that the previous version did not have; read it before installing.",
8288
+ "deps.finding.unchecked": "{name} was not checked on {registry} (offline or no answer in time), so only the spelling was checked."
7778
8289
  };
7779
8290
 
7780
8291
  // src/i18n/es.json
@@ -8471,7 +8982,15 @@ var es_default = {
8471
8982
  "gate.guard.rewrite.not": "una reescritura que parece equivalente, o ejecutar el mismo comando otra vez por el mismo hook",
8472
8983
  "gate.guard.rewrite.human": "Otro hook cambió un comando sensible después de que Finite Monkey lo revisara; las dos versiones están en .finitemonkey/log.jsonl.",
8473
8984
  "doctor.hooks.rewriters": "{hooks, plural, one {# otro hook ve} other {# otros hooks ven}} los comandos de shell antes de ejecutarse y pueden reescribirlos ({list}); un comando sensible que cambien hace fallar la compuerta",
8474
- "doctor.hooks.fix_rewriters": "Si un hook reescribe comandos (updatedInput), haz que ignore los comandos de git, rm, ssh, kubectl y .env, o quítalo"
8985
+ "doctor.hooks.fix_rewriters": "Si un hook reescribe comandos (updatedInput), haz que ignore los comandos de git, rm, ssh, kubectl y .env, o quítalo",
8986
+ "deps.warn.header": "Revisa estos paquetes nuevos antes de instalarlos (es un aviso, no se bloqueó nada):",
8987
+ "deps.warn.human": "{count, plural, one {# aviso sobre un paquete nuevo} other {# avisos sobre paquetes nuevos}}; mira los detalles antes de instalar.",
8988
+ "deps.finding.missing": "{name} no existe en {registry}; puede que el nombre sea inventado.",
8989
+ "deps.finding.typo": "{name} está a una o dos letras del paquete popular {popular}; revisa cómo se escribe.",
8990
+ "deps.finding.young": "{name} tiene poco historial: se publicó por primera vez hace {days, plural, one {# día} other {# días}}, {count, plural, one {# versión} other {# versiones}}.",
8991
+ "deps.finding.fresh": "{name}@{version} salió hace {hours, plural, one {# hora} other {# horas}}; mejor una versión con al menos {days, plural, one {# día} other {# días}}.",
8992
+ "deps.finding.script": "{name}@{version} trae un script de instalación que la versión anterior no tenía; léelo antes de instalar.",
8993
+ "deps.finding.unchecked": "{name} no se comprobó en {registry} (sin red o sin respuesta a tiempo), así que solo se revisó la ortografía."
8475
8994
  };
8476
8995
 
8477
8996
  // src/i18n/pt-BR.json
@@ -9168,7 +9687,15 @@ var pt_BR_default = {
9168
9687
  "gate.guard.rewrite.not": "uma reescrita que parece equivalente, ou rodar o mesmo comando de novo pelo mesmo hook",
9169
9688
  "gate.guard.rewrite.human": "Outro hook mudou um comando sensível depois que o Finite Monkey o conferiu; as duas versões estão em .finitemonkey/log.jsonl.",
9170
9689
  "doctor.hooks.rewriters": "{hooks, plural, one {# outro hook vê} other {# outros hooks veem}} comandos de shell antes de rodarem e podem reescrevê-los ({list}); um comando sensível que eles mudarem reprova o portão",
9171
- "doctor.hooks.fix_rewriters": "Se um hook reescreve comandos (updatedInput), faça ele pular comandos de git, rm, ssh, kubectl e .env, ou remova o hook"
9690
+ "doctor.hooks.fix_rewriters": "Se um hook reescreve comandos (updatedInput), faça ele pular comandos de git, rm, ssh, kubectl e .env, ou remova o hook",
9691
+ "deps.warn.header": "Confira estes pacotes novos antes de instalar (é um aviso, nada foi bloqueado):",
9692
+ "deps.warn.human": "{count, plural, one {# aviso sobre pacote novo} other {# avisos sobre pacotes novos}}; veja os detalhes antes de instalar.",
9693
+ "deps.finding.missing": "{name} não existe no {registry}; o nome pode ter sido inventado.",
9694
+ "deps.finding.typo": "{name} está a uma ou duas letras do pacote popular {popular}; confira a grafia.",
9695
+ "deps.finding.young": "{name} tem pouco histórico: publicado pela primeira vez há {days, plural, one {# dia} other {# dias}}, {count, plural, one {# versão} other {# versões}}.",
9696
+ "deps.finding.fresh": "{name}@{version} saiu há {hours, plural, one {# hora} other {# horas}}; prefira uma versão com pelo menos {days, plural, one {# dia} other {# dias}}.",
9697
+ "deps.finding.script": "{name}@{version} traz um script de instalação que a versão anterior não tinha; leia antes de instalar.",
9698
+ "deps.finding.unchecked": "{name} não foi conferido no {registry} (sem rede ou sem resposta a tempo), então só a grafia foi checada."
9172
9699
  };
9173
9700
 
9174
9701
  // src/i18n/review.json
@@ -9866,7 +10393,15 @@ var review_default = {
9866
10393
  "gate.guard.rewrite.not": "machine",
9867
10394
  "gate.guard.rewrite.human": "machine",
9868
10395
  "doctor.hooks.rewriters": "machine",
9869
- "doctor.hooks.fix_rewriters": "machine"
10396
+ "doctor.hooks.fix_rewriters": "machine",
10397
+ "deps.warn.header": "machine",
10398
+ "deps.warn.human": "machine",
10399
+ "deps.finding.missing": "machine",
10400
+ "deps.finding.typo": "machine",
10401
+ "deps.finding.young": "machine",
10402
+ "deps.finding.fresh": "machine",
10403
+ "deps.finding.script": "machine",
10404
+ "deps.finding.unchecked": "machine"
9870
10405
  },
9871
10406
  es: {
9872
10407
  "command.init.summary": "machine",
@@ -10561,7 +11096,15 @@ var review_default = {
10561
11096
  "gate.guard.rewrite.not": "machine",
10562
11097
  "gate.guard.rewrite.human": "machine",
10563
11098
  "doctor.hooks.rewriters": "machine",
10564
- "doctor.hooks.fix_rewriters": "machine"
11099
+ "doctor.hooks.fix_rewriters": "machine",
11100
+ "deps.warn.header": "machine",
11101
+ "deps.warn.human": "machine",
11102
+ "deps.finding.missing": "machine",
11103
+ "deps.finding.typo": "machine",
11104
+ "deps.finding.young": "machine",
11105
+ "deps.finding.fresh": "machine",
11106
+ "deps.finding.script": "machine",
11107
+ "deps.finding.unchecked": "machine"
10565
11108
  }
10566
11109
  };
10567
11110
 
@@ -10845,8 +11388,8 @@ var FLAGGED_EVENT = "callers.flagged";
10845
11388
  var RELEASED_EVENT = "callers.released";
10846
11389
  var MIN_SAMPLE = 20;
10847
11390
  var MAX_FALSE_POSITIVE = 0.05;
10848
- var pairs = (record3) => {
10849
- const { task, names } = record3;
11391
+ var pairs = (record4) => {
11392
+ const { task, names } = record4;
10850
11393
  if (typeof task !== "string" || !Array.isArray(names)) return [];
10851
11394
  return names.filter((name) => typeof name === "string").map((name) => `${task}\0${name}`);
10852
11395
  };
@@ -11063,7 +11606,7 @@ var siteNotes = (open) => [
11063
11606
  ...[...open].map(([name, sites]) => note("gate.callers.open", { name, count: sites.length })),
11064
11607
  ...[...open.values()].flat().slice(0, MAX_SITES).map((site) => note("gate.callers.at", { name: site.name, file: site.file, line: site.line }))
11065
11608
  ];
11066
- var record = (ctx, event, names) => {
11609
+ var record2 = (ctx, event, names) => {
11067
11610
  if (names.length > 0) logEvent(ctx.runtime, ctx.paths, event, { task: ctx.contract.id, names: [...names] });
11068
11611
  };
11069
11612
  var callersCheck = (_check, ctx) => {
@@ -11071,12 +11614,12 @@ var callersCheck = (_check, ctx) => {
11071
11614
  const grouped = byName(callSites(ctx.runtime, ctx.git, symbols));
11072
11615
  const ignore = new Set(ctx.contract.callersIgnore ?? []);
11073
11616
  const open = new Map([...grouped].filter(([name]) => !ignore.has(name)));
11074
- record(
11617
+ record2(
11075
11618
  ctx,
11076
11619
  RELEASED_EVENT,
11077
11620
  [...grouped.keys()].filter((name) => ignore.has(name))
11078
11621
  );
11079
- record(ctx, FLAGGED_EVENT, [...open.keys()]);
11622
+ record2(ctx, FLAGGED_EVENT, [...open.keys()]);
11080
11623
  if (open.size === 0) return passed([note("gate.callers.none", { count: symbols.length })]);
11081
11624
  const rate = callersRate(ctx.runtime, ctx.paths);
11082
11625
  if (!rate.downgraded) return failed(siteNotes(open));
@@ -12287,8 +12830,8 @@ var BASELINE_STATES = ["aceito", "falso-positivo"];
12287
12830
  var EXCEPTION_MAX_DAYS = 90;
12288
12831
  var EMPTY_BASELINE = { schema: BASELINE_SCHEMA, entries: [] };
12289
12832
  var baselinePath = (root) => toAbsolutePath(join25(root, BASELINE_FILE2));
12290
- var text7 = (record3, key) => {
12291
- const value = record3[key];
12833
+ var text7 = (record4, key) => {
12834
+ const value = record4[key];
12292
12835
  return typeof value === "string" && value !== "" ? value : void 0;
12293
12836
  };
12294
12837
  var stateOf = (value) => BASELINE_STATES.find((state) => state === value);
@@ -12355,8 +12898,8 @@ var incidentId = (rule, fingerprint) => `inc-${sha256Hex(`${rule}\0${fingerprint
12355
12898
  var incidentsDir = (root) => toAbsolutePath(join26(root, INCIDENTS_DIR));
12356
12899
  var incidentFile = (root, id) => toAbsolutePath(join26(incidentsDir(root), `${id}.json`));
12357
12900
  var pick3 = (options, value) => options.find((option) => option === value);
12358
- var str2 = (record3, key) => {
12359
- const value = record3[key];
12901
+ var str2 = (record4, key) => {
12902
+ const value = record4[key];
12360
12903
  return typeof value === "string" ? value : void 0;
12361
12904
  };
12362
12905
  var parseEvent = (value) => {
@@ -12374,12 +12917,12 @@ var parseHistory = (value) => {
12374
12917
  var parseIncident = (raw) => {
12375
12918
  const parsed = parseJsonObject(raw);
12376
12919
  if (!parsed.ok) return parsed;
12377
- const record3 = parsed.value;
12378
- const [id, rule, where, fingerprint, createdAt, updatedAt] = ["id", "rule", "where", "fingerprint", "createdAt", "updatedAt"].map((key) => str2(record3, key));
12379
- const state = pick3(INCIDENT_STATES, record3["state"]);
12380
- const source = pick3(INCIDENT_SOURCES, record3["source"]);
12381
- const history = parseHistory(record3["history"]);
12382
- const firstCommit = str2(record3, "firstCommit");
12920
+ const record4 = parsed.value;
12921
+ const [id, rule, where, fingerprint, createdAt, updatedAt] = ["id", "rule", "where", "fingerprint", "createdAt", "updatedAt"].map((key) => str2(record4, key));
12922
+ const state = pick3(INCIDENT_STATES, record4["state"]);
12923
+ const source = pick3(INCIDENT_SOURCES, record4["source"]);
12924
+ const history = parseHistory(record4["history"]);
12925
+ const firstCommit = str2(record4, "firstCommit");
12383
12926
  if (id === void 0 || !ID.test(id) || rule === void 0 || where === void 0) return reject("incomplete");
12384
12927
  if (fingerprint === void 0 || createdAt === void 0 || updatedAt === void 0) return reject("incomplete");
12385
12928
  if (state === void 0 || source === void 0 || history === void 0) return reject("incomplete");
@@ -14590,23 +15133,23 @@ var headsOf = (value) => isRecord(value) ? Object.fromEntries(
14590
15133
  var parseJournalLine = (line2) => {
14591
15134
  const parsed = parseJsonObject(line2);
14592
15135
  if (!parsed.ok) return void 0;
14593
- const record3 = parsed.value;
14594
- const ref = optionalString(record3, "ref");
14595
- const commit2 = optionalString(record3, "commit");
14596
- const at = optionalString(record3, "at");
15136
+ const record4 = parsed.value;
15137
+ const ref = optionalString(record4, "ref");
15138
+ const commit2 = optionalString(record4, "commit");
15139
+ const at = optionalString(record4, "at");
14597
15140
  if (ref === void 0 || commit2 === void 0 || at === void 0) return void 0;
14598
15141
  return {
14599
15142
  schema: JOURNAL_SCHEMA,
14600
15143
  at,
14601
- op: optionalString(record3, "op") ?? "",
15144
+ op: optionalString(record4, "op") ?? "",
14602
15145
  ref,
14603
15146
  commit: commit2,
14604
- head: optionalString(record3, "head"),
14605
- branch: optionalString(record3, "branch"),
14606
- index: optionalString(record3, "index"),
14607
- owner: optionalString(record3, "owner"),
14608
- command: optionalString(record3, "command"),
14609
- heads: headsOf(record3["heads"])
15147
+ head: optionalString(record4, "head"),
15148
+ branch: optionalString(record4, "branch"),
15149
+ index: optionalString(record4, "index"),
15150
+ owner: optionalString(record4, "owner"),
15151
+ command: optionalString(record4, "command"),
15152
+ heads: headsOf(record4["heads"])
14610
15153
  };
14611
15154
  };
14612
15155
  var readJournal = (runtime, root) => {
@@ -14709,8 +15252,8 @@ var listSnapshots = (runtime, repo) => {
14709
15252
  var SHELL_TOOLS2 = /* @__PURE__ */ new Set(["Bash", "PowerShell"]);
14710
15253
  var ownerOf2 = (payload) => ownerKey(optionalString(payload.raw, "agent_id") ?? payload.sessionId);
14711
15254
  var shellText = (payload) => {
14712
- if (!SHELL_TOOLS2.has(toolName2(payload))) return void 0;
14713
- const command = optionalString(toolInput2(payload), "command") ?? "";
15255
+ if (!SHELL_TOOLS2.has(toolName(payload))) return void 0;
15256
+ const command = optionalString(toolInput(payload), "command") ?? "";
14714
15257
  return command.trim() === "" ? void 0 : command;
14715
15258
  };
14716
15259
 
@@ -14758,7 +15301,7 @@ var outcomeMessage = (t, outcome2) => {
14758
15301
  };
14759
15302
 
14760
15303
  // src/git/hooks/pre-tool.ts
14761
- var ALLOW = { kind: "allow" };
15304
+ var ALLOW2 = { kind: "allow" };
14762
15305
  var rmOutcome = (input, command, worktrees) => {
14763
15306
  if (removalTargets(command) === void 0) return void 0;
14764
15307
  const verdict = rmVerdict(input.runtime, { cwd: input.cwd, home: input.runtime.homeDir(), worktrees }, command);
@@ -14778,7 +15321,7 @@ var snapshotsOf = (input, owner, pending) => {
14778
15321
  }
14779
15322
  done.set(item2.repo.root, input.t("git.snapshot.context", { command: item2.command, ref: taken.value.ref }));
14780
15323
  }
14781
- if (done.size === 0) return ALLOW;
15324
+ if (done.size === 0) return ALLOW2;
14782
15325
  const context = [...done.values()].join("\n");
14783
15326
  return { kind: "allow", output: { hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext: context } } };
14784
15327
  };
@@ -14805,9 +15348,9 @@ var review = (input, commands, owner) => {
14805
15348
  };
14806
15349
  var gitPreToolUse = (input) => {
14807
15350
  const text9 = shellText(input.payload);
14808
- if (text9 === void 0) return ALLOW;
14809
- const commands = normalizeCommand(text9, dialectOf(toolName2(input.payload))).commands;
14810
- if (commands.length === 0) return ALLOW;
15351
+ if (text9 === void 0) return ALLOW2;
15352
+ const commands = normalizeCommand(text9, dialectOf(toolName(input.payload))).commands;
15353
+ if (commands.length === 0) return ALLOW2;
14811
15354
  const owner = ownerOf2(input.payload);
14812
15355
  const result = review(input, commands, owner);
14813
15356
  if (result.blocked !== void 0) {
@@ -14826,15 +15369,15 @@ var gitPreToolUse = (input) => {
14826
15369
 
14827
15370
  // src/git/hooks/session.ts
14828
15371
  import { dirname as dirname6, relative as relative7 } from "node:path";
14829
- var ALLOW2 = { kind: "allow" };
15372
+ var ALLOW3 = { kind: "allow" };
14830
15373
  var EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
14831
15374
  var gitSessionStart = (input) => {
14832
15375
  const repo = openRepo(input.runtime, input.cwd);
14833
15376
  if (repo !== void 0) recordBaseline(input.runtime, repo, ownerOf2(input.payload));
14834
- return ALLOW2;
15377
+ return ALLOW3;
14835
15378
  };
14836
15379
  var editedPath = (input) => {
14837
- const inputs = toolInput2(input.payload);
15380
+ const inputs = toolInput(input.payload);
14838
15381
  const raw = optionalString(inputs, "file_path") ?? optionalString(inputs, "notebook_path");
14839
15382
  if (raw === void 0 || raw === "") return void 0;
14840
15383
  const file = toAbsolutePath(raw, input.cwd);
@@ -14844,16 +15387,16 @@ var editedPath = (input) => {
14844
15387
  return path === "" || path.startsWith("..") || path.startsWith(STATE_PREFIX) ? void 0 : { repo, path };
14845
15388
  };
14846
15389
  var gitPostToolUse = (input) => {
14847
- const tool = toolName2(input.payload);
15390
+ const tool = toolName(input.payload);
14848
15391
  if (EDIT_TOOLS.has(tool)) {
14849
15392
  const edited = editedPath(input);
14850
15393
  if (edited !== void 0) recordEdits(input.runtime, edited.repo, ownerOf2(input.payload), [edited.path]);
14851
- return ALLOW2;
15394
+ return ALLOW3;
14852
15395
  }
14853
- if (!SHELL_TOOLS2.has(tool)) return ALLOW2;
15396
+ if (!SHELL_TOOLS2.has(tool)) return ALLOW3;
14854
15397
  const repo = openRepo(input.runtime, input.cwd);
14855
15398
  if (repo !== void 0) recordShellChanges(input.runtime, repo, ownerOf2(input.payload));
14856
- return ALLOW2;
15399
+ return ALLOW3;
14857
15400
  };
14858
15401
 
14859
15402
  // src/git/hooks/index.ts
@@ -14907,7 +15450,7 @@ var recordShadow = (input, paths, set, policy, reason) => {
14907
15450
  logEvent(runtime, paths, "guard_shadow", {
14908
15451
  guard: set.id,
14909
15452
  hook: input.event,
14910
- tool: toolName2(input.payload),
15453
+ tool: toolName(input.payload),
14911
15454
  wouldHave: "block",
14912
15455
  reason
14913
15456
  });
@@ -15018,7 +15561,7 @@ var heredocIn = (value, heredocs) => {
15018
15561
  };
15019
15562
 
15020
15563
  // src/prose/marker-guard.ts
15021
- var ALLOW3 = { kind: "allow" };
15564
+ var ALLOW4 = { kind: "allow" };
15022
15565
  var SHELLS2 = /* @__PURE__ */ new Set(["Bash", "PowerShell"]);
15023
15566
  var READ_ONLY = /* @__PURE__ */ new Set(["grep", "rg", "cat", "less", "head", "tail", "ls", "wc", "finitemonkey", "fm"]);
15024
15567
  var GIT_READ_ONLY = /* @__PURE__ */ new Set(["grep", "log", "show", "diff", "status", "blame"]);
@@ -15059,10 +15602,10 @@ var blocked = (input, file) => ({
15059
15602
  human: input.t("prose.block.human")
15060
15603
  });
15061
15604
  var proseMarkerGuard = (input) => {
15062
- const tool = toolName2(input.payload);
15063
- const inputs = toolInput2(input.payload);
15605
+ const tool = toolName(input.payload);
15606
+ const inputs = toolInput(input.payload);
15064
15607
  if (SHELLS2.has(tool)) {
15065
- if (!shellAddsMarker(optionalString(inputs, "command") ?? "")) return ALLOW3;
15608
+ if (!shellAddsMarker(optionalString(inputs, "command") ?? "")) return ALLOW4;
15066
15609
  return {
15067
15610
  kind: "block",
15068
15611
  reason: modelReason(input.t, "prose.block.marker_shell", { marker: ALLOW_COMMENT }),
@@ -15070,7 +15613,7 @@ var proseMarkerGuard = (input) => {
15070
15613
  };
15071
15614
  }
15072
15615
  const file = optionalString(inputs, "file_path") ?? optionalString(inputs, "notebook_path") ?? "";
15073
- return addsMarker(input, tool, inputs, file) ? blocked(input, shown2(input.cwd, file)) : ALLOW3;
15616
+ return addsMarker(input, tool, inputs, file) ? blocked(input, shown2(input.cwd, file)) : ALLOW4;
15074
15617
  };
15075
15618
 
15076
15619
  // src/secrets/project.ts
@@ -15186,7 +15729,7 @@ var describeSpans = (t, spans) => spans.slice(0, MAX_SPANS).map((span) => t("pro
15186
15729
  var describeFixes = (t, spans) => rulesOf2(spans).slice(0, MAX_FIXES).map((rule) => t("prose.fix_item", { rule, fix: t(RULE_FIX_KEYS[rule]) })).join("; ");
15187
15730
 
15188
15731
  // src/prose/text-guard.ts
15189
- var ALLOW4 = { kind: "allow" };
15732
+ var ALLOW5 = { kind: "allow" };
15190
15733
  var SHELLS3 = /* @__PURE__ */ new Set(["Bash", "PowerShell"]);
15191
15734
  var TARGET_KEYS = {
15192
15735
  commit: "prose.target.commit",
@@ -15233,15 +15776,15 @@ var decisionFor = (input, found, blocking2) => {
15233
15776
  return { kind: "allow", output: { hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext } } };
15234
15777
  };
15235
15778
  var proseCommandGuard = (input) => {
15236
- if (!SHELLS3.has(toolName2(input.payload))) return ALLOW4;
15237
- const found = proseInCommand(optionalString(toolInput2(input.payload), "command") ?? "");
15238
- if (found.length === 0) return ALLOW4;
15779
+ if (!SHELLS3.has(toolName(input.payload))) return ALLOW5;
15780
+ const found = proseInCommand(optionalString(toolInput(input.payload), "command") ?? "");
15781
+ if (found.length === 0) return ALLOW5;
15239
15782
  const blocking2 = projectPreset(input.runtime, projectRoot(input)) === "gabriel";
15240
15783
  for (const item2 of found) {
15241
15784
  const decision = decisionFor(input, item2, blocking2);
15242
15785
  if (decision !== void 0) return decision;
15243
15786
  }
15244
- return ALLOW4;
15787
+ return ALLOW5;
15245
15788
  };
15246
15789
 
15247
15790
  // src/prose/hooks.ts
@@ -15265,7 +15808,7 @@ var liveFindings = (input, texts2) => {
15265
15808
  const accepted2 = acceptedFingerprints(baseline.ok ? baseline.value : EMPTY_BASELINE, input.runtime.now());
15266
15809
  return texts2.flatMap((text9) => scanText(text9, { rules: CREDENTIAL_RULES })).filter((finding4) => !accepted2.has(finding4.fingerprint));
15267
15810
  };
15268
- var record2 = (input, findings, draft, where) => {
15811
+ var record3 = (input, findings, draft, where) => {
15269
15812
  const ids2 = findings.map((finding4) => {
15270
15813
  const saved = recordIncident(
15271
15814
  input.runtime,
@@ -15281,7 +15824,7 @@ var secretsUserPromptSubmit = (input) => {
15281
15824
  const prompt = optionalString(input.payload.raw, "prompt") ?? "";
15282
15825
  const findings = liveFindings(input, [prompt]);
15283
15826
  if (findings.length === 0) return { kind: "allow" };
15284
- const incident = record2(input, findings, { source: "prompt", state: "exposicao-local" }, "prompt");
15827
+ const incident = record3(input, findings, { source: "prompt", state: "exposicao-local" }, "prompt");
15285
15828
  const items = findings.map((finding4) => ({ ruleId: finding4.ruleId, where: lineWhere(input.t, finding4) }));
15286
15829
  return {
15287
15830
  kind: "block",
@@ -15291,8 +15834,8 @@ var secretsUserPromptSubmit = (input) => {
15291
15834
  var secretsPostToolUse = (input) => {
15292
15835
  const findings = liveFindings(input, stringLeaves(input.payload.raw["tool_response"]));
15293
15836
  if (findings.length === 0) return { kind: "allow" };
15294
- const tool = toolName2(input.payload) || "tool";
15295
- const incident = record2(input, findings, { source: "tool-output", state: "aberto" }, `tool-output:${tool}`);
15837
+ const tool = toolName(input.payload) || "tool";
15838
+ const incident = record3(input, findings, { source: "tool-output", state: "aberto" }, `tool-output:${tool}`);
15296
15839
  const items = findings.map((finding4) => ({ ruleId: finding4.ruleId, where: lineWhere(input.t, finding4) }));
15297
15840
  const context = input.t("secrets.post.context", { tool, found: describeFindings(input.t, items), incident });
15298
15841
  return {
@@ -15396,21 +15939,21 @@ var commitIntent = (segments) => {
15396
15939
  };
15397
15940
 
15398
15941
  // src/secrets/guards/pre-tool.ts
15399
- var ALLOW5 = { kind: "allow" };
15942
+ var ALLOW6 = { kind: "allow" };
15400
15943
  var relativeFile = (cwd, file) => {
15401
15944
  if (file === "") return file;
15402
15945
  const rel = relative9(cwd, file).split("\\").join("/");
15403
15946
  return rel.startsWith("..") || rel === "" ? file : rel;
15404
15947
  };
15405
15948
  var warningDecision = (t, warnings) => {
15406
- if (warnings.length === 0) return ALLOW5;
15949
+ if (warnings.length === 0) return ALLOW6;
15407
15950
  const context = t("secrets.warn.write", { found: describeFindings(t, warnings) });
15408
15951
  return { kind: "allow", output: { hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext: context } } };
15409
15952
  };
15410
15953
  var writeDecision = (input, tool) => {
15411
15954
  const { t } = input;
15412
15955
  const warnings = [];
15413
- for (const written of writtenTexts(tool, toolInput2(input.payload))) {
15956
+ for (const written of writtenTexts(tool, toolInput(input.payload))) {
15414
15957
  const file = relativeFile(input.cwd, written.file);
15415
15958
  const all = scanText(written.text, { path: file });
15416
15959
  warnings.push(
@@ -15446,7 +15989,7 @@ var bashMessage = (t, finding4) => {
15446
15989
  var commitDecision = (input, includeUnstaged) => {
15447
15990
  const staged = scanStaged(input.runtime, input.cwd, includeUnstaged);
15448
15991
  const found = staged.ok ? blocking(staged.value) : [];
15449
- if (found.length === 0) return ALLOW5;
15992
+ if (found.length === 0) return ALLOW6;
15450
15993
  const items = found.map((finding4) => ({
15451
15994
  ruleId: finding4.ruleId,
15452
15995
  where: input.t("secrets.where.file", { file: finding4.file, line: finding4.line, column: finding4.column })
@@ -15458,8 +16001,8 @@ var commitDecision = (input, includeUnstaged) => {
15458
16001
  };
15459
16002
  };
15460
16003
  var bashDecision = (input) => {
15461
- const command = optionalString(toolInput2(input.payload), "command") ?? "";
15462
- if (command === "") return ALLOW5;
16004
+ const command = optionalString(toolInput(input.payload), "command") ?? "";
16005
+ if (command === "") return ALLOW6;
15463
16006
  const segments = segmentsOf(command);
15464
16007
  const [first] = bashFindings(command, segments);
15465
16008
  if (first !== void 0) return { kind: "block", reason: bashMessage(input.t, first) };
@@ -15473,12 +16016,12 @@ var bashDecision = (input) => {
15473
16016
  };
15474
16017
  }
15475
16018
  const commit2 = commitIntent(segments);
15476
- return commit2 === void 0 ? ALLOW5 : commitDecision(input, commit2.includeUnstaged);
16019
+ return commit2 === void 0 ? ALLOW6 : commitDecision(input, commit2.includeUnstaged);
15477
16020
  };
15478
16021
  var readDecision = (input) => {
15479
- const inputs = toolInput2(input.payload);
16022
+ const inputs = toolInput(input.payload);
15480
16023
  const target2 = optionalString(inputs, "file_path") ?? optionalString(inputs, "path") ?? "";
15481
- if (target2 === "" || !isSensitivePath(target2)) return ALLOW5;
16024
+ if (target2 === "" || !isSensitivePath(target2)) return ALLOW6;
15482
16025
  return {
15483
16026
  kind: "block",
15484
16027
  reason: modelReason(input.t, "secrets.block.read", { target: relativeFile(input.cwd, target2) }),
@@ -15489,11 +16032,11 @@ var WRITERS2 = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookE
15489
16032
  var SHELLS4 = /* @__PURE__ */ new Set(["Bash", "PowerShell"]);
15490
16033
  var READS = /* @__PURE__ */ new Set(["Read", "Grep"]);
15491
16034
  var secretsPreToolUse = (input) => {
15492
- const tool = toolName2(input.payload);
16035
+ const tool = toolName(input.payload);
15493
16036
  if (WRITERS2.has(tool)) return writeDecision(input, tool);
15494
16037
  if (SHELLS4.has(tool)) return bashDecision(input);
15495
16038
  if (READS.has(tool)) return readDecision(input);
15496
- return ALLOW5;
16039
+ return ALLOW6;
15497
16040
  };
15498
16041
 
15499
16042
  // src/secrets/remotes.ts
@@ -15570,7 +16113,7 @@ var recordBlock = (input, decision) => {
15570
16113
  if (paths === void 0) return;
15571
16114
  logEvent(input.runtime, paths, "guard_block", {
15572
16115
  hook: input.event,
15573
- tool: toolName2(input.payload),
16116
+ tool: toolName(input.payload),
15574
16117
  sessionId: input.payload.sessionId ?? null,
15575
16118
  claim: (decision.reason.split("\n")[0] ?? "").slice(0, 200)
15576
16119
  });
@@ -15617,7 +16160,8 @@ var GUARD_SETS = [
15617
16160
  { id: "secrets", damage: true, handlers: SECRETS_HANDLERS },
15618
16161
  { id: "gate", damage: true, handlers: PORTAO_HANDLERS },
15619
16162
  { id: "prose", damage: false, handlers: PROSE_HANDLERS },
15620
- { id: "git", damage: true, handlers: GIT_HANDLERS }
16163
+ { id: "git", damage: true, handlers: GIT_HANDLERS },
16164
+ { id: "deps", damage: false, handlers: { PreToolUse: [depsGuard] } }
15621
16165
  ];
15622
16166
  var DEFAULT_HANDLERS = mergeHandlers(...GUARD_SETS.map(withPolicy));
15623
16167
 
@@ -15993,9 +16537,9 @@ var hookSchemaProblems = (hooks, claude) => {
15993
16537
  import { join as join35 } from "node:path";
15994
16538
  var entriesOf = (source, hooks, own) => Object.entries(asRecord(hooks)).flatMap(
15995
16539
  ([event, groups]) => asList(groups).flatMap((group) => {
15996
- const record3 = asRecord(group);
15997
- const matcher = optionalString(record3, "matcher");
15998
- return asList(record3["hooks"]).filter((handler) => isRecord(handler) && own(handler)).map((handler) => ({ source, event, matcher, handler }));
16540
+ const record4 = asRecord(group);
16541
+ const matcher = optionalString(record4, "matcher");
16542
+ return asList(record4["hooks"]).filter((handler) => isRecord(handler) && own(handler)).map((handler) => ({ source, event, matcher, handler }));
15999
16543
  })
16000
16544
  );
16001
16545
  var PROJECT_SCOPES = { project: "project", local: "local" };
@@ -16006,7 +16550,7 @@ var projectHookFiles = (env) => readSettings(env).flatMap(({ scope: scope2, path
16006
16550
  var pluginsDir = ({ env, runtime }) => join35(env["CLAUDE_CONFIG_DIR"] ?? join35(runtime.homeDir(), ".claude"), "plugins");
16007
16551
  var installRecord = (value, root) => {
16008
16552
  const records = Array.isArray(value) ? value.filter(isRecord) : isRecord(value) ? [value] : [];
16009
- return records.find((record3) => record3["projectPath"] === root) ?? records[0];
16553
+ return records.find((record4) => record4["projectPath"] === root) ?? records[0];
16010
16554
  };
16011
16555
  var readJson = (env, path) => {
16012
16556
  const text9 = env.runtime.readText(path);
@@ -16015,9 +16559,9 @@ var readJson = (env, path) => {
16015
16559
  };
16016
16560
  var installedPlugin = (env) => {
16017
16561
  const index = readJson(env, toAbsolutePath(join35(pluginsDir(env), "installed_plugins.json")));
16018
- const record3 = installRecord(asRecord(index?.["plugins"])[PLUGIN_ID], env.root);
16019
- const installPath = record3 === void 0 ? void 0 : optionalString(record3, "installPath");
16020
- if (record3 === void 0 || installPath === void 0) return void 0;
16562
+ const record4 = installRecord(asRecord(index?.["plugins"])[PLUGIN_ID], env.root);
16563
+ const installPath = record4 === void 0 ? void 0 : optionalString(record4, "installPath");
16564
+ if (record4 === void 0 || installPath === void 0) return void 0;
16021
16565
  const root = toAbsolutePath(installPath);
16022
16566
  const manifest = readJson(env, toAbsolutePath(join35(root, ".claude-plugin", "plugin.json")));
16023
16567
  const hooksPath = toAbsolutePath(join35(root, "hooks", "hooks.json"));
@@ -16025,7 +16569,7 @@ var installedPlugin = (env) => {
16025
16569
  const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
16026
16570
  return {
16027
16571
  root,
16028
- version: (manifest === void 0 ? void 0 : optionalString(manifest, "version")) ?? optionalString(record3, "version"),
16572
+ version: (manifest === void 0 ? void 0 : optionalString(manifest, "version")) ?? optionalString(record4, "version"),
16029
16573
  hooksFile: parsed?.ok === true ? { source: "plugin", path: hooksPath, hooks: parsed.value["hooks"] } : void 0,
16030
16574
  hooksText: text9.ok ? text9.value : void 0
16031
16575
  };
@@ -16517,8 +17061,8 @@ var doctorCommand = {
16517
17061
  // src/gain/gain.ts
16518
17062
  import { join as join41 } from "node:path";
16519
17063
  var GAIN_SCHEMA = "finitemonkey/gain@1";
16520
- var DAY_MS2 = 864e5;
16521
- var SAVE_WINDOW_MS = DAY_MS2;
17064
+ var DAY_MS3 = 864e5;
17065
+ var SAVE_WINDOW_MS = DAY_MS3;
16522
17066
  var readLog = (runtime, paths) => {
16523
17067
  const text9 = runtime.readText(paths.log);
16524
17068
  if (!text9.ok) return [];
@@ -16554,7 +17098,7 @@ var countsIn = (log, gates, from, to) => {
16554
17098
  };
16555
17099
  var gainReport = (runtime, paths, days) => {
16556
17100
  const to = Date.parse(runtime.now());
16557
- const from = to - days * DAY_MS2;
17101
+ const from = to - days * DAY_MS3;
16558
17102
  const log = readLog(runtime, paths);
16559
17103
  const gates = readGates(runtime, paths);
16560
17104
  return {
@@ -16563,7 +17107,7 @@ var gainReport = (runtime, paths, days) => {
16563
17107
  from: new Date(from).toISOString(),
16564
17108
  to: new Date(to).toISOString(),
16565
17109
  current: countsIn(log, gates, from, to + 1),
16566
- previous: countsIn(log, gates, from - days * DAY_MS2, from)
17110
+ previous: countsIn(log, gates, from - days * DAY_MS3, from)
16567
17111
  };
16568
17112
  };
16569
17113
 
@@ -16762,8 +17306,8 @@ var secretsInRange = (runtime, root, range) => {
16762
17306
  var RECORD = "";
16763
17307
  var commitMessagesInRange = (runtime, root, range) => {
16764
17308
  const log = runtime.git(["log", "--no-merges", `--format=%H%x00%B${RECORD}`, ...range, "--"], root);
16765
- const failures = log.ok ? log.stdout.split(RECORD).map((record3) => record3.replace(/^\n/, "")).filter((record3) => record3.includes("\0")).flatMap((record3) => {
16766
- const [sha = "", body2 = ""] = record3.split("\0");
17309
+ const failures = log.ok ? log.stdout.split(RECORD).map((record4) => record4.replace(/^\n/, "")).filter((record4) => record4.includes("\0")).flatMap((record4) => {
17310
+ const [sha = "", body2 = ""] = record4.split("\0");
16767
17311
  return commitMessageProblems(body2).map((problem4) => `${short(sha)} ${problem4}`);
16768
17312
  }) : [`git log ${range.join(" ")} failed`];
16769
17313
  return { id: "commit-messages", failures, fix: "git rebase -i <base> and reword the listed commits" };
@@ -17061,7 +17605,7 @@ var fsckDangling = (runtime, repo) => {
17061
17605
  });
17062
17606
  return { commits: pick4("commit"), blobs: pick4("blob").length };
17063
17607
  };
17064
- var describe = (runtime, repo, shas) => {
17608
+ var describe2 = (runtime, repo, shas) => {
17065
17609
  if (shas.length === 0) return /* @__PURE__ */ new Map();
17066
17610
  const result = runtime.git(["log", "--no-walk=unsorted", "--format=%H%x09%cr%x09%s", ...shas], repo.root);
17067
17611
  return new Map(
@@ -17080,7 +17624,7 @@ var rescueReport = (runtime, repo) => {
17080
17624
  const seen = new Set(fromReflog.map((line2) => line2.sha));
17081
17625
  const dangling = fsckDangling(runtime, repo);
17082
17626
  const extra = dangling.commits.filter((sha) => !seen.has(sha));
17083
- const details = describe(runtime, repo, [...fromReflog.map((line2) => line2.sha), ...extra].slice(0, LIST_LIMIT));
17627
+ const details = describe2(runtime, repo, [...fromReflog.map((line2) => line2.sha), ...extra].slice(0, LIST_LIMIT));
17084
17628
  const commit2 = (sha, source, ref) => {
17085
17629
  const detail = details.get(sha);
17086
17630
  return detail === void 0 ? [] : [{ sha, when: detail[0], subject: detail[1], source, ref }];
@@ -17328,7 +17872,7 @@ var GLOBAL_FLAGS = [
17328
17872
  ];
17329
17873
  var exitCodeHelp = (ctx) => EXIT_CODE_NAMES.map((name) => ({ code: ExitCode[name], name, summary: ctx.t(exitCodeDocs[ExitCode[name]]) }));
17330
17874
  var pad = (text9, width) => text9.padEnd(Math.max(width, text9.length + 1));
17331
- var describe2 = (ctx, spec2) => ({
17875
+ var describe3 = (ctx, spec2) => ({
17332
17876
  name: commandName(spec2),
17333
17877
  usage: spec2.usage,
17334
17878
  summary: ctx.t(spec2.summaryKey),
@@ -17337,7 +17881,7 @@ var describe2 = (ctx, spec2) => ({
17337
17881
  });
17338
17882
  var summaryLine = (ctx, help) => help.status === "planned" ? `${help.summary} ${ctx.t("help.planned")}` : help.summary;
17339
17883
  var overview = (ctx, registry) => {
17340
- const commands = registry.map((spec2) => describe2(ctx, spec2));
17884
+ const commands = registry.map((spec2) => describe3(ctx, spec2));
17341
17885
  const flags = GLOBAL_FLAGS.map(({ flag: flag2, key }) => ({ flag: flag2, summary: ctx.t(key) }));
17342
17886
  const width = Math.max(...commands.map(({ name }) => name.length)) + 2;
17343
17887
  const flagWidth = Math.max(...flags.map(({ flag: flag2 }) => flag2.length)) + 2;
@@ -17362,7 +17906,7 @@ var overview = (ctx, registry) => {
17362
17906
  return success({ commands, flags, exitCodes }, human2);
17363
17907
  };
17364
17908
  var helpFor = (ctx, spec2) => {
17365
- const help = describe2(ctx, spec2);
17909
+ const help = describe3(ctx, spec2);
17366
17910
  const human2 = [
17367
17911
  `${ctx.t("help.usage")}: ${help.usage}`,
17368
17912
  "",
@@ -18751,14 +19295,14 @@ var gitUnavailable = (detail) => ({
18751
19295
  });
18752
19296
 
18753
19297
  // src/commands/secrets-accept.ts
18754
- var DAY_MS3 = 864e5;
19298
+ var DAY_MS4 = 864e5;
18755
19299
  var daysOf = (values) => {
18756
19300
  const raw = stringOption(values, "days");
18757
19301
  if (raw === void 0) return EXCEPTION_MAX_DAYS;
18758
19302
  const days = Number.parseInt(raw, 10);
18759
19303
  return Number.isInteger(days) && days > 0 && days <= EXCEPTION_MAX_DAYS ? days : void 0;
18760
19304
  };
18761
- var plusDays = (iso, days) => new Date(Date.parse(iso) + days * DAY_MS3).toISOString();
19305
+ var plusDays = (iso, days) => new Date(Date.parse(iso) + days * DAY_MS4).toISOString();
18762
19306
  var failure3 = (ctx, problem4) => failureFrom(ctx.t, problem4);
18763
19307
  var inputProblem = (ctx, id, reason, days) => {
18764
19308
  const fix = "finitemonkey secrets accept <id> --reason <why> [--days 1-90] [--false-positive]";
@@ -19276,8 +19820,8 @@ var usedTokens = (usage) => {
19276
19820
  if (!isRecord(usage)) return void 0;
19277
19821
  return (num(usage["input_tokens"]) ?? 0) + (num(usage["cache_creation_input_tokens"]) ?? 0) + (num(usage["cache_read_input_tokens"]) ?? 0);
19278
19822
  };
19279
- var contextPercent = (record3) => {
19280
- const window = record3["context_window"];
19823
+ var contextPercent = (record4) => {
19824
+ const window = record4["context_window"];
19281
19825
  if (!isRecord(window)) return void 0;
19282
19826
  const direct = window["used_percentage"];
19283
19827
  if (typeof direct === "number" && Number.isFinite(direct)) return Math.round(direct);
@@ -19287,12 +19831,12 @@ var contextPercent = (record3) => {
19287
19831
  };
19288
19832
  var parseStatusInput = (raw) => {
19289
19833
  const parsed = parseJsonObject(raw.trim() === "" ? "{}" : raw);
19290
- const record3 = parsed.ok ? parsed.value : {};
19291
- const workspace = isRecord(record3["workspace"]) ? record3["workspace"] : {};
19834
+ const record4 = parsed.ok ? parsed.value : {};
19835
+ const workspace = isRecord(record4["workspace"]) ? record4["workspace"] : {};
19292
19836
  return {
19293
- sessionId: text8(record3["session_id"]),
19294
- dir: text8(workspace["project_dir"]) ?? text8(workspace["current_dir"]) ?? text8(record3["cwd"]),
19295
- contextPercent: contextPercent(record3),
19837
+ sessionId: text8(record4["session_id"]),
19838
+ dir: text8(workspace["project_dir"]) ?? text8(workspace["current_dir"]) ?? text8(record4["cwd"]),
19839
+ contextPercent: contextPercent(record4),
19296
19840
  raw
19297
19841
  };
19298
19842
  };
@@ -19987,7 +20531,7 @@ var langSettings = (runtime, cwd, env, platform) => {
19987
20531
  };
19988
20532
 
19989
20533
  // src/core/suggest.ts
19990
- var distance = (a, b) => {
20534
+ var distance2 = (a, b) => {
19991
20535
  const row = Array.from({ length: b.length + 1 }, (_, index) => index);
19992
20536
  for (let i = 1; i <= a.length; i += 1) {
19993
20537
  let diagonal = row[0] ?? 0;
@@ -20007,7 +20551,7 @@ var closest = (input, candidates) => {
20007
20551
  const limit = Math.max(1, Math.floor(input.length / 3));
20008
20552
  let best;
20009
20553
  for (const name of candidates) {
20010
- const score = distance(input, name.split(" ")[0] ?? name);
20554
+ const score = distance2(input, name.split(" ")[0] ?? name);
20011
20555
  if (score <= limit && (best === void 0 || score < best.score)) best = { name, score };
20012
20556
  }
20013
20557
  return best?.name;