finitemonkey 0.1.0-next.26 → 0.1.0-next.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.es.md CHANGED
@@ -137,7 +137,7 @@ Generado desde `src/core/footprint.ts`, la lista que los tests exigen al código
137
137
  | Lee | `.git/` | El repositorio a través del comando `git`: estado, diffs, refs y el reflog. |
138
138
  | Lee | `git diff` | Archivos modificados, revisados en busca de secretos, tests saltados, placeholders y patrones de prosa. |
139
139
  | Lee | `.finitemonkey/config.yml`, `.finitemonkey/policy.yml`, `.finitemonkey/briefing.md`, `.finitemonkey/look.yml` | La configuración del proyecto, la política de los guards y el briefing opcional para subagentes. |
140
- | Escribe | `.claude/settings.json`, `.claude/skills/verify/SKILL.md`, `AGENTS.md`, `CLAUDE.md`, `.gitignore`, `.finitemonkey/config.yml`, `.finitemonkey/lock.json`, `.finitemonkey/locks/`, `.finitemonkey/runtime/`, `.finitemonkey/backups/` | `init` y `update`: la configuración de los hooks, la skill verify, un bloque gestionado corto en `AGENTS.md` que `CLAUDE.md` importa, una línea en `.gitignore`, el runtime fijado y una copia de cada archivo que cambian. |
140
+ | Escribe | `.claude/settings.json`, `.claude/settings.local.json`, `.claude/skills/verify/SKILL.md`, `AGENTS.md`, `CLAUDE.md`, `.gitignore`, `.finitemonkey/config.yml`, `.finitemonkey/lock.json`, `.finitemonkey/locks/`, `.finitemonkey/runtime/`, `.finitemonkey/backups/` | `init` y `update`: la configuración de los hooks (y, con `--sandbox`, el sandbox en tu configuración local), la skill verify, un bloque gestionado corto en `AGENTS.md` que `CLAUDE.md` importa, una línea en `.gitignore`, el runtime fijado y una copia de cada archivo que cambian. |
141
141
  | Escribe | `.finitemonkey/tasks/`, `.finitemonkey/state.json` | Contratos de tarea y cuál tarea está activa. |
142
142
  | Escribe | `.finitemonkey/evidence/`, `.finitemonkey/log.jsonl`, `.finitemonkey/prod-evidence.jsonl` | La evidencia de cada comprobación, ligada al hash del árbol, y el log de la puerta. |
143
143
  | Escribe | `.finitemonkey/trust.json`, `.finitemonkey/allow/`, `.finitemonkey/cache/` | Aprobaciones y permisos escritos por la persona, y una caché breve de los pull requests abiertos. |
package/README.md CHANGED
@@ -137,7 +137,7 @@ Generated from `src/core/footprint.ts`, the list the tests hold the code to. No
137
137
  | Reads | `.git/` | The repository through the `git` command: status, diffs, refs and the reflog. |
138
138
  | Reads | `git diff` | Changed files, scanned for secrets, test skips, placeholders and prose patterns. |
139
139
  | Reads | `.finitemonkey/config.yml`, `.finitemonkey/policy.yml`, `.finitemonkey/briefing.md`, `.finitemonkey/look.yml` | Project configuration, the guard policy and the optional briefing for subagents. |
140
- | Writes | `.claude/settings.json`, `.claude/skills/verify/SKILL.md`, `AGENTS.md`, `CLAUDE.md`, `.gitignore`, `.finitemonkey/config.yml`, `.finitemonkey/lock.json`, `.finitemonkey/locks/`, `.finitemonkey/runtime/`, `.finitemonkey/backups/` | `init` and `update`: hook settings, the verify skill, a short managed block in `AGENTS.md` that `CLAUDE.md` imports, one `.gitignore` line, the pinned runtime and a backup of every file it changes. |
140
+ | Writes | `.claude/settings.json`, `.claude/settings.local.json`, `.claude/skills/verify/SKILL.md`, `AGENTS.md`, `CLAUDE.md`, `.gitignore`, `.finitemonkey/config.yml`, `.finitemonkey/lock.json`, `.finitemonkey/locks/`, `.finitemonkey/runtime/`, `.finitemonkey/backups/` | `init` and `update`: hook settings (and, with `--sandbox`, the sandbox in your local settings), the verify skill, a short managed block in `AGENTS.md` that `CLAUDE.md` imports, one `.gitignore` line, the pinned runtime and a backup of every file it changes. |
141
141
  | Writes | `.finitemonkey/tasks/`, `.finitemonkey/state.json` | Task contracts and which task is active. |
