@fusengine/harness 0.1.79 → 0.1.81

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.
Files changed (76) hide show
  1. package/README.md +14 -1
  2. package/dist/adapters/claude/index.d.mts +1 -1
  3. package/dist/adapters/claude/index.mjs +1 -1
  4. package/dist/adapters/cline/index.mjs +1 -1
  5. package/dist/adapters/codex/index.d.mts +11 -12
  6. package/dist/adapters/codex/index.mjs +3 -3
  7. package/dist/adapters/cursor/index.d.mts +29 -29
  8. package/dist/adapters/cursor/index.mjs +1 -1
  9. package/dist/adapters/gemini/index.mjs +1 -1
  10. package/dist/adapters/hermes/index.d.mts +22 -23
  11. package/dist/adapters/hermes/index.mjs +1 -1
  12. package/dist/adapters/kimi/index.d.mts +87 -0
  13. package/dist/adapters/kimi/index.mjs +2 -0
  14. package/dist/cache/index.d.mts +1 -1
  15. package/dist/{claude-51RSspJ1.mjs → claude-D93HY8dq.mjs} +2 -2
  16. package/dist/cli/bin.d.mts +1 -1
  17. package/dist/cli/bin.mjs +130 -39
  18. package/dist/cli/index.d.mts +3 -3
  19. package/dist/cli/index.mjs +1 -1
  20. package/dist/config/index.d.mts +2 -2
  21. package/dist/config/index.mjs +4 -4
  22. package/dist/detect/index.d.mts +2 -1
  23. package/dist/detect/index.mjs +1 -1
  24. package/dist/{doc-helpers-CEKzGg2u.d.mts → doc-helpers-BGCzP9VF.d.mts} +12 -12
  25. package/dist/{dotenv-Jj8aL1FL.mjs → dotenv-DoqNlALG.mjs} +31 -3
  26. package/dist/{evaluate-ClfbCDiY.mjs → evaluate-B4-MhOy_.mjs} +322 -32
  27. package/dist/freshness/index.d.mts +2 -2
  28. package/dist/{handle-Dthtlp7Z.mjs → handle-J5r2f_O_.mjs} +665 -164
  29. package/dist/{harness-Cb9xR8dC.mjs → harness-BMuLJ9lm.mjs} +4 -2
  30. package/dist/{hermes-rTmVdhUL.mjs → hermes-1PUEoY8q.mjs} +2 -2
  31. package/dist/{home-state-BXf38Zi1.mjs → home-state-oUGFB4ds.mjs} +4 -2
  32. package/dist/index-84heAAm_.d.mts +12 -0
  33. package/dist/index-CaxrMD1A.d.mts +1 -0
  34. package/dist/index-CigBu4K9.d.mts +14 -0
  35. package/dist/{index-DGxNjXW7.d.mts → index-D4C2-IIc.d.mts} +6 -8
  36. package/dist/index-DBgnIZn9.d.mts +756 -0
  37. package/dist/{index-mmRF3KNp.d.mts → index-DEfz82BP.d.mts} +12 -13
  38. package/dist/{index-BUwEmIK-.d.mts → index-DhmV2MPh.d.mts} +31 -31
  39. package/dist/index-oXXPs2xl.d.mts +119 -0
  40. package/dist/index-p9fLrSOD.d.mts +27 -0
  41. package/dist/index.d.mts +11 -11
  42. package/dist/index.mjs +9 -9
  43. package/dist/init/index.d.mts +8 -9
  44. package/dist/init/index.mjs +1 -1
  45. package/dist/kimi-ChbcU2uI.mjs +94 -0
  46. package/dist/{layout-C0jaaCQC.mjs → layout-KWoE_Mqn.mjs} +1 -0
  47. package/dist/limits-DG5pl-5s.mjs +59 -0
  48. package/dist/memory/index.mjs +1 -1
  49. package/dist/policy/index.d.mts +2 -3
  50. package/dist/policy/index.mjs +4 -4
  51. package/dist/prompt/index.d.mts +2 -1
  52. package/dist/refs/index.d.mts +1 -1
  53. package/dist/{registry-CymilZiZ.mjs → registry-IhHk2KlT.mjs} +1 -1
  54. package/dist/{run-3tyTW9Ud.mjs → run-D_WchZ3R.mjs} +1 -1
  55. package/dist/{run-Do2JltgU.mjs → run-TAXNRSpD.mjs} +1 -1
  56. package/dist/runtime/index.d.mts +455 -446
  57. package/dist/runtime/index.mjs +3 -3
  58. package/dist/{session-state-CY4iohn_.d.mts → session-state-D5gLr66m.d.mts} +29 -30
  59. package/dist/{skill-path-BO2N0XvB.mjs → skill-path-DhBGlQe3.mjs} +3 -3
  60. package/dist/{store-Bcn08y9m.mjs → store-yr_QFsZY.mjs} +101 -13
  61. package/dist/tracking/index.d.mts +22 -13
  62. package/dist/tracking/index.mjs +2 -2
  63. package/dist/{types-DVbIl9md.d.mts → types-C0-igRKr.d.mts} +8 -8
  64. package/dist/types-DvXTI71X.d.mts +15 -0
  65. package/dist/util/index.d.mts +1 -1
  66. package/dist/{validate-DhOX5hDK.mjs → validate-Bf-EsbDh.mjs} +348 -32
  67. package/package.json +163 -158
  68. package/dist/apex-Wdi1nq_w.d.mts +0 -73
  69. package/dist/harness-s1Fce_dL.d.mts +0 -27
  70. package/dist/index-BEMumjOw.d.mts +0 -27
  71. package/dist/index-BIGVNQB8.d.mts +0 -576
  72. package/dist/index-BKZ67WMa.d.mts +0 -1
  73. package/dist/index-BOBXQ91y.d.mts +0 -12
  74. package/dist/index-BVVgDSdq.d.mts +0 -1
  75. package/dist/index-jj_EzZRv.d.mts +0 -96
  76. package/dist/limits-CHn8AIL1.mjs +0 -31
package/README.md CHANGED
@@ -130,6 +130,7 @@ also works elsewhere.
130
130
  | **gemini-cli** | `BeforeTool` denies via `{decision:"deny",reason}` (`gemini/index.ts:22-36`) | none | Thin stateless adapter — no session track, no APEX gates wired through it. |
131
131
  | **cline** | `PreToolUse` only; block → `{cancel:true}`, non-block → `contextModification` (`cline/index.ts:24-36`) | none | Same as gemini-cli: stateless guard only. |
