finitemonkey 0.1.0-next.11 → 0.1.0-next.12

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
@@ -123,6 +123,29 @@ La comparación completa, con fuentes, está en [docs/compare.es.md](docs/compar
123
123
  - **Métodos como superpowers, GSD, Spec Kit u OpenSpec** le dicen al agente cómo planificar y construir. Finite Monkey no los sustituye: comprueba el resultado al final, uses el método que uses.
124
124
  - **Guardas sueltos como tdd-guard, cc-safety-net o dcg** hacen un trabajo cada uno, y lo hacen bien. Finite Monkey cubre cuatro daños con un control, una instalación y un `undo`. Si solo necesitas uno de esos trabajos, un guarda suelto puede ser lo más sencillo.
125
125
 
126
+ <!-- generated:footprint:start -->
127
+ ## Qué lee, escribe y envía
128
+
129
+ Generado desde `src/core/footprint.ts`, la lista que los tests exigen al código. Sin telemetría. Las rutas son relativas al proyecto salvo las que empiezan con `~`.
130
+
131
+ | | Qué | Para qué |
132
+ |---|---|---|
133
+ | Lee | `stdin` | El evento que Claude Code pasa a cada hook: la llamada a la herramienta, el prompt o la petición de parada. |
134
+ | Lee | `.claude/settings.json`, `.claude/settings.local.json`, `~/.claude/settings.json` | La configuración de Claude Code, para comprobar que los hooks están instalados y que nada los desactiva. `$CLAUDE_CONFIG_DIR` reemplaza `~/.claude` si está definido. |
135
+ | Lee | `CLAUDE.md`, `.claude/CLAUDE.md`, `CLAUDE.local.md`, `AGENTS.md`, `.claude/rules/`, `.claudeignore` | Archivos de instrucciones, que `doctor` lee para señalar reglas que solo existen como texto. |
136
+ | Lee | `.git/` | El repositorio a través del comando `git`: estado, diffs, refs y el reflog. |
137
+ | Lee | `git diff` | Archivos modificados, revisados en busca de secretos, tests saltados, placeholders y patrones de prosa. |
138
+ | Lee | `.finitemonkey/config.yml`, `.finitemonkey/briefing.md` | La configuración del proyecto y el briefing opcional para subagentes. |
139
+ | 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 | `.finitemonkey/tasks/`, `.finitemonkey/state.json` | Contratos de tarea y cuál tarea está activa. |
141
+ | 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. |
142
+ | Escribe | `.finitemonkey/trust.json`, `.finitemonkey/allow/`, `.finitemonkey/cache/` | Aprobaciones y permisos escritos por la persona, y una caché breve de los pull requests abiertos. |
143
+ | Escribe | `.finitemonkey/findings/baseline.json`, `.finitemonkey/incidents/` | La línea base de hallazgos aceptados y los incidentes de exposición. Ambos guardan huellas y máscaras, nunca el valor de un secreto. |
144
+ | Escribe | `.finitemonkey/git/`, `.finitemonkey/git-journal.jsonl`, `.finitemonkey/merge-dropped.json`, `refs/finitemonkey/backup/` | Snapshots antes de comandos git destructivos, el diario que lee `undo` y las decisiones sobre líneas descartadas en merges. |
145
+ | Escribe | `.finitemonkey/reports/` | Informes de error enmascarados que escribe `doctor --report`, solo cuando se pide. |
146
+ | Envía | `api.github.com` | A través de `gh`, solo si está instalado y con sesión iniciada: el estado del pull request indicado en una tarea y tus pull requests abiertos para la línea de la sesión. El código nunca se sube. |
147
+ <!-- generated:footprint:end -->
148
+
126
149
  ## Preguntas frecuentes
127
150
 
128
151
  Más preguntas, una por cada búsqueda real, en [docs/faq.es.md](docs/faq.es.md).
@@ -131,7 +154,7 @@ Más preguntas, una por cada búsqueda real, en [docs/faq.es.md](docs/faq.es.md)
131
154
  No. Son hooks y comprobaciones que se ejecutan aunque el modelo no se acuerde.
132
155
 
133
156
  **¿Envía mi código a algún sitio?**
134
- No. Por defecto nada sale de tu máquina. Sin telemetría.
157
+ No. Tu código nunca sale de tu máquina y no hay telemetría. La única llamada de red es `gh` preguntando a GitHub por pull requests, y solo si `gh` tiene sesión iniciada. La lista completa está en [Qué lee, escribe y envía](#qué-lee-escribe-y-envía).
135
158
 
136
159
  **¿Me va a ralentizar?**
137
160
  Las tareas pequeñas no tienen ceremonia, solo el control al final. Los hooks rápidos siguen siendo rápidos; la comprobación costosa va en `Stop`. El CI rechaza un pull request que lleve un guard de `PreToolUse` por encima de 50 ms en el p95 o que agregue contexto al inicio de la sesión; los límites están en [budgets.json](budgets.json). Publicaremos los tiempos de punta a punta junto con el benchmark.
package/README.md CHANGED
@@ -123,6 +123,29 @@ The full comparison, with sources, is in [docs/compare.md](docs/compare.md).
123
123
  - **Methods like superpowers, GSD, Spec Kit or OpenSpec** tell the agent how to plan and build. Finite Monkey doesn't replace them. It checks the result at the end, whatever method you used.
124
124
  - **Single guards like tdd-guard, cc-safety-net or dcg** each do one job well. Finite Monkey covers four damages with one gate, one install and one `undo`. If you only need one of those jobs, a single guard may be the simpler choice.
125
125
 
126
+ <!-- generated:footprint:start -->
127
+ ## What it reads, writes and sends
128
+
129
+ Generated from `src/core/footprint.ts`, the list the tests hold the code to. No telemetry. Paths are relative to the project unless they start with `~`.
130
+
131
+ | | What | Why |
132
+ |---|---|---|
133
+ | Reads | `stdin` | The event Claude Code passes to each hook: the tool call, the prompt or the stop request. |
134
+ | Reads | `.claude/settings.json`, `.claude/settings.local.json`, `~/.claude/settings.json` | Claude Code settings, to check that the hooks are installed and nothing switches them off. `$CLAUDE_CONFIG_DIR` replaces `~/.claude` when set. |
135
+ | Reads | `CLAUDE.md`, `.claude/CLAUDE.md`, `CLAUDE.local.md`, `AGENTS.md`, `.claude/rules/`, `.claudeignore` | Instruction files, read by `doctor` to report rules that exist only as text. |
136
+ | Reads | `.git/` | The repository through the `git` command: status, diffs, refs and the reflog. |
137
+ | Reads | `git diff` | Changed files, scanned for secrets, test skips, placeholders and prose patterns. |
138
+ | Reads | `.finitemonkey/config.yml`, `.finitemonkey/briefing.md` | Project configuration and the optional briefing for subagents. |
139
+ | 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 | `.finitemonkey/tasks/`, `.finitemonkey/state.json` | Task contracts and which task is active. |
141
+ | Writes | `.finitemonkey/evidence/`, `.finitemonkey/log.jsonl`, `.finitemonkey/prod-evidence.jsonl` | Evidence of each check, tied to the tree hash, and the gate log. |
142
+ | Writes | `.finitemonkey/trust.json`, `.finitemonkey/allow/`, `.finitemonkey/cache/` | Approvals and grants typed by the person, and a short-lived cache of open pull requests. |
143
+ | Writes | `.finitemonkey/findings/baseline.json`, `.finitemonkey/incidents/` | The accepted-findings baseline and exposure incidents. Both hold fingerprints and masks, never a secret value. |
144
+ | Writes | `.finitemonkey/git/`, `.finitemonkey/git-journal.jsonl`, `.finitemonkey/merge-dropped.json`, `refs/finitemonkey/backup/` | Snapshots before destructive git commands, the journal that `undo` reads and dropped-line decisions from merges. |
145
+ | Writes | `.finitemonkey/reports/` | Masked bug reports that `doctor --report` writes, only when asked. |
146
+ | Sends | `api.github.com` | Through `gh`, only when it is installed and logged in: the state of the pull request named in a task, and your open pull requests for the session line. Code is never uploaded. |
147
+ <!-- generated:footprint:end -->
148
+
126
149
  ## FAQ
127
150
 
128
151
  More questions, one per real search, in [docs/faq.md](docs/faq.md).
@@ -131,7 +154,7 @@ More questions, one per real search, in [docs/faq.md](docs/faq.md).
131
154
  No. It is hooks and checks that run whether or not the model remembers them.
132
155
 
133
156
  **Does it send my code anywhere?**
134
- No. Nothing leaves your machine by default. No telemetry.
157
+ No. Your code never leaves your machine, and there is no telemetry. The one network call is `gh` asking GitHub about pull requests, and only when `gh` is logged in. The full list is in [What it reads, writes and sends](#what-it-reads-writes-and-sends).
135
158
 
136
159
  **Will it slow me down?**
137
160
  Small tasks get no ceremony, only the gate at the end. Fast hooks stay fast; the expensive check runs at `Stop`. CI fails a pull request that pushes a `PreToolUse` guard past 50 ms at p95 or adds context at session start; the ceilings are in [budgets.json](budgets.json). We'll publish end-to-end timings with the benchmark.
package/README.pt-BR.md CHANGED
@@ -123,6 +123,29 @@ A comparação completa, com fontes, está em [docs/compare.pt-BR.md](docs/compa
123
123
  - **Métodos como superpowers, GSD, Spec Kit ou OpenSpec** dizem ao agente como planejar e construir. O Finite Monkey não substitui nenhum deles: confere o resultado no fim, seja qual for o método.
124
124
  - **Guardas avulsos como tdd-guard, cc-safety-net ou dcg** fazem um trabalho cada um, e bem. O Finite Monkey cobre quatro danos com um portão, uma instalação e um `undo`. Se você só precisa de um desses trabalhos, um guarda avulso pode ser a escolha mais simples.
125
125
 
126
+ <!-- generated:footprint:start -->
127
+ ## O que ele lê, grava e envia
128
+
129
+ Gerado a partir de `src/core/footprint.ts`, a lista que os testes cobram do código. Sem telemetria. Caminhos são relativos ao projeto, exceto os que começam com `~`.
130
+
131
+ | | O quê | Para quê |
132
+ |---|---|---|
133
+ | Lê | `stdin` | O evento que o Claude Code passa a cada hook: a chamada de ferramenta, o prompt ou o pedido de parada. |
134
+ | Lê | `.claude/settings.json`, `.claude/settings.local.json`, `~/.claude/settings.json` | As configurações do Claude Code, para conferir que os hooks estão instalados e que nada os desliga. `$CLAUDE_CONFIG_DIR` substitui `~/.claude` quando definido. |
135
+ | Lê | `CLAUDE.md`, `.claude/CLAUDE.md`, `CLAUDE.local.md`, `AGENTS.md`, `.claude/rules/`, `.claudeignore` | Arquivos de instrução, lidos pelo `doctor` para apontar regras que só existem como texto. |
136
+ | Lê | `.git/` | O repositório pelo comando `git`: status, diffs, refs e o reflog. |
137
+ | Lê | `git diff` | Arquivos alterados, varridos atrás de segredos, testes pulados, placeholders e padrões de prosa. |
138
+ | Lê | `.finitemonkey/config.yml`, `.finitemonkey/briefing.md` | A configuração do projeto e o briefing opcional para subagentes. |
139
+ | 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 | `.finitemonkey/tasks/`, `.finitemonkey/state.json` | Contratos de tarefa e qual tarefa está ativa. |
141
+ | 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. |
142
+ | Grava | `.finitemonkey/trust.json`, `.finitemonkey/allow/`, `.finitemonkey/cache/` | Aprovações e liberações digitadas pela pessoa, e um cache curto dos pull requests abertos. |
143
+ | Grava | `.finitemonkey/findings/baseline.json`, `.finitemonkey/incidents/` | A baseline de achados aceitos e os incidentes de exposição. Os dois guardam impressões e máscaras, nunca o valor de um segredo. |
144
+ | Grava | `.finitemonkey/git/`, `.finitemonkey/git-journal.jsonl`, `.finitemonkey/merge-dropped.json`, `refs/finitemonkey/backup/` | Snapshots antes de comandos git destrutivos, o diário que o `undo` lê e as decisões sobre linhas descartadas em merges. |
145
+ | Grava | `.finitemonkey/reports/` | Relatórios de bug mascarados que o `doctor --report` grava, só quando pedido. |
146
+ | Envia | `api.github.com` | Pelo `gh`, só quando ele está instalado e logado: o estado do pull request citado numa tarefa e os seus pull requests abertos para a linha da sessão. Código nunca é enviado. |
147
+ <!-- generated:footprint:end -->
148
+
126
149
  ## Perguntas frequentes
127
150
 
128
151
  Mais perguntas, uma para cada busca real, em [docs/faq.pt-BR.md](docs/faq.pt-BR.md).
@@ -131,7 +154,7 @@ Mais perguntas, uma para cada busca real, em [docs/faq.pt-BR.md](docs/faq.pt-BR.
131
154
  Não. São hooks e checagens que rodam lembrando o modelo ou não.
132
155
 
133
156
  **Ele manda meu código para algum lugar?**
134
- Não. Por padrão nada sai da sua máquina. Sem telemetria.
157
+ Não. Seu código nunca sai da sua máquina, e não há telemetria. A única chamada de rede é o `gh` perguntando ao GitHub sobre pull requests, e só quando o `gh` está logado. A lista completa está em [O que ele lê, grava e envia](#o-que-ele-lê-grava-e-envia).
135
158
 
136
159
  **Vai me deixar mais lento?**
137
160
  Tarefa pequena não tem cerimônia, só o portão no fim. Hook rápido continua rápido; a checagem cara roda no `Stop`. O CI reprova um pull request que leve um guard de `PreToolUse` acima de 50 ms no p95 ou que acrescente contexto no início da sessão; os limites estão em [budgets.json](budgets.json). Os tempos de ponta a ponta saem junto com o benchmark.
@@ -21,7 +21,7 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
21
21
 
22
22
  // src/core/node-exec.ts
23
23
  import { spawn, spawnSync } from "node:child_process";
24
- import { appendFileSync, mkdirSync } from "node:fs";
24
+ import { appendFileSync, existsSync, mkdirSync } from "node:fs";
25
25
  import { dirname } from "node:path";
26
26
 
27
27
  // src/core/parsed.ts
@@ -45,6 +45,32 @@ var optionalString = (record2, key) => {
45
45
  return typeof value === "string" ? value : void 0;
46
46
  };
47
47
 
48
+ // src/core/windows-command.ts
49
+ import { win32 } from "node:path";
50
+ var DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD";
51
+ var NEEDS_SHELL = /\.(cmd|bat)$/i;
52
+ var NEEDS_QUOTES = /[\s"&|<>^()%!]/;
53
+ var envValue = (env, name) => {
54
+ const key = Object.keys(env).find((candidate) => candidate.toUpperCase() === name);
55
+ return key === void 0 ? void 0 : env[key];
56
+ };
57
+ var quoteForCmd = (arg) => arg !== "" && !NEEDS_QUOTES.test(arg) ? arg : `"${arg.replace(/"/g, '""')}"`;
58
+ var resolveWindowsCommand = (command, env, exists) => {
59
+ if (/[\\/]/.test(command) || win32.extname(command) !== "") {
60
+ return { command, shell: NEEDS_SHELL.test(command) };
61
+ }
62
+ const dirs = (envValue(env, "PATH") ?? "").split(";").filter((dir) => dir !== "");
63
+ const extensions = (envValue(env, "PATHEXT") ?? DEFAULT_PATHEXT).split(";").filter((ext) => ext !== "");
64
+ for (const dir of dirs) {
65
+ for (const ext of extensions) {
66
+ const candidate = win32.join(dir, `${command}${ext.toLowerCase()}`);
67
+ if (exists(candidate)) return { command: candidate, shell: NEEDS_SHELL.test(candidate) };
68
+ }
69
+ }
70
+ return { command, shell: false };
71
+ };
72
+ var windowsCommandLine = (resolved, args) => [quoteForCmd(resolved.command), ...args.map(quoteForCmd)].join(" ");
73
+
48
74
  // src/core/node-exec.ts
49
75
  var SHELL_NOT_FOUND = /* @__PURE__ */ new Set([127, 9009]);
50
76
  var CMD_NOT_FOUND = /is not recognized as an internal or external command|não é reconhecido como um comando interno|no se reconoce como un comando interno/i;
@@ -67,7 +93,8 @@ var nodeExec = (request) => {
67
93
  Object.entries({ ...process.env, ...request.env }).filter(([name]) => !(request.unset ?? []).includes(name))
68
94
  )
69
95
  };
70
- const result = request.shell === true ? spawnSync([request.command, ...request.args].join(" "), { ...options, shell: true }) : spawnSync(request.command, [...request.args], options);
96
+ const resolved = process.platform === "win32" && request.shell !== true ? resolveWindowsCommand(request.command, options.env, existsSync) : void 0;
97
+ const result = request.shell === true ? spawnSync([request.command, ...request.args].join(" "), { ...options, shell: true }) : resolved?.shell === true ? spawnSync(windowsCommandLine(resolved, request.args), { ...options, shell: true }) : spawnSync(resolved?.command ?? request.command, [...request.args], options);
71
98
  const code = errnoCode(result.error);
72
99
  const exitCode = result.status;
73
100
  const shellMissing = request.shell === true && exitCode !== null && (SHELL_NOT_FOUND.has(exitCode) || exitCode === 1 && CMD_NOT_FOUND.test(text(result.stderr)));
@@ -1132,6 +1159,12 @@ var isBundlePath = (path) => path.endsWith(".mjs");
1132
1159
 
1133
1160
  // src/install/plan.ts
1134
1161
  var CHANGING = ["create", "merge", "update"];
1162
+ var WSL_MOUNT = /^\/mnt\/[a-z](\/|$)/i;
1163
+ var SYNCED = /(OneDrive|Dropbox|iCloud ?Drive|Mobile Documents|Google ?Drive)/i;
1164
+ var locationOf = (root, env) => ({
1165
+ wslMount: WSL_MOUNT.test(root) && (env["WSL_DISTRO_NAME"] !== void 0 || env["WSL_INTEROP"] !== void 0),
1166
+ synced: SYNCED.test(root)
1167
+ });
1135
1168
  var detect = (project, gitRoot2, input) => ({
1136
1169
  root: project.root,
1137
1170
  gitRoot: gitRoot2,
@@ -1140,7 +1173,8 @@ var detect = (project, gitRoot2, input) => ({
1140
1173
  settings: settingsInfo(project.read(SETTINGS_FILE)),
1141
1174
  authorities: detectAuthorities(project, input.env),
1142
1175
  runner: chooseRunner(project, input.bin),
1143
- claude: detectClaude(project.runtime.probe)
1176
+ claude: detectClaude(project.runtime.probe),
1177
+ location: locationOf(project.root, input.env)
1144
1178
  });
1145
1179
  var readPreviousLock = (project) => {
1146
1180
  const text6 = project.read(LOCK_FILE);
@@ -1996,20 +2030,20 @@ var detectOs = ({ platform, env, runtime }) => {
1996
2030
  const version = runtime.readText(toAbsolutePath("/proc/version"));
1997
2031
  return version.ok && /microsoft/i.test(version.value) ? "wsl" : "linux";
1998
2032
  };
1999
- var WSL_MOUNT = /^\/mnt\/[a-z](\/|$)/i;
2000
- var SYNCED = /(OneDrive|Dropbox|iCloud ?Drive|Mobile Documents|Google ?Drive)/i;
2033
+ var WSL_MOUNT2 = /^\/mnt\/[a-z](\/|$)/i;
2034
+ var SYNCED2 = /(OneDrive|Dropbox|iCloud ?Drive|Mobile Documents|Google ?Drive)/i;
2001
2035
  var NON_ASCII = /[^\x20-\x7e]/;
2002
2036
  var pathTraits = (path) => ({
2003
2037
  spaces: path.includes(" "),
2004
2038
  nonAscii: NON_ASCII.test(path),
2005
- synced: SYNCED.test(path)
2039
+ synced: SYNCED2.test(path)
2006
2040
  });
2007
2041
  var checkOs = (env) => {
2008
2042
  const os = detectOs(env);
2009
2043
  const findings = [
2010
2044
  { check: "os", status: "ok", messageKey: `doctor.os.${os}`, params: { platform: env.platform } }
2011
2045
  ];
2012
- if (os === "wsl" && WSL_MOUNT.test(env.cwd)) {
2046
+ if (os === "wsl" && WSL_MOUNT2.test(env.cwd)) {
2013
2047
  findings.push({
2014
2048
  check: "path.wsl_mount",
2015
2049
  status: "warn",
@@ -5088,6 +5122,8 @@ var en_default = {
5088
5122
  "install.plan.title": "Finite Monkey plan for {root} (preset {preset}):",
5089
5123
  "install.warning": "warning: {message}",
5090
5124
  "install.warn.not_git": "this folder is not inside a git repository; installing here anyway",
5125
+ "install.warn.wsl_mount": "{path} is on a Windows drive mounted in WSL: git and hooks run several times slower and file watching misses changes; move the project to the Linux disk (for example ~/projects) and run init there",
5126
+ "install.warn.synced_folder": "{path} is inside a synced folder (OneDrive, Dropbox, iCloud or Google Drive): sync can lock or rewrite files while git and hooks use them; move the project out of the synced folder",
5091
5127
  "install.warn.fail_open": "Claude Code {version} cannot block an action when a hook crashes (needs 2.1.295 or newer); guards fail open until you update Claude Code",
5092
5128
  "install.warn.authority": "{name} ({source}) also decides when a session may stop; Finite Monkey works alongside it and never turns it off",
5093
5129
  "install.warn.plugin": "the finitemonkey plugin is enabled ({source}); its hooks step aside while this project has its own Finite Monkey hooks",
@@ -5375,7 +5411,9 @@ var en_default = {
5375
5411
  "gate.merge.clean": "{count, plural, one {# merge checked} other {# merges checked}}, nothing lost",
5376
5412
  "doctor.memory.lines": "{file}: {lines, plural, one {# line} other {# lines}} of {max}. Edits made during a session apply only after /clear, /compact or a restart.",
5377
5413
  "doctor.memory.too_long": "{file}: {lines, plural, one {# line} other {# lines}}, above the ceiling of {max}; long instruction files get followed less",
5378
- "doctor.memory.fix_too_long": "Move details out of {file} into docs or skills until it has at most {max} lines, or raise memory.maxLines in .finitemonkey/config.yml"
5414
+ "doctor.memory.fix_too_long": "Move details out of {file} into docs or skills until it has at most {max} lines, or raise memory.maxLines in .finitemonkey/config.yml",
5415
+ "doctor.git.autocrlf": 'git converts line endings to CRLF on checkout (core.autocrlf=true) and {file} does not pin LF; shell hooks and scripts checked out with CRLF fail with "bad interpreter"',
5416
+ "doctor.git.fix_autocrlf": "add `* text=auto eol=lf` to {file}, commit it, then run `git add --renormalize .`"
5379
5417
  };
5380
5418
 
5381
5419
  // src/i18n/es.json
@@ -5588,6 +5626,8 @@ var es_default = {
5588
5626
  "install.plan.title": "Plan de Finite Monkey para {root} (preset {preset}):",
5589
5627
  "install.warning": "aviso: {message}",
5590
5628
  "install.warn.not_git": "esta carpeta no está dentro de un repositorio git; se instala aquí de todos modos",
5629
+ "install.warn.wsl_mount": "{path} está en un disco de Windows montado en WSL: git y los hooks van varias veces más lentos y la vigilancia de archivos pierde cambios; mueve el proyecto al disco de Linux (por ejemplo ~/proyectos) y ejecuta init allí",
5630
+ "install.warn.synced_folder": "{path} está dentro de una carpeta sincronizada (OneDrive, Dropbox, iCloud o Google Drive): la sincronización puede bloquear o reescribir archivos mientras git y los hooks los usan; saca el proyecto de la carpeta sincronizada",
5591
5631
  "install.warn.fail_open": "Claude Code {version} no puede bloquear la acción cuando un hook falla (necesita 2.1.295 o más nueva); los guardas fallan abiertos hasta que actualices Claude Code",
5592
5632
  "install.warn.authority": "{name} ({source}) también decide cuándo puede terminar la sesión; Finite Monkey convive con él y nunca lo desactiva",
5593
5633
  "install.warn.plugin": "el plugin finitemonkey está habilitado ({source}); sus hooks ceden el paso mientras este proyecto tenga sus propios hooks de Finite Monkey",
@@ -5875,7 +5915,9 @@ var es_default = {
5875
5915
  "gate.merge.clean": "{count, plural, one {# fusión revisada} other {# fusiones revisadas}}, no se perdió nada",
5876
5916
  "doctor.memory.lines": "{file}: {lines, plural, one {# línea} other {# líneas}} de {max}. Los cambios hechos durante una sesión solo se aplican tras /clear, /compact o reiniciar.",
5877
5917
  "doctor.memory.too_long": "{file}: {lines, plural, one {# línea} other {# líneas}}, por encima del techo de {max}; un archivo de instrucciones largo se sigue menos",
5878
- "doctor.memory.fix_too_long": "Lleva detalles de {file} a docs o skills hasta que tenga como máximo {max} líneas, o sube memory.maxLines en .finitemonkey/config.yml"
5918
+ "doctor.memory.fix_too_long": "Lleva detalles de {file} a docs o skills hasta que tenga como máximo {max} líneas, o sube memory.maxLines en .finitemonkey/config.yml",
5919
+ "doctor.git.autocrlf": 'git convierte los saltos de línea a CRLF al hacer checkout (core.autocrlf=true) y {file} no fija LF; los hooks y scripts de shell con CRLF fallan con "bad interpreter"',
5920
+ "doctor.git.fix_autocrlf": "añade `* text=auto eol=lf` a {file}, haz commit y ejecuta `git add --renormalize .`"
5879
5921
  };
5880
5922
 
5881
5923
  // src/i18n/pt-BR.json
@@ -6088,6 +6130,8 @@ var pt_BR_default = {
6088
6130
  "install.plan.title": "Plano do Finite Monkey para {root} (preset {preset}):",
6089
6131
  "install.warning": "aviso: {message}",
6090
6132
  "install.warn.not_git": "esta pasta não está dentro de um repositório git; instalando aqui mesmo assim",
6133
+ "install.warn.wsl_mount": "{path} está num disco do Windows montado no WSL: git e hooks ficam várias vezes mais lentos e a observação de arquivos perde mudanças; mova o projeto para o disco do Linux (por exemplo ~/projetos) e rode o init lá",
6134
+ "install.warn.synced_folder": "{path} está dentro de uma pasta sincronizada (OneDrive, Dropbox, iCloud ou Google Drive): a sincronização pode travar ou reescrever arquivos enquanto o git e os hooks os usam; tire o projeto da pasta sincronizada",
6091
6135
  "install.warn.fail_open": "o Claude Code {version} não consegue barrar a ação quando um hook quebra (precisa da 2.1.295 ou mais nova); as travas falham abertas até você atualizar o Claude Code",
6092
6136
  "install.warn.authority": "{name} ({source}) também decide quando a sessão pode parar; o Finite Monkey convive com ele e nunca o desliga",
6093
6137
  "install.warn.plugin": "o plugin finitemonkey está habilitado ({source}); os hooks dele cedem a vez enquanto este projeto tiver os próprios hooks do Finite Monkey",
@@ -6375,7 +6419,9 @@ var pt_BR_default = {
6375
6419
  "gate.merge.clean": "{count, plural, one {# mescla conferida} other {# mesclas conferidas}}, nada se perdeu",
6376
6420
  "doctor.memory.lines": "{file}: {lines, plural, one {# linha} other {# linhas}} de {max}. Edições feitas durante a sessão só valem depois de /clear, /compact ou reiniciar.",
6377
6421
  "doctor.memory.too_long": "{file}: {lines, plural, one {# linha} other {# linhas}}, acima do teto de {max}; arquivo de instruções longo é menos seguido",
6378
- "doctor.memory.fix_too_long": "Leve detalhes de {file} para docs ou skills até ficar com no máximo {max} linhas, ou aumente memory.maxLines em .finitemonkey/config.yml"
6422
+ "doctor.memory.fix_too_long": "Leve detalhes de {file} para docs ou skills até ficar com no máximo {max} linhas, ou aumente memory.maxLines em .finitemonkey/config.yml",
6423
+ "doctor.git.autocrlf": 'o git converte quebras de linha para CRLF no checkout (core.autocrlf=true) e o {file} não fixa LF; hooks e scripts de shell com CRLF falham com "bad interpreter"',
6424
+ "doctor.git.fix_autocrlf": "acrescente `* text=auto eol=lf` ao {file}, faça commit e rode `git add --renormalize .`"
6379
6425
  };
6380
6426
 
6381
6427
  // src/i18n/review.json
@@ -6493,6 +6539,43 @@ var review_default = {
6493
6539
  "doctor.project.fix_lockfiles": "machine",
6494
6540
  "doctor.project.configs": "machine",
6495
6541
  "doctor.project.fix_configs": "machine",
6542
+ "doctor.hooks.ok": "machine",
6543
+ "doctor.hooks.no_command": "machine",
6544
+ "doctor.hooks.not_found": "machine",
6545
+ "doctor.hooks.timeout": "machine",
6546
+ "doctor.hooks.exit": "machine",
6547
+ "doctor.hooks.output": "machine",
6548
+ "doctor.hooks.fix_broken": "machine",
6549
+ "doctor.hooks.fix_broken_plugin": "machine",
6550
+ "doctor.hooks.slow": "machine",
6551
+ "doctor.hooks.fix_slow": "machine",
6552
+ "doctor.hooks.fail_open": "machine",
6553
+ "doctor.hooks.fix_fail_open": "machine",
6554
+ "doctor.hooks.matcher": "machine",
6555
+ "doctor.hooks.fix_matcher": "machine",
6556
+ "doctor.hooks.not_fired": "machine",
6557
+ "doctor.hooks.fix_not_fired": "machine",
6558
+ "doctor.hooks.schema": "machine",
6559
+ "doctor.hooks.fix_schema": "machine",
6560
+ "doctor.hooks.plugin_disabled": "machine",
6561
+ "doctor.hooks.fix_plugin_disabled": "machine",
6562
+ "doctor.hooks.none": "machine",
6563
+ "doctor.hooks.fix_none": "machine",
6564
+ "doctor.hooks.absent": "machine",
6565
+ "doctor.hooks.fix_absent": "machine",
6566
+ "doctor.hooks.version_ok": "machine",
6567
+ "doctor.hooks.version_plugin": "machine",
6568
+ "doctor.hooks.fix_version_plugin": "machine",
6569
+ "doctor.hooks.version_cli": "machine",
6570
+ "doctor.hooks.fix_version_cli": "machine",
6571
+ "doctor.hooks.fix_version_cli_plugin": "machine",
6572
+ "doctor.fix.not_installed": "machine",
6573
+ "doctor.fix.nothing": "machine",
6574
+ "doctor.fix.would": "machine",
6575
+ "doctor.fix.applied": "machine",
6576
+ "doctor.fix.backup": "machine",
6577
+ "doctor.fix.manual": "machine",
6578
+ "doctor.report.written": "machine",
6496
6579
  "hook.failed_closed": "machine",
6497
6580
  "hook.failed_open": "machine",
6498
6581
  "exit.ok": "machine",
@@ -6552,6 +6635,8 @@ var review_default = {
6552
6635
  "install.plan.title": "machine",
6553
6636
  "install.warning": "machine",
6554
6637
  "install.warn.not_git": "machine",
6638
+ "install.warn.wsl_mount": "machine",
6639
+ "install.warn.synced_folder": "machine",
6555
6640
  "install.warn.fail_open": "machine",
6556
6641
  "install.warn.authority": "machine",
6557
6642
  "install.warn.plugin": "machine",
@@ -6840,43 +6925,8 @@ var review_default = {
6840
6925
  "doctor.memory.lines": "machine",
6841
6926
  "doctor.memory.too_long": "machine",
6842
6927
  "doctor.memory.fix_too_long": "machine",
6843
- "doctor.hooks.ok": "machine",
6844
- "doctor.hooks.no_command": "machine",
6845
- "doctor.hooks.not_found": "machine",
6846
- "doctor.hooks.timeout": "machine",
6847
- "doctor.hooks.exit": "machine",
6848
- "doctor.hooks.output": "machine",
6849
- "doctor.hooks.fix_broken": "machine",
6850
- "doctor.hooks.fix_broken_plugin": "machine",
6851
- "doctor.hooks.slow": "machine",
6852
- "doctor.hooks.fix_slow": "machine",
6853
- "doctor.hooks.fail_open": "machine",
6854
- "doctor.hooks.fix_fail_open": "machine",
6855
- "doctor.hooks.matcher": "machine",
6856
- "doctor.hooks.fix_matcher": "machine",
6857
- "doctor.hooks.not_fired": "machine",
6858
- "doctor.hooks.fix_not_fired": "machine",
6859
- "doctor.hooks.schema": "machine",
6860
- "doctor.hooks.fix_schema": "machine",
6861
- "doctor.hooks.plugin_disabled": "machine",
6862
- "doctor.hooks.fix_plugin_disabled": "machine",
6863
- "doctor.hooks.none": "machine",
6864
- "doctor.hooks.fix_none": "machine",
6865
- "doctor.hooks.absent": "machine",
6866
- "doctor.hooks.fix_absent": "machine",
6867
- "doctor.hooks.version_ok": "machine",
6868
- "doctor.hooks.version_plugin": "machine",
6869
- "doctor.hooks.fix_version_plugin": "machine",
6870
- "doctor.hooks.version_cli": "machine",
6871
- "doctor.hooks.fix_version_cli": "machine",
6872
- "doctor.hooks.fix_version_cli_plugin": "machine",
6873
- "doctor.fix.not_installed": "machine",
6874
- "doctor.fix.nothing": "machine",
6875
- "doctor.fix.would": "machine",
6876
- "doctor.fix.applied": "machine",
6877
- "doctor.fix.backup": "machine",
6878
- "doctor.fix.manual": "machine",
6879
- "doctor.report.written": "machine"
6928
+ "doctor.git.autocrlf": "machine",
6929
+ "doctor.git.fix_autocrlf": "machine"
6880
6930
  },
6881
6931
  es: {
6882
6932
  "command.init.summary": "machine",
@@ -6991,6 +7041,43 @@ var review_default = {
6991
7041
  "doctor.project.fix_lockfiles": "machine",
6992
7042
  "doctor.project.configs": "machine",
6993
7043
  "doctor.project.fix_configs": "machine",
7044
+ "doctor.hooks.ok": "machine",
7045
+ "doctor.hooks.no_command": "machine",
7046
+ "doctor.hooks.not_found": "machine",
7047
+ "doctor.hooks.timeout": "machine",
7048
+ "doctor.hooks.exit": "machine",
7049
+ "doctor.hooks.output": "machine",
7050
+ "doctor.hooks.fix_broken": "machine",
7051
+ "doctor.hooks.fix_broken_plugin": "machine",
7052
+ "doctor.hooks.slow": "machine",
7053
+ "doctor.hooks.fix_slow": "machine",
7054
+ "doctor.hooks.fail_open": "machine",
7055
+ "doctor.hooks.fix_fail_open": "machine",
7056
+ "doctor.hooks.matcher": "machine",
7057
+ "doctor.hooks.fix_matcher": "machine",
7058
+ "doctor.hooks.not_fired": "machine",
7059
+ "doctor.hooks.fix_not_fired": "machine",
7060
+ "doctor.hooks.schema": "machine",
7061
+ "doctor.hooks.fix_schema": "machine",
7062
+ "doctor.hooks.plugin_disabled": "machine",
7063
+ "doctor.hooks.fix_plugin_disabled": "machine",
7064
+ "doctor.hooks.none": "machine",
7065
+ "doctor.hooks.fix_none": "machine",
7066
+ "doctor.hooks.absent": "machine",
7067
+ "doctor.hooks.fix_absent": "machine",
7068
+ "doctor.hooks.version_ok": "machine",
7069
+ "doctor.hooks.version_plugin": "machine",
7070
+ "doctor.hooks.fix_version_plugin": "machine",
7071
+ "doctor.hooks.version_cli": "machine",
7072
+ "doctor.hooks.fix_version_cli": "machine",
7073
+ "doctor.hooks.fix_version_cli_plugin": "machine",
7074
+ "doctor.fix.not_installed": "machine",
7075
+ "doctor.fix.nothing": "machine",
7076
+ "doctor.fix.would": "machine",
7077
+ "doctor.fix.applied": "machine",
7078
+ "doctor.fix.backup": "machine",
7079
+ "doctor.fix.manual": "machine",
7080
+ "doctor.report.written": "machine",
6994
7081
  "hook.failed_closed": "machine",
6995
7082
  "hook.failed_open": "machine",
6996
7083
  "exit.ok": "machine",
@@ -7050,6 +7137,8 @@ var review_default = {
7050
7137
  "install.plan.title": "machine",
7051
7138
  "install.warning": "machine",
7052
7139
  "install.warn.not_git": "machine",
7140
+ "install.warn.wsl_mount": "machine",
7141
+ "install.warn.synced_folder": "machine",
7053
7142
  "install.warn.fail_open": "machine",
7054
7143
  "install.warn.authority": "machine",
7055
7144
  "install.warn.plugin": "machine",
@@ -7338,43 +7427,8 @@ var review_default = {
7338
7427
  "doctor.memory.lines": "machine",
7339
7428
  "doctor.memory.too_long": "machine",
7340
7429
  "doctor.memory.fix_too_long": "machine",
7341
- "doctor.hooks.ok": "machine",
7342
- "doctor.hooks.no_command": "machine",
7343
- "doctor.hooks.not_found": "machine",
7344
- "doctor.hooks.timeout": "machine",
7345
- "doctor.hooks.exit": "machine",
7346
- "doctor.hooks.output": "machine",
7347
- "doctor.hooks.fix_broken": "machine",
7348
- "doctor.hooks.fix_broken_plugin": "machine",
7349
- "doctor.hooks.slow": "machine",
7350
- "doctor.hooks.fix_slow": "machine",
7351
- "doctor.hooks.fail_open": "machine",
7352
- "doctor.hooks.fix_fail_open": "machine",
7353
- "doctor.hooks.matcher": "machine",
7354
- "doctor.hooks.fix_matcher": "machine",
7355
- "doctor.hooks.not_fired": "machine",
7356
- "doctor.hooks.fix_not_fired": "machine",
7357
- "doctor.hooks.schema": "machine",
7358
- "doctor.hooks.fix_schema": "machine",
7359
- "doctor.hooks.plugin_disabled": "machine",
7360
- "doctor.hooks.fix_plugin_disabled": "machine",
7361
- "doctor.hooks.none": "machine",
7362
- "doctor.hooks.fix_none": "machine",
7363
- "doctor.hooks.absent": "machine",
7364
- "doctor.hooks.fix_absent": "machine",
7365
- "doctor.hooks.version_ok": "machine",
7366
- "doctor.hooks.version_plugin": "machine",
7367
- "doctor.hooks.fix_version_plugin": "machine",
7368
- "doctor.hooks.version_cli": "machine",
7369
- "doctor.hooks.fix_version_cli": "machine",
7370
- "doctor.hooks.fix_version_cli_plugin": "machine",
7371
- "doctor.fix.not_installed": "machine",
7372
- "doctor.fix.nothing": "machine",
7373
- "doctor.fix.would": "machine",
7374
- "doctor.fix.applied": "machine",
7375
- "doctor.fix.backup": "machine",
7376
- "doctor.fix.manual": "machine",
7377
- "doctor.report.written": "machine"
7430
+ "doctor.git.autocrlf": "machine",
7431
+ "doctor.git.fix_autocrlf": "machine"
7378
7432
  }
7379
7433
  };
7380
7434
 
@@ -10952,11 +11006,11 @@ var withAlias = (invocation, expansion) => ({
10952
11006
 
10953
11007
  // src/git/path-key.ts
10954
11008
  import { posix as posix2 } from "node:path";
10955
- var WSL_MOUNT2 = /^\/mnt\/([a-zA-Z])(\/|$)/;
11009
+ var WSL_MOUNT3 = /^\/mnt\/([a-zA-Z])(\/|$)/;
10956
11010
  var DRIVE = /^([a-zA-Z]):(\/|$)/;
10957
11011
  var pathKey = (path) => {
10958
11012
  const posixPath = toPosixPath(path).replace(
10959
- WSL_MOUNT2,
11013
+ WSL_MOUNT3,
10960
11014
  (_match, letter, slash) => `${letter}:${slash || "/"}`
10961
11015
  );
10962
11016
  const drive = DRIVE.exec(posixPath);
@@ -13185,8 +13239,21 @@ var checkInstructions = (env) => {
13185
13239
  ];
13186
13240
  };
13187
13241
 
13188
- // src/doctor/memory.ts
13242
+ // src/doctor/line-endings.ts
13189
13243
  import { join as join30 } from "node:path";
13244
+ var LF_RULE = /^\*\s.*\beol=lf\b/m;
13245
+ var checkLineEndings = ({ root, runtime }) => {
13246
+ const autocrlf = runtime.git(["config", "--get", "core.autocrlf"], root);
13247
+ if (!autocrlf.ok || autocrlf.stdout.trim().toLowerCase() !== "true") return [];
13248
+ const attributes = runtime.readText(toAbsolutePath(join30(root, ".gitattributes")));
13249
+ if (attributes.ok && LF_RULE.test(attributes.value)) return [];
13250
+ return [
13251
+ finding("git.autocrlf", "warn", "doctor.git.autocrlf", { file: ".gitattributes" }, "doctor.git.fix_autocrlf")
13252
+ ];
13253
+ };
13254
+
13255
+ // src/doctor/memory.ts
13256
+ import { join as join31 } from "node:path";
13190
13257
  var ceilingOf = (config) => {
13191
13258
  const value = configAt(parseConfigYaml(config ?? ""), "memory", "maxLines");
13192
13259
  const parsed = typeof value === "string" ? Number.parseInt(value, 10) : Number.NaN;
@@ -13194,9 +13261,9 @@ var ceilingOf = (config) => {
13194
13261
  };
13195
13262
  var lineCount2 = (text6) => text6.replace(/\r?\n$/, "").split(/\r?\n/).length;
13196
13263
  var checkAgentsFile = ({ root, runtime }) => {
13197
- const text6 = runtime.readText(toAbsolutePath(join30(root, AGENTS_FILE)));
13264
+ const text6 = runtime.readText(toAbsolutePath(join31(root, AGENTS_FILE)));
13198
13265
  if (!text6.ok) return [];
13199
- const config = runtime.readText(toAbsolutePath(join30(root, CONFIG_FILE)));
13266
+ const config = runtime.readText(toAbsolutePath(join31(root, CONFIG_FILE)));
13200
13267
  const max = ceilingOf(config.ok ? config.value : void 0);
13201
13268
  const lines3 = lineCount2(text6.value);
13202
13269
  const params2 = { file: AGENTS_FILE, lines: lines3, max };
@@ -13204,7 +13271,7 @@ var checkAgentsFile = ({ root, runtime }) => {
13204
13271
  };
13205
13272
 
13206
13273
  // src/doctor/project-files.ts
13207
- import { join as join31 } from "node:path";
13274
+ import { join as join32 } from "node:path";
13208
13275
  var variants = (base, extensions) => extensions.map((extension) => `${base}${extension}`);
13209
13276
  var SCRIPT = [".js", ".cjs", ".mjs", ".ts", ".cts", ".mts"];
13210
13277
  var DATA = ["", ".json", ".yml", ".yaml"];
@@ -13235,7 +13302,7 @@ var CONFIG_GROUPS = [
13235
13302
  files: [...variants("commitlint.config", SCRIPT), ...variants(".commitlintrc", [...DATA, ".js"])]
13236
13303
  }
13237
13304
  ];
13238
- var present = (env, files) => files.filter((file) => env.runtime.stat(toAbsolutePath(join31(env.root, file)))?.kind === "file");
13305
+ var present = (env, files) => files.filter((file) => env.runtime.stat(toAbsolutePath(join32(env.root, file)))?.kind === "file");
13239
13306
  var crowded = (env, groups, build) => groups.flatMap(({ name, files }) => {
13240
13307
  const found = present(env, files);
13241
13308
  return found.length > 1 ? [build(name, found.join(", "))] : [];
@@ -13273,6 +13340,7 @@ var CHECKS = [
13273
13340
  checkClaudeSettings,
13274
13341
  checkInstructions,
13275
13342
  checkProjectFiles,
13343
+ checkLineEndings,
13276
13344
  checkAgentsFile,
13277
13345
  checkHooks
13278
13346
  ];
@@ -13800,7 +13868,7 @@ var rescueCommand = {
13800
13868
  };
13801
13869
 
13802
13870
  // src/git/undo.ts
13803
- import { basename as basename3, dirname as dirname8, join as join32 } from "node:path";
13871
+ import { basename as basename3, dirname as dirname8, join as join33 } from "node:path";
13804
13872
  var INDEX_SUBJECT = "finitemonkey index:";
13805
13873
  var gitLine = (runtime, repo, args) => {
13806
13874
  const result = runtime.git(args, repo.root);
@@ -13843,7 +13911,7 @@ var findSnapshot = (views, wanted) => {
13843
13911
  };
13844
13912
  var stampOf2 = (iso) => iso.replace(/[:.]/g, "-");
13845
13913
  var worktreePath = (runtime, repo) => {
13846
- const base = join32(dirname8(repo.root), `${basename3(repo.root)}-undo-${stampOf2(runtime.now())}`);
13914
+ const base = join33(dirname8(repo.root), `${basename3(repo.root)}-undo-${stampOf2(runtime.now())}`);
13847
13915
  return toAbsolutePath(runtime.stat(toAbsolutePath(base)) === void 0 ? base : `${base}-2`);
13848
13916
  };
13849
13917
  var intoWorktree = (runtime, repo, view2) => {
@@ -14032,13 +14100,13 @@ var helpCommand = {
14032
14100
  };
14033
14101
 
14034
14102
  // src/install/plugin-yield.ts
14035
- import { join as join33 } from "node:path";
14103
+ import { join as join34 } from "node:path";
14036
14104
  var PLUGIN_VIA = "plugin";
14037
14105
  var YIELD_STDOUT = `${JSON.stringify({ continue: true })}
14038
14106
  `;
14039
14107
  var projectOwnsHooks = (ctx) => {
14040
14108
  const root = ctx.env["CLAUDE_PROJECT_DIR"] ?? ctx.cwd;
14041
- const text6 = ctx.runtime.readText(toAbsolutePath(join33(root, ...SETTINGS_FILE.split("/"))));
14109
+ const text6 = ctx.runtime.readText(toAbsolutePath(join34(root, ...SETTINGS_FILE.split("/"))));
14042
14110
  if (!text6.ok) return false;
14043
14111
  const parsed = parseJsonObject(text6.value);
14044
14112
  return parsed.ok && ownHandlersIn(parsed.value) > 0;
@@ -14091,7 +14159,7 @@ var hookCommand = {
14091
14159
  };
14092
14160
 
14093
14161
  // src/commands/i18n-check.ts
14094
- import { join as join34 } from "node:path";
14162
+ import { join as join35 } from "node:path";
14095
14163
 
14096
14164
  // src/i18n/message.ts
14097
14165
  var ARG = /^\s*([a-zA-Z][a-zA-Z0-9_]*)\s*(?:,\s*(plural|select)\s*,)?/;
@@ -14293,7 +14361,7 @@ var unreadable = (dir, file, detail) => ({
14293
14361
  exitCode: ExitCode.NotConfigured
14294
14362
  });
14295
14363
  var readCatalog = (ctx, dir, lang) => {
14296
- const file = toAbsolutePath(join34(dir, `${lang}.json`));
14364
+ const file = toAbsolutePath(join35(dir, `${lang}.json`));
14297
14365
  const text6 = ctx.runtime.readText(file);
14298
14366
  if (!text6.ok) return reject(unreadable(dir, file, text6.error));
14299
14367
  const parsed = parseJsonObject(text6.value);
@@ -14316,7 +14384,7 @@ var describeProblem = (ctx, problem4) => {
14316
14384
  return ` ${problem4.lang} ${problem4.key} ${kind}`;
14317
14385
  };
14318
14386
  var loadReview = (ctx, dir) => {
14319
- const text6 = ctx.runtime.readText(toAbsolutePath(join34(dir, "review.json")));
14387
+ const text6 = ctx.runtime.readText(toAbsolutePath(join35(dir, "review.json")));
14320
14388
  const parsed = text6.ok ? parseJsonObject(text6.value) : void 0;
14321
14389
  if (parsed?.ok !== true) return void 0;
14322
14390
  const statuses = (lang) => {
@@ -14429,6 +14497,8 @@ var itemWarning = (t, item) => {
14429
14497
  var planWarnings = (t, plan) => [
14430
14498
  ...plan.detection.gitRoot ? [] : [{ code: "not_git", message: t("install.warn.not_git") }],
14431
14499
  ...plan.detection.claude.failClosed ? [] : [{ code: "fail_open", message: t("install.warn.fail_open", { version: plan.detection.claude.version ?? "?" }) }],
14500
+ ...plan.detection.location.wslMount ? [{ code: "wsl_mount", message: t("install.warn.wsl_mount", { path: plan.detection.root }) }] : [],
14501
+ ...plan.detection.location.synced ? [{ code: "synced_folder", message: t("install.warn.synced_folder", { path: plan.detection.root }) }] : [],
14432
14502
  ...plan.detection.authorities.map((authority) => authorityWarning(t, authority)),
14433
14503
  ...plan.items.flatMap((item) => itemWarning(t, item))
14434
14504
  ];
@@ -14996,7 +15066,7 @@ var secretsScanCommand = {
14996
15066
  };
14997
15067
 
14998
15068
  // src/task/create.ts
14999
- import { join as join35 } from "node:path";
15069
+ import { join as join36 } from "node:path";
15000
15070
 
15001
15071
  // src/core/yaml/write.ts
15002
15072
  var PLAIN_SAFE = /^[A-Za-z_./][A-Za-z0-9_./ ()-]*$/;
@@ -15080,10 +15150,10 @@ var explicitChecks = (draft) => [
15080
15150
  var draftChecks = (runtime, paths, draft) => {
15081
15151
  const disk = {
15082
15152
  read: (relative9) => {
15083
- const text6 = runtime.readText(toAbsolutePath(join35(paths.root, relative9)));
15153
+ const text6 = runtime.readText(toAbsolutePath(join36(paths.root, relative9)));
15084
15154
  return text6.ok ? text6.value : void 0;
15085
15155
  },
15086
- exists: (relative9) => runtime.stat(toAbsolutePath(join35(paths.root, relative9))) !== void 0
15156
+ exists: (relative9) => runtime.stat(toAbsolutePath(join36(paths.root, relative9))) !== void 0
15087
15157
  };
15088
15158
  const detected = detectChecks(disk);
15089
15159
  const base = draft.runs.length > 0 || draft.tests !== void 0 ? explicitChecks(draft) : detected;
@@ -15487,7 +15557,7 @@ var REGISTRY = [
15487
15557
  ];
15488
15558
 
15489
15559
  // src/i18n/lang-settings.ts
15490
- import { join as join36 } from "node:path";
15560
+ import { join as join37 } from "node:path";
15491
15561
  var read3 = (runtime, path) => {
15492
15562
  const text6 = runtime.readText(toAbsolutePath(path));
15493
15563
  return text6.ok ? text6.value : void 0;
@@ -15502,14 +15572,14 @@ var languageIn = (text6) => {
15502
15572
  const value = parsed.ok && isRecord(parsed.value) ? parsed.value["language"] : void 0;
15503
15573
  return typeof value === "string" && value.trim() !== "" ? value : void 0;
15504
15574
  };
15505
- var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ?? join36(runtime.homeDir(), ".claude");
15575
+ var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ?? join37(runtime.homeDir(), ".claude");
15506
15576
  var langSettings = (runtime, cwd, env, platform) => {
15507
15577
  const root = findGitRoot(cwd, runtime) ?? cwd;
15508
- const config = parseConfigYaml(read3(runtime, join36(root, CONFIG_FILE)) ?? "");
15578
+ const config = parseConfigYaml(read3(runtime, join37(root, CONFIG_FILE)) ?? "");
15509
15579
  const claudeFiles = [
15510
- join36(root, ".claude", "settings.local.json"),
15511
- join36(root, ".claude", "settings.json"),
15512
- join36(claudeDir(runtime, env), "settings.json")
15580
+ join37(root, ".claude", "settings.local.json"),
15581
+ join37(root, ".claude", "settings.json"),
15582
+ join37(claudeDir(runtime, env), "settings.json")
15513
15583
  ];
15514
15584
  return {
15515
15585
  config: configText(config, "lang"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "finitemonkey",
3
- "version": "0.1.0-next.11",
3
+ "version": "0.1.0-next.12",
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",
@@ -55,7 +55,7 @@
55
55
  "i18n:check": "node dist/finitemonkey.mjs i18n check --dir src/i18n",
56
56
  "bench:startup": "node scripts/bench-startup.mjs",
57
57
  "smoke:install": "node scripts/smoke-install.mjs",
58
- "check": "npm run typecheck && npm run lint && npm run format:check && npm run check:dist && npm test && npm run i18n:check && npm run plugin:validate && npm pack --dry-run && npm run llms:check && npm run schema:check",
58
+ "check": "npm run typecheck && npm run lint && npm run format:check && npm run check:dist && npm test && npm run i18n:check && npm run plugin:validate && npm pack --dry-run && npm run llms:check && npm run schema:check && npm run docs:check",
59
59
  "prepublishOnly": "npm run typecheck && npm run check:dist && npm test",
60
60
  "prepare": "husky",
61
61
  "gif": "tsc -p assets/scripts && node --experimental-strip-types assets/scripts/render.ts",
@@ -66,6 +66,8 @@
66
66
  "schema": "tsx scripts/build-schemas.ts",
67
67
  "schema:check": "tsx scripts/build-schemas.ts --check",
68
68
  "budgets": "tsx scripts/budgets.ts",
69
+ "docs": "tsx scripts/build-docs.ts",
70
+ "docs:check": "tsc -p scripts && tsx scripts/build-docs.ts --check",
69
71
  "plugin:sync": "node scripts/plugin-sync.mjs",
70
72
  "plugin:validate": "node scripts/plugin-validate.mjs",
71
73
  "version": "node scripts/plugin-sync.mjs"