142
142
  | Writes | `.finitemonkey/evidence/`, `.finitemonkey/log.jsonl`, `.finitemonkey/prod-evidence.jsonl` | Evidence of each check, tied to the tree hash, and the gate log. |
143
143
  | Writes | `.finitemonkey/trust.json`, `.finitemonkey/allow/`, `.finitemonkey/cache/` | Approvals and grants typed by the person, and a short-lived cache of open pull requests. |
package/README.pt-BR.md CHANGED
@@ -137,7 +137,7 @@ Gerado a partir de `src/core/footprint.ts`, a lista que os testes cobram do cód
137
137
  | Lê | `.git/` | O repositório pelo comando `git`: status, diffs, refs e o reflog. |
138
138
  | Lê | `git diff` | Arquivos alterados, varridos atrás de segredos, testes pulados, placeholders e padrões de prosa. |
139
139
  | Lê | `.finitemonkey/config.yml`, `.finitemonkey/policy.yml`, `.finitemonkey/briefing.md`, `.finitemonkey/look.yml` | A configuração do projeto, a política dos guards e o briefing opcional para subagentes. |
140
- | Grava | `.claude/settings.json`, `.claude/skills/verify/SKILL.md`, `AGENTS.md`, `CLAUDE.md`, `.gitignore`, `.finitemonkey/config.yml`, `.finitemonkey/lock.json`, `.finitemonkey/locks/`, `.finitemonkey/runtime/`, `.finitemonkey/backups/` | `init` e `update`: configuração dos hooks, a skill verify, um bloco gerenciado curto no `AGENTS.md` que o `CLAUDE.md` importa, uma linha no `.gitignore`, o runtime fixado e um backup de cada arquivo que alteram. |
140
+ | Grava | `.claude/settings.json`, `.claude/settings.local.json`, `.claude/skills/verify/SKILL.md`, `AGENTS.md`, `CLAUDE.md`, `.gitignore`, `.finitemonkey/config.yml`, `.finitemonkey/lock.json`, `.finitemonkey/locks/`, `.finitemonkey/runtime/`, `.finitemonkey/backups/` | `init` e `update`: configuração dos hooks (e, com `--sandbox`, o sandbox nas suas configurações locais), a skill verify, um bloco gerenciado curto no `AGENTS.md` que o `CLAUDE.md` importa, uma linha no `.gitignore`, o runtime fixado e um backup de cada arquivo que alteram. |
141
141
  | Grava | `.finitemonkey/tasks/`, `.finitemonkey/state.json` | Contratos de tarefa e qual tarefa está ativa. |
142
142
  | Grava | `.finitemonkey/evidence/`, `.finitemonkey/log.jsonl`, `.finitemonkey/prod-evidence.jsonl` | A evidência de cada checagem, ligada ao hash da árvore, e o log do portão. |
143
143
  | Grava | `.finitemonkey/trust.json`, `.finitemonkey/allow/`, `.finitemonkey/cache/` | Aprovações e liberações digitadas pela pessoa, e um cache curto dos pull requests abertos. |
@@ -1371,7 +1371,7 @@ var parseStatusLineAdded = (value) => {
1371
1371
  };
1372
1372
 
1373
1373
  // src/install/lock-file.ts
1374
- var ENTRY_KINDS = ["settings", "gitignore", "generated", "markdown", "githook"];
1374
+ var ENTRY_KINDS = ["settings", "gitignore", "generated", "markdown", "githook", "sandbox"];
1375
1375
  var strings = (value) => Array.isArray(value) && value.every((item2) => typeof item2 === "string") ? value : void 0;