132
132
  | **hermes** | `pre_tool_call` proven: reuses the Claude stdin reader, blocks via `{decision:"block",reason}` (`hermes/index.ts:12-36`) | untested — no lifecycle dispatch wired for Hermes in this repo | `ask`/`inform` degrade to non-blocking `{context}` — Hermes "has no interactive ask state" (`hermes/index.ts:27-28`). |
133
+ | **kimi** | `PreToolUse` denies via the camelCase JSON channel `{"hookSpecificOutput":{"permissionDecision":"deny","permissionDecisionReason":"…"}}` on stdout at **exit 0** (verified live against kimi-code v0.27.0 — exit 2 with stderr = reason also blocks, but is not required) — only `deny` is documented. `ask` is **downgraded to deny** prefixed `[downgraded from ask — Kimi Code has no interactive approval]`; `inform` rides plain stdout text at exit 0, never wrapped in JSON. Blocking events: `UserPromptSubmit`, `PreToolUse`, `Stop` (`kimi/index.ts`). | Observation only: `PostToolUse`, `PostToolUseFailure`, `PermissionRequest`, `PermissionResult`, `SessionStart`, `SessionEnd`, `SubagentStart`, `SubagentStop`, `StopFailure`, `Interrupt`, `PreCompact`, `PostCompact`, `Notification` — Kimi delivers them but **ignores any response**, so no verdict can be returned from them. | A **hook** cannot request approval — Kimi's `ask` lives in a parallel, hook-unreachable system (`[[permission.rules]] decision = "ask"` in `config.toml`), hence the ask→deny downgrade. Hooks are configured **only** in the global `~/.kimi-code/config.toml` (no project-local hooks file), so `harness init` writes no kimi wiring — copy the TOML snippet from [docs/adapters.md](docs/adapters.md#kimi-code--manual-wiring), and emit **no field** beyond `event`/`matcher`/`command`/`timeout` or the whole config fails to load. **Fail-open by design**: any exit code other than 0/2, a timeout, or a crash lets the call through. Stdin payload is snake_case and carries only `hook_event_name`, `session_id`, `cwd`, `tool_name`, `tool_input.command` and an undocumented `tool_call_id` (unused here) — no `transcript_path`, no `permission_mode`, no `tool_response`. Verified live against kimi-code v0.27.0 for `PreToolUse`/`Bash`; validate hand-written config with `kimi doctor`. Instructions file is `AGENTS.md`, not `CLAUDE.md`. |
133
134
 
134
135
  ## What it enforces
135
136
 
@@ -191,6 +192,14 @@ Features shipped since 0.1.44, each with its own test:
191
192
  version + drift vs. npm, `.claude/BOARD.md`, and the one-shot summary, each
192
193
  collector isolated so one failure can't blank the rest
193
194
  (`src/runtime/lifecycle/snapshot/index.ts`, `test/snapshot.test.ts`).
195
+ - **Target-aware root-doc injection** — `SessionStart` and `UserPromptSubmit`
196
+ inject the target's own root instructions doc, not a hardcoded `CLAUDE.md`:
197
+ `apexDocName(id)` + `harnessHomeSegment(id)` (`src/policy/apex-target.ts`)
198
+ resolve `AGENTS.md` under `.codex`/`.kimi-code` for Codex/Kimi, `CLAUDE.md`
199
+ under `.claude` for every other target (byte-identical to the pre-target-aware
200
+ default). The `systemMessage` label follows suit (`"AGENTS.md injected"`
201
+ under Codex/Kimi) (`src/runtime/lifecycle/session-start.ts`,
202
+ `src/runtime/inject-context.ts`).
194
203
  - **Injection budget cap** — harness-produced context fragments (lessons,
195
204
  snapshot, APEX task context) are capped at ~8000 chars each; owner-authored
196
205
  content (CLAUDE.md/rules) is never capped
@@ -202,7 +211,11 @@ Features shipped since 0.1.44, each with its own test:
202
211
  carrying `tool_input.agent_type` is recorded as `subagent-<agent_type>` in the
203
212
  same SessionTrack as Claude Task evidence, so a Codex explore-codebase/sniper
204
213
  satisfies the APEX freshness gates exactly like a Claude subagent
205
- (`src/tracking/session-state.ts`, sim scenario 32b).
214
+ (`src/tracking/session-state.ts`, sim scenario 32b). Recognized
215
+ agent-launcher tools across targets: `Task` (Claude), `Agent`/`AgentSwarm`
216
+ (Kimi Code) — each credits its `subagent_type`/`agent_type` toward the same
217
+ freshness evidence (`src/runtime/activity.ts`,
218
+ `src/freshness/agent-evidence-record.ts`).
206
219
  - **File-size gate judges the real Edit outcome, not the on-disk count** — an
207
220
  Edit on a file already over `FUSE_SOLID_MAX_LINES` used to be denied
208
221
  unconditionally (the on-disk count always won); the gate now computes the
@@ -1,2 +1,2 @@
1
- import { a as denyResponse, c as informResponse, d as toClaudeResponse, i as contextResponse, l as readClaudeInput, n as attachSystemMessage, o as fileSizeGuard, r as blockResponse, s as guard, t as ClaudeHookInput, u as systemMessage } from "../../index-DGxNjXW7.mjs";
1
+ import { a as denyResponse, c as informResponse, d as toClaudeResponse, i as contextResponse, l as readClaudeInput, n as attachSystemMessage, o as fileSizeGuard, r as blockResponse, s as guard, t as ClaudeHookInput, u as systemMessage } from "../../index-D4C2-IIc.mjs";
2
2
  export { ClaudeHookInput, attachSystemMessage, blockResponse, contextResponse, denyResponse, fileSizeGuard, guard, informResponse, readClaudeInput, systemMessage, toClaudeResponse };
@@ -1,2 +1,2 @@
1
- import { a as fileSizeGuard, c as readClaudeInput, i as denyResponse, l as systemMessage, n as blockResponse, o as guard, r as contextResponse, s as informResponse, t as attachSystemMessage, u as toClaudeResponse } from "../../claude-51RSspJ1.mjs";
1
+ import { a as fileSizeGuard, c as readClaudeInput, i as denyResponse, l as systemMessage, n as blockResponse, o as guard, r as contextResponse, s as informResponse, t as attachSystemMessage, u as toClaudeResponse } from "../../claude-D93HY8dq.mjs";
2
2
  export { attachSystemMessage, blockResponse, contextResponse, denyResponse, fileSizeGuard, guard, informResponse, readClaudeInput, systemMessage, toClaudeResponse };
@@ -1,4 +1,4 @@
1
- import { t as evaluate } from "../../evaluate-ClfbCDiY.mjs";
1
+ import { t as evaluate } from "../../evaluate-B4-MhOy_.mjs";
2
2
  import { t as formatPrompt } from "../../types-ernB1Dy3.mjs";