1376
1376
  var parseAdded = (value) => {
1377
1377
  if (!isRecord(value)) return void 0;
@@ -1405,6 +1405,7 @@ var parseEntry = (value) => {
1405
1405
  }
1406
1406
  const added = parseAdded(value["added"]);
1407
1407
  const handlers = Array.isArray(value["handlers"]) ? value["handlers"].filter(isRecord) : void 0;
1408
+ const sandbox = isRecord(value["sandbox"]) ? value["sandbox"] : void 0;
1408
1409
  return {
1409
1410
  path,
1410
1411
  kind: known2,
@@ -1412,7 +1413,8 @@ var parseEntry = (value) => {
1412
1413
  hash,
1413
1414
  ...typeof backup === "string" ? { backup } : {},
1414
1415
  ...added === void 0 ? {} : { added },
1415
- ...handlers === void 0 ? {} : { handlers }
1416
+ ...handlers === void 0 ? {} : { handlers },
1417
+ ...sandbox === void 0 ? {} : { sandbox }
1416
1418
  };
1417
1419
  };
1418
1420
  var parseEntries = (value) => {
@@ -2087,6 +2089,37 @@ var policyTemplate = (lang) => [
2087
2089
  ""
2088
2090
  ].join("\n");
2089
2091
 
2092
+ // src/install/sandbox.ts
2093
+ var SANDBOX_FILE = ".claude/settings.local.json";
2094
+ var DEFAULT_HOSTS = [
2095
+ "registry.npmjs.org",
2096
+ "github.com",
2097
+ "api.github.com",
2098
+ "codeload.github.com",
2099
+ "objects.githubusercontent.com"
2100
+ ];
2101
+ var SANDBOX_PLATFORMS = ["darwin", "linux"];
2102
+ var sandboxBlock = (root, hosts) => ({
2103
+ enabled: true,
2104
+ failIfUnavailable: true,
2105
+ filesystem: { denyRead: ["~/.ssh", "~/.aws", `${root}/.env`, `${root}/.env.*`] },
2106
+ network: { allowedDomains: [...hosts] }
2107
+ });
2108
+ var withSandbox = (text9, block4) => {
2109
+ const parsed = parseSettings(text9);
2110
+ if (!parsed.ok) return void 0;
2111
+ if ("sandbox" in parsed.value) return text9;
2112
+ return renderJson({ ...parsed.value, sandbox: block4 }, text9 === void 0 ? DEFAULT_STYLE : styleOf(text9));
2113
+ };
2114
+ var withoutSandbox = (text9, added) => {
2115
+ const parsed = parseSettings(text9);
2116
+ if (!parsed.ok) return text9;
2117
+ const current2 = parsed.value["sandbox"];
2118
+ if (!isRecord(current2) || JSON.stringify(current2) !== JSON.stringify(added)) return text9;
2119
+ const rest = Object.fromEntries(Object.entries(parsed.value).filter(([key]) => key !== "sandbox"));
2120
+ return Object.keys(rest).length === 0 ? void 0 : renderJson(rest, styleOf(text9));
2121
+ };
2122
+
2090
2123
  // src/install/settings-merge.ts
2091
2124
  var SETTINGS_SCHEMA_URL = "https://json.schemastore.org/claude-code-settings.json";
2092
2125
  var ATTRIBUTION_OFF = { commit: "", pr: "", sessionUrl: false };
@@ -2306,6 +2339,17 @@ var markdownItems = (project, runner, lang) => [
2306
2339
  const after = render3(before);
2307
2340
  return { path, kind: "markdown", action: actionFor(before, after, "merge"), before, after };
2308
2341
  });
2342
+ var sandboxItems = (project, input) => {
2343
+ if (!input.sandbox || !SANDBOX_PLATFORMS.includes(input.platform)) return [];
2344
+ const declared = configList(parseConfigYaml(project.read(CONFIG_FILE2) ?? ""), "sandbox", "hosts");
2345
+ const block4 = sandboxBlock(project.root, declared.length > 0 ? declared : DEFAULT_HOSTS);
2346
+ const before = project.read(SANDBOX_FILE);
2347
+ const after = withSandbox(before, block4);
2348
+ if (after === void 0) return [];
2349
+ return [
2350
+ { path: SANDBOX_FILE, kind: "sandbox", action: actionFor(before, after, "merge"), before, after, sandbox: block4 }
2351
+ ];
2352
+ };
2309
2353
  var gitignoreItem = (project) => {
2310
2354
  const before = project.read(GITIGNORE_FILE);
2311
2355
  const after = withBlock(before);
@@ -2350,14 +2394,18 @@ var buildPlan = (project, detection, input, previous, migrations = MIGRATIONS) =
2350
2394
  const files = generatedFiles(project, input, detection.runner, vendor);
2351
2395
  if (!files.ok) return files;
2352
2396
  const generated = files.value.map((file) => generatedItem(project, file, entryFor(previous, file.path)));
2397
+ const sandboxSupported = SANDBOX_PLATFORMS.includes(input.platform);
2398
+ const sandbox = input.sandbox ? sandboxSupported ? "added" : "unsupported" : input.offerSandbox ? "offer" : "none";
2353
2399
  return accept({
2354
2400
  detection,
2401
+ sandbox,
2355
2402
  preset: input.preset,
2356
2403
  items: applyMigrations(
2357
2404
  [
2358
2405
  settings.value,
2359
2406
  gitignoreItem(project),
2360
2407
  ...markdownItems(project, detection.runner, input.lang),
2408
+ ...sandboxItems(project, input),
2361
2409
  ...generated,
2362
2410
  ...gitHookItems(project, detection.runner)
2363
2411
  ],
@@ -2406,7 +2454,8 @@ var entryOf2 = (plan, item2, backupDir) => {
2406
2454
  hash,
2407
2455
  ...backup === void 0 ? {} : { backup },
2408
2456
  ...item2.added === void 0 ? {} : { added: item2.added },
2409
- ...item2.handlers === void 0 ? {} : { handlers: item2.handlers }
2457
+ ...item2.handlers === void 0 ? {} : { handlers: item2.handlers },
2458
+ ...item2.sandbox === void 0 ? {} : { sandbox: item2.sandbox }
2410
2459
  };
2411
2460
  };
2412
2461
  var nextLock = (plan, version, now, backupDir, createdDirs, only) => ({
@@ -2602,13 +2651,16 @@ var selfBundle = (ctx) => {
2602
2651
  return text9.ok ? text9.value : void 0;
2603
2652
  };
2604
2653
  var INHERITED = /* @__PURE__ */ new Set(["default", "config", "claude-settings"]);
2605
- var planInputFor = (ctx, preset, bin, marketplace) => ({
2654
+ var planInputFor = (ctx, preset, bin, marketplace, sandbox = false) => ({
2606
2655
  preset,
2607
2656
  lang: ctx.lang,
2608
2657
  claudeLanguage: INHERITED.has(ctx.langSource) ? void 0 : CLAUDE_LANGUAGE[ctx.lang],
2609
2658
  bin,
2610
2659
  version: ctx.version,
2611
2660
  marketplace,
2661
+ sandbox,
2662
+ offerSandbox: !sandbox && SANDBOX_PLATFORMS.includes(ctx.platform) && (ctx.io.stdin.isTTY === true || ctx.yes),
2663
+ platform: ctx.platform,
2612
2664
  bundle: selfBundle(ctx),
2613
2665
  env: ctx.env,
2614
2666
  t: ctx.t
@@ -7020,7 +7072,9 @@ var en_default = {
7020
7072
  "critique.decision": "Decision needed: finding {id} ({what}) already fails on the base branch and is an active risk.",
7021
7073
  "critique.verdict": "Verdict: {verdict}",
7022
7074
  "critique.error.invalid": "the findings file is not valid: {detail}",
7023
- "critique.error.must_fix": "{count, plural, one {# must-fix finding reproduced on head only} other {# must-fix findings reproduced on head only}}"
7075
+ "critique.error.must_fix": "{count, plural, one {# must-fix finding reproduced on head only} other {# must-fix findings reproduced on head only}}",
7076
+ "install.warn.sandbox_offer": "The Claude Code sandbox runs here. Add --sandbox to turn it on for you only, in .claude/settings.local.json: it fails to start without the sandbox, hides ~/.ssh, ~/.aws and .env files from commands, and lets them reach only the hosts in sandbox.hosts.",
7077
+ "install.warn.sandbox_unsupported": "--sandbox was ignored: native Windows has no Claude Code sandbox, so the guards are the only protection here. Run Claude Code inside WSL2 to use it."
7024
7078
  };
7025
7079
 
7026
7080
  // src/i18n/es.json
@@ -7709,7 +7763,9 @@ var es_default = {
7709
7763
  "critique.decision": "Decisión necesaria: el hallazgo {id} ({what}) ya falla en la rama base y es un riesgo activo.",
7710
7764
  "critique.verdict": "Veredicto: {verdict}",
7711
7765
  "critique.error.invalid": "el archivo de hallazgos no es válido: {detail}",
7712
- "critique.error.must_fix": "{count, plural, one {# hallazgo obligatorio reproducido solo en head} other {# hallazgos obligatorios reproducidos solo en head}}"
7766
+ "critique.error.must_fix": "{count, plural, one {# hallazgo obligatorio reproducido solo en head} other {# hallazgos obligatorios reproducidos solo en head}}",
7767
+ "install.warn.sandbox_offer": "El sandbox de Claude Code funciona aquí. Añade --sandbox para activarlo solo para ti, en .claude/settings.local.json: no arranca sin el sandbox, oculta ~/.ssh, ~/.aws y los archivos .env a los comandos y solo deja llegar a los hosts de sandbox.hosts.",
7768
+ "install.warn.sandbox_unsupported": "Se ignoró --sandbox: Windows nativo no tiene sandbox de Claude Code, así que aquí solo protegen los bloqueos. Ejecuta Claude Code dentro de WSL2 para usarlo."
7713
7769
  };
7714
7770
 
7715
7771
  // src/i18n/pt-BR.json
@@ -8398,7 +8454,9 @@ var pt_BR_default = {
8398
8454
  "critique.decision": "Decisão necessária: o achado {id} ({what}) já falha na branch base e é um risco ativo.",
8399
8455
  "critique.verdict": "Veredito: {verdict}",
8400
8456
  "critique.error.invalid": "o arquivo de achados não é válido: {detail}",
8401
- "critique.error.must_fix": "{count, plural, one {# achado obrigatório reproduzido só no head} other {# achados obrigatórios reproduzidos só no head}}"
8457
+ "critique.error.must_fix": "{count, plural, one {# achado obrigatório reproduzido só no head} other {# achados obrigatórios reproduzidos só no head}}",
8458
+ "install.warn.sandbox_offer": "O sandbox do Claude Code roda aqui. Acrescente --sandbox para ligar só para você, em .claude/settings.local.json: ele se recusa a iniciar sem o sandbox, esconde ~/.ssh, ~/.aws e os arquivos .env dos comandos e só deixa chegar aos hosts de sandbox.hosts.",
8459
+ "install.warn.sandbox_unsupported": "--sandbox foi ignorado: o Windows nativo não tem sandbox do Claude Code, então aqui só as travas protegem. Rode o Claude Code dentro do WSL2 para usar."
8402
8460
  };
8403
8461
 
8404
8462
  // src/i18n/review.json
@@ -9088,7 +9146,9 @@ var review_default = {
9088
9146
  "critique.decision": "machine",
9089
9147
  "critique.verdict": "machine",
9090
9148
  "critique.error.invalid": "machine",
9091
- "critique.error.must_fix": "machine"
9149
+ "critique.error.must_fix": "machine",
9150
+ "install.warn.sandbox_offer": "machine",
9151
+ "install.warn.sandbox_unsupported": "machine"
9092
9152
  },
9093
9153
  es: {
9094
9154
  "command.init.summary": "machine",
@@ -9775,7 +9835,9 @@ var review_default = {
9775
9835
  "critique.decision": "machine",
9776
9836
  "critique.verdict": "machine",
9777
9837
  "critique.error.invalid": "machine",
9778
- "critique.error.must_fix": "machine"
9838
+ "critique.error.must_fix": "machine",
9839
+ "install.warn.sandbox_offer": "machine",
9840
+ "install.warn.sandbox_unsupported": "machine"
9779
9841
  }
9780
9842
  };
9781
9843
 
@@ -17794,11 +17856,18 @@ var itemWarning = (t, item2) => {
17794
17856
  return [{ code: "edited_file", message: t("install.warn.edited", { path: item2.path }) }];
17795
17857
  return [];
17796
17858
  };
17859
+ var sandboxWarnings = (t, plan) => {
17860
+ if (plan.sandbox === "unsupported")
17861
+ return [{ code: "sandbox_unsupported", message: t("install.warn.sandbox_unsupported") }];
17862
+ if (plan.sandbox === "offer") return [{ code: "sandbox_offer", message: t("install.warn.sandbox_offer") }];
17863
+ return [];
17864
+ };
17797
17865
  var planWarnings = (t, plan) => [
17798
17866
  ...plan.detection.gitRoot ? [] : [{ code: "not_git", message: t("install.warn.not_git") }],
17799
17867
  ...plan.detection.claude.failClosed ? [] : [{ code: "fail_open", message: t("install.warn.fail_open", { version: plan.detection.claude.version ?? "?" }) }],
17800
17868
  ...plan.detection.location.wslMount ? [{ code: "wsl_mount", message: t("install.warn.wsl_mount", { path: plan.detection.root }) }] : [],
17801
17869
  ...plan.detection.location.synced ? [{ code: "synced_folder", message: t("install.warn.synced_folder", { path: plan.detection.root }) }] : [],
17870
+ ...sandboxWarnings(t, plan),
17802
17871
  ...plan.detection.authorities.map((authority) => authorityWarning(t, authority)),
17803
17872
  ...plan.items.flatMap((item2) => itemWarning(t, item2))
17804
17873
  ];
@@ -17839,7 +17908,8 @@ var flagsFor = (request, preset) => [
17839
17908
  `finitemonkey ${request.mode} --yes`,
17840
17909
  ...request.mode === "init" && preset !== "neutral" ? [`--preset ${preset}`] : [],
17841
17910
  ...request.bin === void 0 ? [] : [`--bin "${request.bin}"`],
17842
- ...request.marketplace ? ["--marketplace"] : []
17911
+ ...request.marketplace ? ["--marketplace"] : [],
17912
+ ...request.sandbox === true ? ["--sandbox"] : []
17843
17913
  ].join(" ");
17844
17914
  var presentPlan = (ctx, request, plan) => {
17845
17915
  const data = planData(ctx.t, plan, request.mode, false, void 0);
@@ -17865,7 +17935,7 @@ var runInstall = (ctx, request, presetFallback) => {
17865
17935
  const { project, gitRoot: gitRoot2 } = openProject(ctx);
17866
17936
  const previous = readPreviousLock(project);
17867
17937
  if (!previous.ok) return failureFrom(ctx.t, planProblem(previous.error));
17868
- const input = planInputFor(ctx, preset, request.bin, request.marketplace);
17938
+ const input = planInputFor(ctx, preset, request.bin, request.marketplace, request.sandbox === true);
17869
17939
  const plan = buildPlan(project, detect(project, gitRoot2, input), input, previous.value);
17870
17940
  if (!plan.ok) return failureFrom(ctx.t, planProblem(plan.error));
17871
17941
  if (ctx.dryRun || !ctx.yes || !needsWrite(plan.value)) return presentPlan(ctx, request, plan.value);
@@ -17876,6 +17946,7 @@ var initRequest = (args2) => ({
17876
17946
  preset: stringOption(args2.values, "preset"),
17877
17947
  bin: stringOption(args2.values, "bin"),
17878
17948
  marketplace: args2.values["marketplace"] === true,
17949
+ sandbox: args2.values["sandbox"] === true,
17879
17950
  showDiff: false
17880
17951
  });
17881
17952
  var withLookBack = (ctx, result) => {
@@ -17892,12 +17963,13 @@ var withLookBack = (ctx, result) => {
17892
17963
  var initCommand = {
17893
17964
  path: ["init"],
17894
17965
  summaryKey: "command.init.summary",
17895
- usage: "finitemonkey init [--preset neutral|gabriel] [--bin <path>] [--marketplace] [--look-back] [--dry-run] [--yes]",
17966
+ usage: "finitemonkey init [--preset neutral|gabriel] [--bin <path>] [--marketplace] [--sandbox] [--look-back] [--dry-run] [--yes]",
17896
17967
  status: "ready",
17897
17968
  options: {
17898
17969
  preset: { type: "string" },
17899
17970
  bin: { type: "string" },
17900
17971
  marketplace: { type: "boolean" },
17972
+ sandbox: { type: "boolean" },
17901
17973
  "look-back": { type: "boolean" }
17902
17974
  },
17903
17975
  run: (ctx, args2) => {
@@ -19536,6 +19608,10 @@ var structural = (entry2, current2) => {
19536
19608
  const text9 = blockless(entry2.kind, current2);
19537
19609
  return text9.trim() === "" && !entry2.existed ? void 0 : text9;
19538
19610
  }
19611
+ if (entry2.kind === "sandbox") {
19612
+ const text9 = withoutSandbox(current2, entry2.sandbox ?? {});
19613
+ return text9 === void 0 && entry2.existed ? "{}\n" : text9;
19614
+ }
19539
19615
  const parsed = parseSettings(current2);
19540
19616
  if (!parsed.ok || entry2.added === void 0) return current2;
19541
19617
  const settings = unmergeSettings(parsed.value, entry2.added, entry2.handlers ?? []);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "finitemonkey",
3
- "version": "0.1.0-next.26",
3
+ "version": "0.1.0-next.27",
4
4
  "description": "Claude Code hooks that stop a coding agent from saying done before the tests pass: a machine-checked definition of done, plus guardrails for secrets, git safety and production. Pre-release.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",