3
3
  //#region src/adapters/cline/index.ts
4
4
  /**
@@ -1,19 +1,18 @@
1
- import { t as Prompt } from "../../types-DVbIl9md.mjs";
2
- import { a as denyResponse, c as informResponse, i as contextResponse, l as readClaudeInput, t as ClaudeHookInput } from "../../index-DGxNjXW7.mjs";
3
-
1
+ import { t as Prompt } from "../../types-C0-igRKr.mjs";
2
+ import { a as denyResponse, c as informResponse, i as contextResponse, l as readClaudeInput, t as ClaudeHookInput } from "../../index-D4C2-IIc.mjs";
4
3
  //#region src/adapters/codex/index.d.ts
5
4
  /**
6
- * Render a portable {@link Prompt} as a Codex hook response, `ask` → explicit deny.
7
- * NOTE: the REAL wired route is `harness hook codex` → handleHook → respond.ts
8
- * (source of truth for the ask→deny downgrade); this thin export exists for
9
- * direct package consumers and is kept aligned so it never silently diverges.
10
- */
5
+ * Render a portable {@link Prompt} as a Codex hook response, `ask` → explicit deny.
6
+ * NOTE: the REAL wired route is `harness hook codex` → handleHook → respond.ts
7
+ * (source of truth for the ask→deny downgrade); this thin export exists for
8
+ * direct package consumers and is kept aligned so it never silently diverges.
9
+ */
11
10
  declare function toCodexResponse(prompt: Prompt): string;
12
11
  /**
13
- * Run the bundled policy over a Codex payload and return the native response
14
- * string (deny/additionalContext), or null to allow. `apply_patch` is fanned
15
- * into per-file checks; every other tool routes through the portable policy.
16
- */
12
+ * Run the bundled policy over a Codex payload and return the native response
13
+ * string (deny/additionalContext), or null to allow. `apply_patch` is fanned
14
+ * into per-file checks; every other tool routes through the portable policy.
15
+ */
17
16
  declare function guard(input: ClaudeHookInput): string | null;
18
17
  //#endregion
19
18
  export { type ClaudeHookInput as CodexHookInput, contextResponse, denyResponse, guard, informResponse, readClaudeInput as readCodexInput, toCodexResponse };
@@ -1,8 +1,8 @@
1
- import { t as evaluate } from "../../evaluate-ClfbCDiY.mjs";
2
- import { f as countLines } from "../../home-state-BXf38Zi1.mjs";
1
+ import { t as evaluate } from "../../evaluate-B4-MhOy_.mjs";
2
+ import { f as countLines } from "../../home-state-oUGFB4ds.mjs";
3
3
  import { t as formatPrompt } from "../../types-ernB1Dy3.mjs";
4
4
  import { n as parseApplyPatch, t as isBypassPermissions } from "../../permission-mode-BN3MNgbm.mjs";
5
- import { c as readClaudeInput, i as denyResponse, r as contextResponse, s as informResponse, v as commandToString } from "../../claude-51RSspJ1.mjs";
5
+ import { _ as commandToString, c as readClaudeInput, i as denyResponse, r as contextResponse, s as informResponse } from "../../claude-D93HY8dq.mjs";
6
6
  //#region src/adapters/codex/index.ts
7
7
  /**
8
8
  * OpenAI Codex CLI adapter (hook-mode). Codex's `PreToolUse` hook (since 2026)
@@ -15,24 +15,24 @@ interface CursorEditPayload {
15
15
  }[];
16
16
  }
17
17
  /**
18
- * `afterFileEdit` stdout response. Its schema (cursor.com/docs/hooks#afterFileEdit)
19
- * is DELIBERATELY narrower than the "before" hooks: `permission` + `user_message`
20
- * only — there is NO `agent_message` and NO `updated_input`. Since the edit is
21
- * already on disk when this "after" hook fires, `deny` cannot revert it and the
22
- * correction reaches only the HUMAN (`user_message`), never the model — so this
23
- * path is strictly ADVISORY, not an enforceable gate.
24
- */
18
+ * `afterFileEdit` stdout response. Its schema (cursor.com/docs/hooks#afterFileEdit)
19
+ * is DELIBERATELY narrower than the "before" hooks: `permission` + `user_message`
20
+ * only — there is NO `agent_message` and NO `updated_input`. Since the edit is
21
+ * already on disk when this "after" hook fires, `deny` cannot revert it and the
22
+ * correction reaches only the HUMAN (`user_message`), never the model — so this
23
+ * path is strictly ADVISORY, not an enforceable gate.
24
+ */
25
25
  interface CursorEditResponse {
26
26
  permission: "allow" | "deny";
27
27
  /** User-visible correction — snake_case (#141516); the only channel afterFileEdit exposes. */
28
28
  user_message?: string;
29
29
  }
30
30
  /**
31
- * `beforeShellExecution` stdout response. Message keys are snake_case:
32
- * Cursor silently ignores camelCase `userMessage`/`agentMessage` (#141516,
33
- * regression persists through v2.0.77+ — forum #142589), matching the
34
- * schema respond.ts emits for the cursor harness.
35
- */
31
+ * `beforeShellExecution` stdout response. Message keys are snake_case:
32
+ * Cursor silently ignores camelCase `userMessage`/`agentMessage` (#141516,
33
+ * regression persists through v2.0.77+ — forum #142589), matching the
34
+ * schema respond.ts emits for the cursor harness.
35
+ */
36
36
  interface CursorResponse {
37
37
  permission: "allow" | "deny" | "ask";
38
38
  continue?: boolean;
@@ -46,23 +46,23 @@ interface CursorResponse {
46
46
  /** Guard a shell command (git/install policies). */
47
47
  declare function beforeShellExecution(payload: CursorShellPayload): CursorResponse;
48
48
  /**
49
- * Advise on a file edit AFTER Cursor has written it — a HUMAN-VISIBLE audit note,
50
- * never a gate. This is an "after" hook: the edit is already on disk. On a
51
- * SOLID/DRY violation we surface the correction through `user_message` (the only
52
- * channel afterFileEdit exposes — no `agent_message`, so the model is never
53
- * re-informed) while ALWAYS returning `permission: "allow"`.
54
- *
55
- * We deliberately never emit `permission: "deny"` here, for two distinct reasons:
56
- * (1) structural — afterFileEdit was "informational only" at launch (Chacon,
57
- * Cursor hooks beta 1.7, 2025-09: no channel to stop the agent), and a post-write
58
- * deny has no documented rollback; (2) empirical — Cursor staff confirm the
59
- * deny-enforcement path is broken for file operations (forum.cursor.com/t/154377,
60
- * v2.6.18, 2026-03, open) — proven for file READS, plausibly the same for writes.
61
- * So a `deny` would be a false blocking signal; `allow` + `user_message` is the
62
- * only proven-safe shape.
63
- * @param payload - The `afterFileEdit` stdin payload.
64
- * @returns Always an allow; carries the user-visible correction on a violation.
65
- */
49
+ * Advise on a file edit AFTER Cursor has written it — a HUMAN-VISIBLE audit note,
50
+ * never a gate. This is an "after" hook: the edit is already on disk. On a
51
+ * SOLID/DRY violation we surface the correction through `user_message` (the only
52
+ * channel afterFileEdit exposes — no `agent_message`, so the model is never
53
+ * re-informed) while ALWAYS returning `permission: "allow"`.
54
+ *
55
+ * We deliberately never emit `permission: "deny"` here, for two distinct reasons:
56
+ * (1) structural — afterFileEdit was "informational only" at launch (Chacon,
57
+ * Cursor hooks beta 1.7, 2025-09: no channel to stop the agent), and a post-write
58
+ * deny has no documented rollback; (2) empirical — Cursor staff confirm the
59
+ * deny-enforcement path is broken for file operations (forum.cursor.com/t/154377,
60
+ * v2.6.18, 2026-03, open) — proven for file READS, plausibly the same for writes.
61
+ * So a `deny` would be a false blocking signal; `allow` + `user_message` is the
62
+ * only proven-safe shape.
63
+ * @param payload - The `afterFileEdit` stdin payload.
64
+ * @returns Always an allow; carries the user-visible correction on a violation.
65
+ */
66
66
  declare function afterFileEdit(payload: CursorEditPayload): CursorEditResponse;
67
67
  //#endregion
68
68
  export { type CursorEditPayload, type CursorEditResponse, type CursorResponse, type CursorShellPayload, afterFileEdit, beforeShellExecution };
@@ -1,4 +1,4 @@
1
- import { t as evaluate } from "../../evaluate-ClfbCDiY.mjs";
1
+ import { t as evaluate } from "../../evaluate-B4-MhOy_.mjs";
2
2
  import { t as formatPrompt } from "../../types-ernB1Dy3.mjs";
3
3
  //#region src/adapters/cursor/index.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as evaluate } from "../../evaluate-ClfbCDiY.mjs";
1
+ import { t as evaluate } from "../../evaluate-B4-MhOy_.mjs";
2
2
  import { t as formatPrompt } from "../../types-ernB1Dy3.mjs";
3
3
  //#region src/adapters/gemini/index.ts
4
4
  /**
@@ -1,12 +1,11 @@
1
- import { t as Prompt } from "../../types-DVbIl9md.mjs";
2
-
1
+ import { t as Prompt } from "../../types-C0-igRKr.mjs";
3
2
  //#region src/adapters/hermes/interfaces/types.d.ts
4
3
  /**
5
- * Hermes Agent (NousResearch/hermes-agent) shell-hook wire types. Verified 2026
6
- * against hermes-agent.nousresearch.com/docs/user-guide/features/hooks: hooks
7
- * are configured in `~/.hermes/config.yaml` under `hooks:`, receive a JSON
8
- * payload on stdin, and answer with JSON on stdout.
9
- */
4
+ * Hermes Agent (NousResearch/hermes-agent) shell-hook wire types. Verified 2026
5
+ * against hermes-agent.nousresearch.com/docs/user-guide/features/hooks: hooks
6
+ * are configured in `~/.hermes/config.yaml` under `hooks:`, receive a JSON
7
+ * payload on stdin, and answer with JSON on stdout.
8
+ */
10
9
  /** `pre_tool_call` stdin payload (subset). Same wire shape as Claude Code. */
11
10
  interface HermesHookInput {
12
11
  hook_event_name?: string;
@@ -21,10 +20,10 @@ interface HermesHookInput {
21
20
  cwd?: string;
22
21
  }
23
22
  /**
24
- * Hook stdout response. `{}` allows; `{ decision: "block", reason }` cancels
25
- * the tool call; `{ context }` injects LLM context (honored on `pre_llm_call`).
26
- * Hermes has NO Claude-style `permissionDecision` and no interactive "ask".
27
- */
23
+ * Hook stdout response. `{}` allows; `{ decision: "block", reason }` cancels
24
+ * the tool call; `{ context }` injects LLM context (honored on `pre_llm_call`).
25
+ * Hermes has NO Claude-style `permissionDecision` and no interactive "ask".
26
+ */
28
27
  interface HermesResponse {
29
28
  decision?: "block";
30
29
  reason?: string;
@@ -35,20 +34,20 @@ interface HermesResponse {
35
34
  /** Read & parse the Hermes hook stdin payload (same wire shape as Claude). */
36
35
  declare function readHermesInput(): Promise<HermesHookInput>;
37
36
  /**
38
- * Render a portable {@link Prompt} as a Hermes hook response string:
39
- * `block` -> `{decision:"block",reason}`; `ask`/`inform` -> non-blocking
40
- * `{context}` — Hermes has no interactive "ask" state, so both degrade to
41
- * context injection, mirroring the gemini-cli/cline routing in `respond()`.
42
- * @param prompt - The portable policy prompt.
43
- * @returns The native Hermes JSON response string.
44
- */
37
+ * Render a portable {@link Prompt} as a Hermes hook response string:
38
+ * `block` -> `{decision:"block",reason}`; `ask`/`inform` -> non-blocking
39
+ * `{context}` — Hermes has no interactive "ask" state, so both degrade to
40
+ * context injection, mirroring the gemini-cli/cline routing in `respond()`.
41
+ * @param prompt - The portable policy prompt.
42
+ * @returns The native Hermes JSON response string.
43
+ */
45
44
  declare function toHermesResponse(prompt: Prompt): string;
46
45
  /**
47
- * Run the bundled policy over a Hermes `pre_tool_call` payload and return the
48
- * native response string, or null to allow (the hook then prints `{}`).
49
- * @param input - Parsed `pre_tool_call` stdin payload.
50
- * @returns Native response JSON, or null when the tool use is allowed.
51
- */
46
+ * Run the bundled policy over a Hermes `pre_tool_call` payload and return the
47
+ * native response string, or null to allow (the hook then prints `{}`).
48
+ * @param input - Parsed `pre_tool_call` stdin payload.
49
+ * @returns Native response JSON, or null when the tool use is allowed.
50
+ */
52
51
  declare function guard(input: HermesHookInput): string | null;
53
52
  //#endregion
54
53
  export { type HermesHookInput, type HermesResponse, guard, readHermesInput, toHermesResponse };
@@ -1,2 +1,2 @@
1
- import { n as readHermesInput, r as toHermesResponse, t as guard } from "../../hermes-rTmVdhUL.mjs";
1
+ import { n as readHermesInput, r as toHermesResponse, t as guard } from "../../hermes-1PUEoY8q.mjs";
2
2
  export { guard, readHermesInput, toHermesResponse };
@@ -0,0 +1,87 @@
1
+ import { t as Prompt } from "../../types-C0-igRKr.mjs";
2
+ //#region src/adapters/kimi/interfaces/types.d.ts
3
+ /**
4
+ * Kimi Code CLI shell-hook wire types. Verified 2026 against
5
+ * kimi.com/code/docs/en/kimi-code-cli/customization/hooks.html (v0.27.0):
6
+ * hooks are configured in `~/.kimi-code/config.toml` under `[[hooks]]`,
7
+ * receive a snake_case JSON payload on stdin, and answer with a camelCase
8
+ * JSON envelope on stdout (a real, documented casing inconsistency).
9
+ */
10
+ /**
11
+ * `PreToolUse`/`UserPromptSubmit`/`Stop` stdin payload (subset). Same base
12
+ * wire shape as Claude Code (`hook_event_name`/`tool_name`/`tool_input`/
13
+ * `cwd`); `tool_input.command` is confirmed for `Bash` by the docs' example
14
+ * script (`payload.tool_input?.command`). Verified live against kimi-code
15
+ * v0.27.0 on `PreToolUse`/`Bash`: `tool_name` is present at top level (the
16
+ * docs never name it as a stdin key) and `tool_input.command` carries the
17
+ * shell string. The live payload also carries an UNDOCUMENTED `tool_call_id`
18
+ * (e.g. `"tool_LsTnl0gBxM62XuWvKw0GFGHs"`); the harness does not consume it,
19
+ * so it is deliberately absent from this interface. `cwd` is the
20
+ * symlink-resolved path (macOS: `/private/tmp/…` for a shell in `/tmp/…`).
21
+ */
22
+ interface KimiHookInput {
23
+ hook_event_name?: string;
24
+ tool_name?: string;
25
+ /**
26
+ * Verified against the official tool reference (`docs/en/reference/tools.md`,
27
+ * moonshotai/kimi-code) AND live against kimi-code v0.27.0: `Write` sends
28
+ * `{ path, content, mode? }`, `Edit` sends
29
+ * `{ path, old_string, new_string, replace_all? }` — the path key is `path`,
30
+ * NOT Claude's `file_path`. `file_path` is kept as a tolerated alias (read
31
+ * second) for Claude-shaped replay payloads, mirroring
32
+ * `runtime/normalize.ts` which resolves both on the CLI path.
33
+ */
34
+ tool_input?: {
35
+ path?: string;
36
+ file_path?: string;
37
+ content?: string;
38
+ new_string?: string;
39
+ command?: string | string[];
40
+ };
41
+ session_id?: string;
42
+ cwd?: string;
43
+ }
44
+ /**
45
+ * Hook stdout response. Only `"deny"` is documented for `permissionDecision`
46
+ * — no `"ask"`/`"allow"` value, and no `additionalContext` field exists on
47
+ * this envelope. Blocking events are `UserPromptSubmit`, `PreToolUse`, and
48
+ * `Stop` only; every other event's return value is ignored.
49
+ */
50
+ interface KimiResponse {
51
+ hookSpecificOutput?: {
52
+ permissionDecision?: "deny";
53
+ permissionDecisionReason?: string;
54
+ };
55
+ }
56
+ //#endregion
57
+ //#region src/adapters/kimi/index.d.ts
58
+ /** Read & parse the Kimi hook stdin payload (same wire shape as Claude for the fields we consume). */
59
+ declare function readKimiInput(): Promise<KimiHookInput>;
60
+ /**
61
+ * Render a portable {@link Prompt} as a Kimi hook response string:
62
+ * `block`/`ask` -> `{hookSpecificOutput:{permissionDecision:"deny",...}}`
63
+ * (only `"deny"` is documented; `ask` is downgraded with {@link ASK_PREFIX});
64
+ * `inform` -> raw text (Kimi appends stdout to context on exit 0 — no
65
+ * documented JSON envelope for non-blocking context injection).
66
+ * @param prompt - The portable policy prompt.
67
+ * @returns The native Kimi response string.
68
+ */
69
+ declare function toKimiResponse(prompt: Prompt): string;
70
+ /**
71
+ * Render a raw deny reason as a Kimi hook response — for callers that already
72
+ * hold a fully-formatted message (e.g. the MCP cache-hit notice) rather than a
73
+ * portable {@link Prompt}. Same envelope as {@link toKimiResponse}, minus the
74
+ * `[BLOCKED]` prompt framing and without `hookEventName` (undocumented upstream).
75
+ * @param reason - The fully-formatted deny message.
76
+ * @returns The native Kimi response string.
77
+ */
78
+ declare function kimiDenyResponse(reason: string): string;
79
+ /**
80
+ * Run the bundled policy over a Kimi hook payload and return the native
81
+ * response string, or null to allow (the hook then exits 0 with empty stdout).
82
+ * @param input - Parsed hook stdin payload.
83
+ * @returns Native response string, or null when the tool use is allowed.
84
+ */
85
+ declare function guard(input: KimiHookInput): string | null;
86
+ //#endregion
87
+ export { type KimiHookInput, type KimiResponse, guard, kimiDenyResponse, readKimiInput, toKimiResponse };
@@ -0,0 +1,2 @@
1
+ import { i as toKimiResponse, n as kimiDenyResponse, r as readKimiInput, t as guard } from "../../kimi-ChbcU2uI.mjs";
2
+ export { guard, kimiDenyResponse, readKimiInput, toKimiResponse };
@@ -1,2 +1,2 @@
1
- import { _ as queryHash, a as cacheLookupSubstringMeta, c as mcpCacheKey, d as extractText, f as IndexSummary, g as jaccardSimilar, h as compactMarkdown, i as cacheLookupSubstring, l as mcpCacheWrite, m as summarizeIndex, n as cacheLookup, o as cachePath, p as loadIndex, r as cacheLookupMeta, s as cacheStore, t as CacheHit, u as webfetchCacheWrite } from "../index-BUwEmIK-.mjs";
1
+ import { _ as queryHash, a as cacheLookupSubstringMeta, c as mcpCacheKey, d as extractText, f as IndexSummary, g as jaccardSimilar, h as compactMarkdown, i as cacheLookupSubstring, l as mcpCacheWrite, m as summarizeIndex, n as cacheLookup, o as cachePath, p as loadIndex, r as cacheLookupMeta, s as cacheStore, t as CacheHit, u as webfetchCacheWrite } from "../index-DhmV2MPh.mjs";
2
2
  export { CacheHit, IndexSummary, cacheLookup, cacheLookupMeta, cacheLookupSubstring, cacheLookupSubstringMeta, cachePath, cacheStore, compactMarkdown, extractText, jaccardSimilar, loadIndex, mcpCacheKey, mcpCacheWrite, queryHash, summarizeIndex, webfetchCacheWrite };
@@ -1,4 +1,4 @@
1
- import { t as evaluate } from "./evaluate-ClfbCDiY.mjs";
1
+ import { t as evaluate } from "./evaluate-B4-MhOy_.mjs";
2
2
  import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
3
3
  import { dirname, join } from "node:path";
4
4
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
@@ -216,4 +216,4 @@ function guard(input) {
216
216
  /** @deprecated use {@link guard}. Kept for back-compat. */
217
217
  const fileSizeGuard = guard;
218
218
  //#endregion
219
- export { writeText as _, fileSizeGuard as a, readClaudeInput as c, collectFiles as d, pathExists as f, spawnCapture as g, sleep as h, denyResponse as i, systemMessage as l, readText as m, blockResponse as n, guard as o, readStdin as p, contextResponse as r, informResponse as s, attachSystemMessage as t, toClaudeResponse as u, commandToString as v };
219
+ export { commandToString as _, fileSizeGuard as a, readClaudeInput as c, collectFiles as d, pathExists as f, writeText as g, spawnCapture as h, denyResponse as i, systemMessage as l, sleep as m, blockResponse as n, guard as o, readText as p, contextResponse as r, informResponse as s, attachSystemMessage as t, toClaudeResponse as u };
@@ -1 +1 @@
1
- export { };
1
+ export {}
package/dist/cli/bin.mjs CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { r as loadDotenv, s as resolveTtlSec } from "../dotenv-Jj8aL1FL.mjs";
3
- import { t as detectHarness } from "../harness-Cb9xR8dC.mjs";
4
- import { t as claudeHome } from "../home-state-BXf38Zi1.mjs";
5
- import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-3tyTW9Ud.mjs";
6
- import { n as writeInitFile, t as initFor } from "../run-Do2JltgU.mjs";
7
- import { $ as runningVersion, Q as runDoctor, Z as notify, et as versionBanner, t as handleHook, zt as todayUtc } from "../handle-Dthtlp7Z.mjs";
8
- import { p as readStdin$1 } from "../claude-51RSspJ1.mjs";
2
+ import { o as resolveStdinMaxBytes } from "../limits-DG5pl-5s.mjs";
3
+ import { r as loadDotenv, s as resolveTtlSec } from "../dotenv-DoqNlALG.mjs";
4
+ import { t as detectHarness } from "../harness-BMuLJ9lm.mjs";
5
+ import { t as claudeHome } from "../home-state-oUGFB4ds.mjs";
6
+ import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-D_WchZ3R.mjs";
7
+ import { n as writeInitFile, t as initFor } from "../run-TAXNRSpD.mjs";
8
+ import { $ as runningVersion, Q as runDoctor, Z as notify, et as versionBanner, i as respond, t as handleHook, zt as todayUtc } from "../handle-J5r2f_O_.mjs";
9
9
  import { delimiter, join } from "node:path";
10
- import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
10
+ import { existsSync, mkdirSync, readFileSync, readSync, readdirSync, writeFileSync } from "node:fs";
11
11
  import { homedir } from "node:os";
12
12
  //#region src/changelog/fetch.ts
13
13
  /**
@@ -908,7 +908,9 @@ function renderPrefixRules(rules) {
908
908
  const HEADER = "# Codex execpolicy rules — generated from @fusengine/harness src/policy/guards/security.ts\n# DO NOT hand-edit: regenerate via `harness codex-rules`.\n#\n# security.ts is the source of truth for dangerous-command detection; every\n# CRITICAL_PATTERNS label maps to decision = \"forbidden\", every ASK_PATTERNS\n# label maps to decision = \"prompt\". Strictest decision wins across matches.\n# The harness remains the AUTHORITATIVE enforcement layer — this file is a\n# complementary defense layer on the Codex side, not a replacement.\n#\n# IRREDUCIBLE gaps (shell syntax, invisible to argv-prefix matching, no\n# prefix_rule can express them) — compensated by sandbox_mode +\n# approval_policy.granular.sandbox_approval, not by this file:\n# - fork bomb: `:(){ :|:& };:`\n# - redirect to a raw disk device: `> /dev/sda`\n# - write to /etc via redirection: `> /etc/...`\n#\n# ASSUMED over-width (intentionally broader than security.ts, chosen to\n# avoid a silent gap rather than an exact-but-brittle translation):\n# - dd: prompts on every dd invocation; security.ts only fires on of=/dev/*\n# - rm: prompts even on paths security.ts exempts via a 'trash' substring\n# - rm/eval/unlink: prompts even on a bare token with no trailing argument;\n# security.ts requires a space (i.e. an argument) after the command\n# - chmod 777: prompts on relative paths too, not only absolute ones\n# - sudo (/usr/bin/sudo alias): forbids the absolute-path form too;\n# security.ts's word-boundary regex does not match a preceding '/'\n# - curl/wget: prompts on EVERY curl/wget invocation, not only when piped\n# into a shell; security.ts's \"remote script piped to a shell\" label\n# only fires on `curl|wget ... | sh`. A simple plain-word pipe IS split\n# by Codex into separate argv commands (a bare-shell rule would also\n# catch that case), but this rule additionally targets the download\n# half for compound chains and non-shell payload execution where\n# sh/bash never appears as argv[0] (curl -o x && chmod +x x && ./x,\n# curl ... | python3); it replaces a former bare sh/bash/zsh heuristic\n# that prompted on ~50% of Codex's own `bash -lc \"...\"` command\n# wrapping and rarely caught the actual download\n#\n# NOT covered (out of scope, not an over-width — a purely local pipe with no\n# network fetch is invisible to this rule AND to security.ts, which only\n# matches curl/wget as the left-hand side of the pipe):\n# - `cat file | sh`: local pipe, no curl/wget involved\n# - quoted-variable/redirection installer one-liners, e.g.\n# `curl -fsSL \"$URL\" | sh`: variable substitution disables Codex's\n# pipe-splitting, collapsing the whole invocation to a single\n# ['bash', '-lc', '<script>'] where curl is not argv[0] — this exact\n# form was covered by the removed bare sh/bash heuristic and is no\n# longer covered by any rule here\n#\n# The following labels have NO argv-prefix equivalent at all (shell syntax\n# not visible to execpolicy's argv matching) and are intentionally NOT\n# expressed as prefix_rule() below — covered instead by sandbox_mode/approval_policy:";
909
909
  /** Render the full Codex execpolicy `.rules` Starlark file as a string. */
910
910
  function buildCodexRules() {
911
- return `${HEADER}\n${SKIP_LIST.map((s) => `# - ${sanitizeComment(s.sourceLabel)}: ${sanitizeComment(s.reason)}`).join("\n")}\n\n${renderPrefixRules(ALL_RULES)}\n`;
911
+ const skipComment = SKIP_LIST.map((s) => `# - ${sanitizeComment(s.sourceLabel)}: ${sanitizeComment(s.reason)}`).join("\n");
912
+ const body = renderPrefixRules(ALL_RULES);
913
+ return `${HEADER}\n${skipComment}\n\n${body}\n`;
912
914
  }
913
915
  //#endregion
914
916
  //#region src/refs/discover.ts
@@ -972,6 +974,117 @@ function discoverRefs(home, cwd, marketplaces) {
972
974
  return [...bySkill.values()].join(delimiter);
973
975
  }
974
976
  //#endregion
977
+ //#region src/cli/scope.ts
978
+ /** Closed set of plugin scopes accepted after `harness hook <id>`. */
979
+ const VALID_SCOPES = /* @__PURE__ */ new Set([
980
+ "solid",
981
+ "rules",
982
+ "carto",
983
+ "security",
984
+ "changelog",
985
+ "aipilot",
986
+ "lessons",
987
+ "seo",
988
+ "memory",
989
+ "tailwindcss"
990
+ ]);
991
+ /**
992
+ * Parse the optional scope argument.
993
+ * @param scopeArg - `process.argv[4]`, when present.
994
+ * @param err - Warning sink (defaults to stderr; injectable for tests).
995
+ * @returns The valid scope, or `"core"` (with a warning) otherwise.
996
+ */
997
+ function parseScope(scopeArg, err = (m) => process.stderr.write(m)) {
998
+ if (scopeArg !== void 0 && scopeArg !== "core" && !VALID_SCOPES.has(scopeArg)) err(`harness: unknown scope "${scopeArg}", falling back to "core"\n`);
999
+ return scopeArg !== void 0 && VALID_SCOPES.has(scopeArg) ? scopeArg : "core";
1000
+ }
1001
+ //#endregion
1002
+ //#region src/cli/hook-io.ts
1003
+ /**
1004
+ * Hook stdin reading + debug tracing for `harness hook` — extracted from
1005
+ * `bin.ts` so the command dispatcher stays under the SOLID line ceiling.
1006
+ * Tracing is stderr-only and active only when FUSE_HARNESS_DEBUG=1 AND
1007
+ * CI=true (both set by test/sim/exec.ts; never in an interactive session).
1008
+ *
1009
+ * Stdin is read BOUNDED (`resolveStdinMaxBytes`, default 16 MiB): the reader
1010
+ * never buffers past the cap, so an oversized payload cannot exhaust memory
1011
+ * nor slip an uninspected tool call through (fail-closed — see `bin.ts`).
1012
+ */
1013
+ const hookDebug = process.env.FUSE_HARNESS_DEBUG === "1" && process.env.CI === "true";
1014
+ /** stderr-only trace, no-op outside the debug flag combination above. */
1015
+ function traceHook(label, data) {
1016
+ if (hookDebug) process.stderr.write(`[hook-debug] ${label}: ${typeof data === "string" ? data : JSON.stringify(data)}\n`);
1017
+ }
1018
+ /** Type guard for the oversize variant (narrows the readStdin union). */
1019
+ function isOversize(x) {
1020
+ return typeof x === "object" && x !== null && x.kind === "oversize";
1021
+ }
1022
+ /** First 4 KiB are kept on oversize for event-name sniffing downstream. */
1023
+ const HEAD_BYTES = 4096;
1024
+ const CHUNK = 64 * 1024;
1025
+ /**
1026
+ * Read a file descriptor to EOF, bounded at `maxBytes` (+1 byte to detect
1027
+ * the overflow). Injectable fd so tests never touch process stdin.
1028
+ * @param fd - The descriptor to read (0 in production).
1029
+ * @param maxBytes - Cap from {@link resolveStdinMaxBytes}.
1030
+ */
1031
+ function readBounded(fd, maxBytes) {
1032
+ const buf = Buffer.alloc(CHUNK);
1033
+ const parts = [];
1034
+ let total = 0;
1035
+ for (;;) {
1036
+ const n = readSync(fd, buf, 0, CHUNK, null);
1037
+ if (n === 0) break;
1038
+ total += n;
1039
+ parts.push(Buffer.from(buf.subarray(0, n)));
1040
+ if (total > maxBytes) return {
1041
+ kind: "oversize",
1042
+ head: Buffer.concat(parts).subarray(0, HEAD_BYTES).toString("utf8")
1043
+ };
1044
+ }
1045
+ traceHook("stdin-text-length", total);
1046
+ return {
1047
+ kind: "ok",
1048
+ text: Buffer.concat(parts).toString("utf8")
1049
+ };
1050
+ }
1051
+ /** Read the hook payload from stdin; `{}` on empty or invalid JSON (fail-open parity). */
1052
+ async function readStdin() {
1053
+ const read = readBounded(0, resolveStdinMaxBytes());
1054
+ if (read.kind === "oversize") return read;
1055
+ const text = read.text.trim();
1056
+ if (!text) return {};
1057
+ try {
1058
+ const parsed = JSON.parse(text);
1059
+ return typeof parsed === "object" && parsed !== null ? parsed : {};
1060
+ } catch (e) {
1061
+ traceHook("stdin-parse-error", e instanceof Error ? e.message : String(e));
1062
+ return {};
1063
+ }
1064
+ }
1065
+ /** Blockable hook events (fail-closed on oversize); others are observation-only. */
1066
+ const BLOCKABLE = /* @__PURE__ */ new Set([
1067
+ "PreToolUse",
1068
+ "UserPromptSubmit",
1069
+ "Stop"
1070
+ ]);
1071
+ /**
1072
+ * Native stdout for an oversize payload: a deny on blockable (or
1073
+ * undeterminable) events — never an uninspected passthrough — and a neutral
1074
+ * empty string on observation-only events (no crash, no noise).
1075
+ * @param id - Harness id (selects the native deny shape via `respond`).
1076
+ * @param head - The first bytes of the payload (event-name sniffing).
1077
+ */
1078
+ function oversizeStdout(id, head) {
1079
+ const event = /"hook_event_name"\s*:\s*"([^"]+)"/.exec(head)?.[1] ?? "";
1080
+ if (event && !BLOCKABLE.has(event)) return "";
1081
+ return respond(id, {
1082
+ kind: "block",
1083
+ title: "Oversize hook payload",
1084
+ reason: `stdin payload exceeds ${resolveStdinMaxBytes()} bytes — denied uninspected`
1085
+ });
1086
+ }
1087
+ //#endregion
975
1088
  //#region src/cli/hook-sound.ts
976
1089
  /**
977
1090
  * @module hook-sound
@@ -1032,22 +1145,6 @@ function maybePlaySound(argv) {
1032
1145
  * harness changelog fetch + diff the Claude Code changelog, print a JSON summary (changelog-watcher)
1033
1146
  * harness codex-rules generate a Codex execpolicy .rules (Starlark) file from security.ts; stdout or --out <path>
1034
1147
  */
1035
- const hookDebug = process.env.FUSE_HARNESS_DEBUG === "1";
1036
- function traceHook(label, data) {
1037
- if (hookDebug) process.stderr.write(`[hook-debug] ${label}: ${typeof data === "string" ? data : JSON.stringify(data)}\n`);
1038
- }
1039
- async function readStdin() {
1040
- const text = (await readStdin$1()).trim();
1041
- traceHook("stdin-text-length", text.length);
1042
- if (!text) return {};
1043
- try {
1044
- const parsed = JSON.parse(text);
1045
- return typeof parsed === "object" && parsed !== null ? parsed : {};
1046
- } catch (e) {
1047
- traceHook("stdin-parse-error", e instanceof Error ? e.message : String(e));
1048
- return {};
1049
- }
1050
- }
1051
1148
  const cmd = process.argv[2];
1052
1149
  if (cmd === "--version" || cmd === "-v") {
1053
1150
  process.stderr.write(versionBanner(import.meta.url) + "\n");
@@ -1059,19 +1156,7 @@ if (cmd === "--version" || cmd === "-v") {
1059
1156
  } else if (cmd === "hook") {
1060
1157
  const id = process.argv[3] ?? detectHarness().id;
1061
1158
  loadDotenv(id);
1062
- const scopeArg = process.argv[4];
1063
- const scope = scopeArg !== void 0 && (/* @__PURE__ */ new Set([
1064
- "solid",
1065
- "rules",
1066
- "carto",
1067
- "security",
1068
- "changelog",
1069
- "aipilot",
1070
- "lessons",
1071
- "seo",
1072
- "memory",
1073
- "tailwindcss"
1074
- ])).has(scopeArg) ? scopeArg : "core";
1159
+ const scope = parseScope(process.argv[4]);
1075
1160
  if (maybePlaySound(process.argv)) process.exit(0);
1076
1161
  const marketplaces = (process.env.FUSE_HARNESS_MARKETPLACES ?? "fusengine-plugins").split(",").map((s) => s.trim()).filter(Boolean);
1077
1162
  const refsDir = process.env.FUSE_HARNESS_REFS || discoverRefs(homedir(), process.cwd(), marketplaces) || void 0;
@@ -1081,7 +1166,13 @@ if (cmd === "--version" || cmd === "-v") {
1081
1166
  });
1082
1167
  let outcome;
1083
1168
  try {
1084
- outcome = await handleHook(id, await readStdin(), {
1169
+ const stdin = await readStdin();
1170
+ if (isOversize(stdin)) {
1171
+ const stdout = oversizeStdout(id, stdin.head);
1172
+ if (stdout) process.stdout.write(stdout);
1173
+ process.exit(0);
1174
+ }
1175
+ outcome = await handleHook(id, stdin, {
1085
1176
  now: Date.now(),
1086
1177
  cwd: process.cwd(),
1087
1178
  refsDir,
@@ -4,9 +4,9 @@ declare function stagedFiles(): string[];
4
4
  /** Read a file's staged (index) content — not the working-tree version. */
5
5
  declare function stagedContent(path: string): string;
6
6
  /**
7
- * Evaluate staged code files against the policy core; return violation blocks.
8
- * `read` is injected (the real impl is {@link stagedContent}) so this is pure + testable.
9
- */
7
+ * Evaluate staged code files against the policy core; return violation blocks.
8
+ * `read` is injected (the real impl is {@link stagedContent}) so this is pure + testable.
9
+ */
10
10
  declare function checkStaged(files: string[], read: (path: string) => string): string[];
11
11
  //#endregion
12
12
  export { checkStaged, stagedContent, stagedFiles };
@@ -1,2 +1,2 @@
1
- import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-3tyTW9Ud.mjs";
1
+ import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-D_WchZ3R.mjs";
2
2
  export { checkStaged, stagedContent, stagedFiles };
@@ -1,2 +1,2 @@
1
- import { _ as parseEnvInt, a as ProjectLayout, c as projectLayout, d as resolveMaxLines, f as splitTarget, g as ttlLabel, h as resolveTtlSec, i as parseEnvFile, l as DEFAULT_MAX_LINES, m as TTL_ENV_KEY, n as envCandidates, o as STATE_GITIGNORE, p as DEFAULT_TTL_SEC, r as loadDotenv, s as STATE_ROOT, t as HOME_DIR, u as MAX_LINES_ENV_KEY } from "../index-jj_EzZRv.mjs";
2
- export { DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, HOME_DIR, MAX_LINES_ENV_KEY, ProjectLayout, STATE_GITIGNORE, STATE_ROOT, TTL_ENV_KEY, envCandidates, loadDotenv, parseEnvFile, parseEnvInt, projectLayout, resolveMaxLines, resolveTtlSec, splitTarget, ttlLabel };
1
+ import { _ as DEFAULT_TTL_SEC, a as ProjectLayout, b as ttlLabel, c as projectLayout, d as MAX_LINES_ENV_KEY, f as hookBudget, g as storeBudget, h as splitTarget, i as parseEnvFile, l as DEFAULT_MAX_LINES, m as resolveStdinMaxBytes, n as envCandidates, o as STATE_GITIGNORE, p as resolveMaxLines, r as loadDotenv, s as STATE_ROOT, t as HOME_DIR, u as DEFAULT_STDIN_MAX_BYTES, v as TTL_ENV_KEY, x as parseEnvInt, y as resolveTtlSec } from "../index-oXXPs2xl.mjs";
2
+ export { DEFAULT_MAX_LINES, DEFAULT_STDIN_MAX_BYTES, DEFAULT_TTL_SEC, HOME_DIR, MAX_LINES_ENV_KEY, ProjectLayout, STATE_GITIGNORE, STATE_ROOT, TTL_ENV_KEY, envCandidates, hookBudget, loadDotenv, parseEnvFile, parseEnvInt, projectLayout, resolveMaxLines, resolveStdinMaxBytes, resolveTtlSec, splitTarget, storeBudget, ttlLabel };