@fusengine/harness 0.1.59 → 0.1.61

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.
@@ -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-DZCLmSoO.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-ce2J1bHC.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-BLuab-tR.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-BJER7Mbj.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-BgFLlqrs.mjs";
1
+ import { t as evaluate } from "../../evaluate-PuyQPU36.mjs";
2
2
  import { t as formatPrompt } from "../../types-ernB1Dy3.mjs";
3
3
  //#region src/adapters/cline/index.ts
4
4
  /**
@@ -1,5 +1,5 @@
1
1
  import { t as Prompt } from "../../types-DVbIl9md.mjs";
2
- import { a as denyResponse, i as contextResponse, l as readClaudeInput, t as ClaudeHookInput } from "../../index-DZCLmSoO.mjs";
2
+ import { a as denyResponse, i as contextResponse, l as readClaudeInput, t as ClaudeHookInput } from "../../index-ce2J1bHC.mjs";
3
3
 
4
4
  //#region src/adapters/codex/index.d.ts
5
5
  /** Render a portable {@link Prompt} as a Codex hook response, `ask` → explicit deny. */
@@ -1,8 +1,8 @@
1
1
  import { f as countLines } from "../../home-state-D0RLWP8J.mjs";
2
- import { t as evaluate } from "../../evaluate-BgFLlqrs.mjs";
2
+ import { t as evaluate } from "../../evaluate-PuyQPU36.mjs";
3
3
  import { t as formatPrompt } from "../../types-ernB1Dy3.mjs";
4
4
  import { t as parseApplyPatch } from "../../apply-patch-CIS2EZ_q.mjs";
5
- import { c as readClaudeInput, i as denyResponse, r as contextResponse } from "../../claude-BLuab-tR.mjs";
5
+ import { _ as commandToString, c as readClaudeInput, i as denyResponse, r as contextResponse } from "../../claude-BJER7Mbj.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)
@@ -49,7 +49,7 @@ function resolvePrompt(input) {
49
49
  tool: input.tool_name ?? "Write",
50
50
  filePath: i?.file_path,
51
51
  content: i?.content ?? i?.new_string,
52
- command: i?.command
52
+ command: commandToString(i?.command)
53
53
  });
54
54
  return r.decision === "allow" || !r.prompt ? null : r.prompt;
55
55
  }
@@ -59,7 +59,7 @@ function resolvePrompt(input) {
59
59
  * into per-file checks; every other tool routes through the portable policy.
60
60
  */
61
61
  function guard(input) {
62
- const prompt = input.tool_name === "apply_patch" ? applyPatchPrompt(input.tool_input?.command ?? "") : resolvePrompt(input);
62
+ const prompt = input.tool_name === "apply_patch" ? applyPatchPrompt(commandToString(input.tool_input?.command) ?? "") : resolvePrompt(input);
63
63
  return prompt ? toCodexResponse(prompt) : null;
64
64
  }
65
65
  //#endregion
@@ -1,4 +1,4 @@
1
- import { t as evaluate } from "../../evaluate-BgFLlqrs.mjs";
1
+ import { t as evaluate } from "../../evaluate-PuyQPU36.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-BgFLlqrs.mjs";
1
+ import { t as evaluate } from "../../evaluate-PuyQPU36.mjs";
2
2
  import { t as formatPrompt } from "../../types-ernB1Dy3.mjs";
3
3
  //#region src/adapters/gemini/index.ts
4
4
  /**
@@ -15,7 +15,7 @@ interface HermesHookInput {
15
15
  path?: string;
16
16
  file_path?: string;
17
17
  content?: string;
18
- command?: string;
18
+ command?: string | string[];
19
19
  };
20
20
  session_id?: string;
21
21
  cwd?: string;
@@ -1,2 +1,2 @@
1
- import { n as readHermesInput, r as toHermesResponse, t as guard } from "../../hermes-CyhCYqD7.mjs";
1
+ import { n as readHermesInput, r as toHermesResponse, t as guard } from "../../hermes-DKPzVwI7.mjs";
2
2
  export { guard, readHermesInput, toHermesResponse };
@@ -1,8 +1,49 @@
1
- import { t as evaluate } from "./evaluate-BgFLlqrs.mjs";
1
+ import { t as evaluate } from "./evaluate-PuyQPU36.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";
5
5
  import { spawnSync } from "node:child_process";
6
+ //#region src/runtime/command-string.ts
7
+ /** Shell basenames whose `-c`-style argv carries the real script in argv[2]. */
8
+ const SHELLS = /* @__PURE__ */ new Set([
9
+ "bash",
10
+ "sh",
11
+ "zsh",
12
+ "dash"
13
+ ]);
14
+ /** True when `arg` is (a path to) a POSIX shell we know wraps a `-c` script. */
15
+ function isShell(arg) {
16
+ if (arg === void 0) return false;
17
+ const base = arg.slice(arg.lastIndexOf("/") + 1);
18
+ return SHELLS.has(base);
19
+ }
20
+ /** True for a shell command flag (`-c`, `-lc`, `-ic`, …) — anything ending in `c`. */
21
+ function isCommandFlag(arg) {
22
+ return arg !== void 0 && /^-[a-z]*c$/.test(arg);
23
+ }
24
+ /**
25
+ * Coerce a hook payload's `command` field to the shell string the guard chain
26
+ * expects. Harnesses disagree on the wire shape: Claude Code, Cursor, and Hermes
27
+ * send a plain string, but Codex CLI sends an argv ARRAY
28
+ * (`["bash", "-lc", "git commit -m x"]`). A naive `typeof === "string"` check
29
+ * dropped the array to `undefined`, blinding every `ctx.command` guard
30
+ * (destructive-git, mutators, install…) into a silent fail-open. This restores
31
+ * the script so the array and string payloads reach an identical verdict.
32
+ *
33
+ * @param v - Raw `command` value off the payload (string, argv array, or other).
34
+ * @returns A string as-is; a `[shell, -c|-lc, script]` argv reduced to its
35
+ * script; any other all-string array joined with spaces; and `undefined` for
36
+ * empty arrays, arrays holding non-strings, or any other type.
37
+ */
38
+ function commandToString(v) {
39
+ if (typeof v === "string") return v;
40
+ if (!Array.isArray(v)) return void 0;
41
+ if (v.length === 0 || !v.every((e) => typeof e === "string")) return void 0;
42
+ const argv = v;
43
+ if (argv.length >= 3 && isShell(argv[0]) && isCommandFlag(argv[1])) return argv[2];
44
+ return argv.join(" ");
45
+ }
46
+ //#endregion
6
47
  //#region src/util/runtime-io.ts
7
48
  /**
8
49
  * Cross-runtime I/O helpers (Node + Bun). The published CLI ships a `node`
@@ -165,7 +206,7 @@ function guard(input) {
165
206
  tool: input.tool_name ?? "Write",
166
207
  filePath: input.tool_input?.file_path,
167
208
  content: input.tool_input?.content ?? input.tool_input?.new_string,
168
- command: input.tool_input?.command
209
+ command: commandToString(input.tool_input?.command)
169
210
  });
170
211
  if (result.decision === "allow" || !result.prompt) return null;
171
212
  return toClaudeResponse(input.hook_event_name ?? "PreToolUse", result.prompt);
@@ -173,4 +214,4 @@ function guard(input) {
173
214
  /** @deprecated use {@link guard}. Kept for back-compat. */
174
215
  const fileSizeGuard = guard;
175
216
  //#endregion
176
- export { 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 };
217
+ 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 };
package/dist/cli/bin.mjs CHANGED
@@ -2,9 +2,9 @@
2
2
  import { r as loadDotenv, s as resolveTtlSec } from "../dotenv-Jj8aL1FL.mjs";
3
3
  import { t as detectHarness } from "../harness-Cb9xR8dC.mjs";
4
4
  import { t as claudeHome } from "../home-state-D0RLWP8J.mjs";
5
- import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-B4b2_7Qm.mjs";
5
+ import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-CvqZrZUR.mjs";
6
6
  import { n as writeInitFile, t as initFor } from "../run-Do2JltgU.mjs";
7
- import { J as runningVersion, Lt as todayUtc, Y as versionBanner, q as runDoctor, t as handleHook } from "../handle-B6yL03we.mjs";
7
+ import { J as runningVersion, Lt as todayUtc, Y as versionBanner, q as runDoctor, t as handleHook } from "../handle-DJovWDAy.mjs";
8
8
  import { delimiter, join } from "node:path";
9
9
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
10
10
  import { homedir } from "node:os";
@@ -1,2 +1,2 @@
1
- import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-B4b2_7Qm.mjs";
1
+ import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-CvqZrZUR.mjs";
2
2
  export { checkStaged, stagedContent, stagedFiles };
@@ -404,39 +404,61 @@ function hasSafeWriteTarget(cmd) {
404
404
  const quoted = (p) => cmd.includes(`'${p}'`) || cmd.includes(`"${p}"`) || cmd.includes(`'${p}/`) || cmd.includes(`"${p}/`);
405
405
  return SAFE_WRITE_PATHS.some(quoted) || SAFE_WRITE_RAW.some(quoted);
406
406
  }
407
+ /**
408
+ * Command-position anchor: line/segment start or a `;&|(` separator, then any
409
+ * chain of {@link WRAP} wrappers (each with its own {@link WRAP_ARG} tokens).
410
+ * Anchoring a write motif here makes it fire on a real invocation
411
+ * (`env sed -i x src/a.ts`) while ignoring a quoted/argument MENTION of the same
412
+ * text (`git commit -m "fix sed -i doc"`, `npm run t -- --grep "sed -i"`), which
413
+ * is never at a command position.
414
+ *
415
+ * Defense-in-depth first pass, NOT the sole control: regex-on-raw-string is
416
+ * lossy vs a real shell lexer. Known residual gaps (enumerated, not silently
417
+ * assumed away): command substitution `$(…)`/backticks, indirect exec via
418
+ * `bash -c`/`eval`/`find -exec`, process substitution `<(…)`, shell-quoting
419
+ * tricks (`s''ed`, `${x}ed`), and variable-indirection targets (`tee $F`). Those
420
+ * need a tokenizer, out of scope here; the Write/Edit tool gate stays the
421
+ * backstop for what slips through.
422
+ */
423
+ const CMD = `(?:^|[\\n;&|(])\\s*(?:(?:env|timeout|nice|nohup|sudo|command|stdbuf|time|ionice|exec|xargs)\\s+(?:(?:\\w+=\\S+|--?[^\\s-]\\S*|\\d+[smhd]?)\\s+)*)*`;
407
424
  //#endregion
408
425
  //#region src/policy/guards/bash-write-patterns.ts
426
+ /** Code-file extensions the write guards police (shared by CODE_REDIRECT and
427
+ * CODE_COMMAND_WRITE). Case-sensitive, parity with the Python guard's list. */
428
+ const CODE_EXT = "ts|tsx|js|jsx|py|go|rb|rs|java|kt|php|swift|vue|svelte|astro|css|c|cpp|h";
409
429
  /** Redirect (`>`/`>>`) targeting a code-file extension. */
410
- const CODE_REDIRECT = /(?:>>?)\s*[^\s|;&]*\.(?:ts|tsx|js|jsx|py|go|rb|rs|java|kt|php|swift|vue|svelte|astro|css|c|cpp|h)\b/;
430
+ const CODE_REDIRECT = new RegExp(`(?:>>?)\\s*[^\\s|;&]*\\.(?:${CODE_EXT})\\b`);
411
431
  /**
412
432
  * Interpreters / tools that mutate source in place, plus heredoc-into-file —
413
433
  * split into labeled sub-patterns (parity bash-write-guard.py `DENY_PATTERNS`,
414
434
  * each with its own `desc`) so the deny reason names which motif matched
415
- * instead of a single generic message for all six.
435
+ * instead of a single generic message. Every command-named motif is
436
+ * {@link CMD}-anchored (start/separator + optional wrapper chain); only the
437
+ * structural heredoc-into-file motif is position-free.
416
438
  */
417
439
  const CODE_MUTATORS = [
418
440
  {
419
- re: /\bpython3?\s+-\s*<</,
441
+ re: new RegExp(`${CMD}python3?\\s+-\\s*<<`),
420
442
  desc: "Python heredoc input"
421
443
  },
422
444
  {
423
- re: /\bpython3?\s+-c\b/,
445
+ re: new RegExp(`${CMD}python3?\\s+-c\\b`),
424
446
  desc: "Python inline script"
425
447
  },
426
448
  {
427
- re: /\bsed\b[^|]*\s-i/,
449
+ re: new RegExp(`${CMD}sed\\b[^|]*\\s-i`),
428
450
  desc: "sed in-place edit"
429
451
  },
430
452
  {
431
- re: /\bperl\b[^|]*\s-[pi]i?\b/,
453
+ re: new RegExp(`${CMD}perl\\b[^|]*\\s-[pi]i?\\b`),
432
454
  desc: "perl in-place edit"
433
455
  },
434
456
  {
435
- re: /\bawk\b[^|]*-i\s*inplace/,
457
+ re: new RegExp(`${CMD}awk\\b[^|]*-i\\s*inplace`),
436
458
  desc: "awk in-place edit"
437
459
  },
438
460
  {
439
- re: /(?:^|[\n;&|(])\s*patch(?=\s|<|[;&|)>]|$)/,
461
+ re: new RegExp(`${CMD}patch(?=\\s|<|[;&|)>]|$)`),
440
462
  desc: "patch file modification"
441
463
  },
442
464
  {
@@ -444,6 +466,15 @@ const CODE_MUTATORS = [
444
466
  desc: "heredoc redirected into a file"
445
467
  }
446
468
  ];
469
+ /**
470
+ * `tee`/`dd of=` whose TARGET is a code file — the command-form parallel to
471
+ * CODE_REDIRECT. {@link CMD}-anchored so `env`/`timeout` wrappers don't shield
472
+ * it; the `tee` branch scans ALL args up to the next `;&|` separator so a
473
+ * decoy-first-target (`tee log.txt src/x.ts`) can't hide the code write. A tee
474
+ * to a NON-code target (`cmd | tee results.txt`) does not match — it stays a
475
+ * plain ASK_WRITERS ask, never a hard block, so ordinary logging is untouched.
476
+ */
477
+ const CODE_COMMAND_WRITE = new RegExp(`${CMD}(?:tee\\s+[^;&|\\n]*?|dd\\b[^|]*\\bof=\\S*)\\.(?:${CODE_EXT})\\b`);
447
478
  /** File-mutating one-liners via `node -e` / `ruby -e` (parity NODE_WRITES/RUBY_WRITES). */
448
479
  const NODE_WRITES = /writeFile|appendFile|createWriteStream|fs\.(?:write|rename|unlink|mkdir|rmdir|copyFile)|execSync|spawnSync|child_process/;
449
480
  const RUBY_WRITES = /File\.(?:write|open|delete|rename)|IO\.write|FileUtils|\bsystem\b|\bexec\b|`[^`]/;
@@ -530,11 +561,22 @@ function askFileWrite(reason) {
530
561
  * to source files); asks before other file-writing shell commands unless the
531
562
  * target is a harness-owned safe path. Forces use of the Write/Edit tool so
532
563
  * APEX/SOLID checks are not bypassed.
564
+ *
565
+ * The code-write detectors (CODE_MUTATORS, CODE_COMMAND_WRITE) run BEFORE the
566
+ * SAFE_PREFIXES short-circuit: they are command-position anchored
567
+ * (bash-command-anchor.ts), so a transparent wrapper (`env sed -i src/x.ts`,
568
+ * `timeout 5 patch`, `cp a b; tee src/y.ts`) can no longer smuggle a code write
569
+ * past its safe first token, while a quoted mention (`git commit -m "fix sed -i"`)
570
+ * still falls through. The short-circuit becomes the terminal allow — "first
571
+ * token never writes, nothing above matched".
533
572
  */
534
573
  function bashWriteGuard(ctx) {
535
574
  if (ctx.tool !== "Bash" || !ctx.command) return null;
536
575
  const cmd = ctx.command;
537
576
  const stripped = cmd.trim();
577
+ const mutator = CODE_MUTATORS.find((m) => m.re.test(cmd));
578
+ if (mutator) return blockCodeWrite(`${mutator.desc} — Use Edit/Write tools instead`);
579
+ if (CODE_COMMAND_WRITE.test(cmd)) return blockCodeWrite("tee/dd into a code file — Use Edit/Write tools instead");
538
580
  if (SAFE_PREFIXES.some((p) => stripped.startsWith(p)) && !FILE_REDIRECT.test(stripped)) return null;
539
581
  if (cmd.includes(".fuse-harness/cache/sessions")) return {
540
582
  kind: "block",
@@ -542,8 +584,6 @@ function bashWriteGuard(ctx) {
542
584
  reason: "Bash access to the harness session-state directory is a hook-bypass vector — the freshness/APEX enforcement reads it to decide block/allow.",
543
585
  actions: ["Never read or write session state from the shell"]
544
586
  };
545
- const mutator = CODE_MUTATORS.find((m) => m.re.test(cmd));
546
- if (mutator) return blockCodeWrite(`${mutator.desc} — Use Edit/Write tools instead`);
547
587
  if (FILE_REDIRECT.test(cmd)) {
548
588
  if (isSafeWritePath(cmd)) return null;
549
589
  return CODE_REDIRECT.test(cmd) ? blockCodeWrite("Bash redirect to code file — Use Write/Edit tools (enforces APEX + SOLID specs)") : askFileWrite("Shell redirect to file detected. Authorize?");
@@ -4,7 +4,7 @@ import { r as projectLayout } from "./layout-C0jaaCQC.mjs";
4
4
  import { i as walkUpFor, n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./project-root-3kk7gCOp.mjs";
5
5
  import { A as detectCreationIntent, E as isExcludedSwiftPath, F as docConsultedGate, H as detectProjectType$1, I as evaluateApex, M as POST_AUTH_GATES, N as PRE_AUTH_GATES, S as usesTailwindUtilities, T as isExcludedJsPath, V as detectModularArchitecture, W as requiredArchSkill, _ as scanPlugin, c as EXCLUDE_DIRS$1, d as buildApexTaskInjection, h as buildClaudeMdContext, k as capVerbosity, l as PROJECT_INDICATORS, n as missingSeoElements, o as parseEnrichment, r as descFromText, s as parseEntry, t as isHtmlLike, w as frameworkSolidGate, x as skillTriggerGate, y as parseField, z as detectFramework } from "./validate-DCQ8dkdL.mjs";
6
6
  import { a as sanitizeSessionId, c as sessionsDir, d as countFrameworkCodeLines, f as countLines, i as loadSessionState, l as PLUGINS_DIR, n as fuseHarnessHome, o as saveSessionState, r as fusengineCache, t as claudeHome } from "./home-state-D0RLWP8J.mjs";
7
- import { n as FAIL_CLOSED, t as evaluate } from "./evaluate-BgFLlqrs.mjs";
7
+ import { n as FAIL_CLOSED, t as evaluate } from "./evaluate-PuyQPU36.mjs";
8
8
  import { a as writeJsonFile, i as readJsonFile, r as hashText, t as atomicWrite } from "./json-io-DisYd2fb.mjs";
9
9
  import { r as isDocConsulted } from "./doc-helpers-CWZegVdR.mjs";
10
10
  import { n as findMarketplacePlugins, r as readPluginMeta, t as resolveSkillPath } from "./skill-path-DhItkBzk.mjs";
@@ -14,8 +14,8 @@ import { d as loadIndex, i as cacheLookupMeta, n as webfetchCacheWrite, o as cac
14
14
  import { t as loadRefs } from "./loader-AGz4nK7d.mjs";
15
15
  import { a as writeLastNonce, c as recordAgent, d as recordRefRead, f as recordTarget, h as apexAuthorizationGate, i as verifyTrack, l as recordBrainstormRequired, m as trivialCount, n as saveTrack, o as agentsFresh, p as recordTrivialEdit, r as signTrack, s as emptyTrack, t as loadTrack, u as recordDoc } from "./store-CQ4roWrU.mjs";
16
16
  import { t as parseApplyPatch } from "./apply-patch-CIS2EZ_q.mjs";
17
- import { d as collectFiles, f as pathExists, g as writeText, h as spawnCapture, i as denyResponse, l as systemMessage, m as sleep, n as blockResponse, p as readText, r as contextResponse, s as informResponse, t as attachSystemMessage } from "./claude-BLuab-tR.mjs";
18
- import { r as toHermesResponse } from "./hermes-CyhCYqD7.mjs";
17
+ import { _ as commandToString, d as collectFiles, f as pathExists, g as writeText, h as spawnCapture, i as denyResponse, l as systemMessage, m as sleep, n as blockResponse, p as readText, r as contextResponse, s as informResponse, t as attachSystemMessage } from "./claude-BJER7Mbj.mjs";
18
+ import { r as toHermesResponse } from "./hermes-DKPzVwI7.mjs";
19
19
  import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
20
20
  import { appendFileSync, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
21
21
  import { homedir } from "node:os";
@@ -111,7 +111,7 @@ function normalizeEvent(id, payload) {
111
111
  ...base,
112
112
  filePath: str(input.file_path) ?? str(input.path) ?? str(payload.file_path),
113
113
  content: str(input.content) ?? str(input.new_string),
114
- command: str(input.command) ?? str(payload.command)
114
+ command: commandToString(input.command) ?? commandToString(payload.command)
115
115
  };
116
116
  }
117
117
  //#endregion
@@ -1,6 +1,6 @@
1
- import { t as evaluate } from "./evaluate-BgFLlqrs.mjs";
1
+ import { t as evaluate } from "./evaluate-PuyQPU36.mjs";
2
2
  import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
3
- import { c as readClaudeInput } from "./claude-BLuab-tR.mjs";
3
+ import { _ as commandToString, c as readClaudeInput } from "./claude-BJER7Mbj.mjs";
4
4
  //#region src/adapters/hermes/index.ts
5
5
  /**
6
6
  * Hermes Agent adapter (hook-mode). Nous Research's hermes-agent (2026) pipes a
@@ -45,7 +45,7 @@ function guard(input) {
45
45
  tool: input.tool_name ?? "write_file",
46
46
  filePath: t?.path ?? t?.file_path,
47
47
  content: t?.content,
48
- command: t?.command
48
+ command: commandToString(t?.command)
49
49
  });
50
50
  if (r.decision === "allow" || !r.prompt) return null;
51
51
  return toHermesResponse(r.prompt);
@@ -218,7 +218,9 @@ declare const CODE_REDIRECT: RegExp;
218
218
  * Interpreters / tools that mutate source in place, plus heredoc-into-file —
219
219
  * split into labeled sub-patterns (parity bash-write-guard.py `DENY_PATTERNS`,
220
220
  * each with its own `desc`) so the deny reason names which motif matched
221
- * instead of a single generic message for all six.
221
+ * instead of a single generic message. Every command-named motif is
222
+ * {@link CMD}-anchored (start/separator + optional wrapper chain); only the
223
+ * structural heredoc-into-file motif is position-free.
222
224
  */
223
225
  declare const CODE_MUTATORS: readonly {
224
226
  re: RegExp;
@@ -250,6 +252,14 @@ declare const SESSION_STATE_FRAGMENT = ".fuse-harness/cache/sessions";
250
252
  * to source files); asks before other file-writing shell commands unless the
251
253
  * target is a harness-owned safe path. Forces use of the Write/Edit tool so
252
254
  * APEX/SOLID checks are not bypassed.
255
+ *
256
+ * The code-write detectors (CODE_MUTATORS, CODE_COMMAND_WRITE) run BEFORE the
257
+ * SAFE_PREFIXES short-circuit: they are command-position anchored
258
+ * (bash-command-anchor.ts), so a transparent wrapper (`env sed -i src/x.ts`,
259
+ * `timeout 5 patch`, `cp a b; tee src/y.ts`) can no longer smuggle a code write
260
+ * past its safe first token, while a quoted mention (`git commit -m "fix sed -i"`)
261
+ * still falls through. The short-circuit becomes the terminal allow — "first
262
+ * token never writes, nothing above matched".
253
263
  */
254
264
  declare function bashWriteGuard(ctx: GuardContext): Prompt | null;
255
265
  //#endregion
@@ -9,7 +9,7 @@ interface ClaudeHookInput {
9
9
  file_path?: string;
10
10
  content?: string;
11
11
  new_string?: string;
12
- command?: string;
12
+ command?: string | string[];
13
13
  };
14
14
  cwd?: string;
15
15
  }
package/dist/index.d.mts CHANGED
@@ -5,7 +5,7 @@ import { a as HarnessInfo, i as HarnessId, n as detectMode, o as HarnessMode, r
5
5
  import { a as isDocConsulted, i as formatDocSatisfactionStatus, n as DocSatisfactionStatus, o as resolveSessions, r as formatDocDeny, t as AuthEntry } from "./doc-helpers-CEKzGg2u.mjs";
6
6
  import { t as incrementTrivialEditCounter } from "./index-BOBXQ91y.mjs";
7
7
  import { a as compactJson, i as walkUpFor, n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./index-BEMumjOw.mjs";
8
- import { $ as ASK_PATTERNS, A as FAIL_CLOSED, B as SWIFT_PROTO_RE, C as usesTailwindUtilities, Ct as ModularArchitecture, D as MAX_TOKENS, Dt as isApexCommand, E as MAX_EXA_RESULTS, Et as detectProjectType, F as installGuard, G as CODE_MUTATORS, H as interfaceSeparationGuard, I as GO_DECL_RE, J as SAFE_PREFIXES, K as CODE_REDIRECT, L as JAVA_DECL_RE, M as clearUserGuards, N as registerGuard, O as capVerbosity, Ot as requiredArchSkill, P as runGuards, Q as protectedPathGuard, R as PHP_DECL_RE, S as skillTriggerGate, St as DEV_KEYWORDS, T as frameworkSolidGate, Tt as detectModularArchitecture, U as bashWriteGuard, V as TS_DECL_RE, W as ASK_WRITERS, X as PROTECTED_FRAGMENTS, Y as SESSION_STATE_FRAGMENT, Z as PROTECTED_GIT_RE, _ as DEV_VERBS, _t as SOLID_REF, a as firstHeading, at as evaluate, b as detectClaudeMdProjectType, bt as evaluateFileSize, c as parseEntry, ct as GIT_ASK, d as EXCLUDE_DIRS, dt as RALPH_SAFE, et as CRITICAL_PATTERNS, f as PROJECT_INDICATORS, ft as SYSTEM_INSTALL, g as loadApexTaskState, gt as PLUGINS_DIR, h as buildApexTaskInjection, ht as FileSizeVerdict, i as firstComment, it as GuardContext, j as GUARDS, k as detectCreationIntent, l as parseBodyDesc, lt as GIT_BLOCKED, m as buildApexTaskContext, mt as matchPatterns, n as missingSeoElements, nt as securityGuard, o as TreeEntry, ot as PolicyContext, p as ApexTaskState, pt as isRalphMode, q as FILE_REDIRECT, r as descFromText, rt as Guard, s as parseEnrichment, st as PolicyResult, t as isHtmlLike, tt as LabeledPattern, u as parseField, ut as PROJECT_INSTALL, v as buildApexInstruction, vt as countFrameworkCodeLines, w as SKILL_TRIGGERS, wt as ProjectType, x as detectRequiredSkills, xt as detectFramework, y as buildClaudeMdContext, yt as countLines, z as PY_MODEL_RE } from "./index-BfMsILmg.mjs";
8
+ import { $ as ASK_PATTERNS, A as FAIL_CLOSED, B as SWIFT_PROTO_RE, C as usesTailwindUtilities, Ct as ModularArchitecture, D as MAX_TOKENS, Dt as isApexCommand, E as MAX_EXA_RESULTS, Et as detectProjectType, F as installGuard, G as CODE_MUTATORS, H as interfaceSeparationGuard, I as GO_DECL_RE, J as SAFE_PREFIXES, K as CODE_REDIRECT, L as JAVA_DECL_RE, M as clearUserGuards, N as registerGuard, O as capVerbosity, Ot as requiredArchSkill, P as runGuards, Q as protectedPathGuard, R as PHP_DECL_RE, S as skillTriggerGate, St as DEV_KEYWORDS, T as frameworkSolidGate, Tt as detectModularArchitecture, U as bashWriteGuard, V as TS_DECL_RE, W as ASK_WRITERS, X as PROTECTED_FRAGMENTS, Y as SESSION_STATE_FRAGMENT, Z as PROTECTED_GIT_RE, _ as DEV_VERBS, _t as SOLID_REF, a as firstHeading, at as evaluate, b as detectClaudeMdProjectType, bt as evaluateFileSize, c as parseEntry, ct as GIT_ASK, d as EXCLUDE_DIRS, dt as RALPH_SAFE, et as CRITICAL_PATTERNS, f as PROJECT_INDICATORS, ft as SYSTEM_INSTALL, g as loadApexTaskState, gt as PLUGINS_DIR, h as buildApexTaskInjection, ht as FileSizeVerdict, i as firstComment, it as GuardContext, j as GUARDS, k as detectCreationIntent, l as parseBodyDesc, lt as GIT_BLOCKED, m as buildApexTaskContext, mt as matchPatterns, n as missingSeoElements, nt as securityGuard, o as TreeEntry, ot as PolicyContext, p as ApexTaskState, pt as isRalphMode, q as FILE_REDIRECT, r as descFromText, rt as Guard, s as parseEnrichment, st as PolicyResult, t as isHtmlLike, tt as LabeledPattern, u as parseField, ut as PROJECT_INSTALL, v as buildApexInstruction, vt as countFrameworkCodeLines, w as SKILL_TRIGGERS, wt as ProjectType, x as detectRequiredSkills, xt as detectFramework, y as buildClaudeMdContext, yt as countLines, z as PY_MODEL_RE } from "./index-BUDzFkeo.mjs";
9
9
  import { n as RouteResult, r as ScoredRef, t as RefMeta } from "./types-CY5qT2X1.mjs";
10
10
  import { a as PRE_AUTH_GATES, c as evaluateApex, i as POST_AUTH_GATES, l as freshnessGate, n as ApexContext, o as brainstormGate, r as ApexGate, s as docConsultedGate, t as APEX_GATES, u as solidReadGate } from "./apex-Wdi1nq_w.mjs";
11
11
  import { a as ReminderState, c as readState, d as throttleMs, i as registryFile, l as setStateField, n as addRoot, o as lessonsFileFor, r as readRoots, s as nowStamp, t as ensureMemoryGitignore, u as stateFileFor } from "./index-DLYhervv.mjs";
package/dist/index.mjs CHANGED
@@ -6,7 +6,7 @@ import { i as walkUpFor, n as projectRoot, r as projectRootOrNull, t as isCodeFi
6
6
  import { n as detectMode, r as modeFor, t as detectHarness } from "./harness-Cb9xR8dC.mjs";
7
7
  import { A as detectCreationIntent, B as DEV_KEYWORDS, C as SKILL_TRIGGERS, D as MAX_EXA_RESULTS, F as docConsultedGate, H as detectProjectType, I as evaluateApex, L as freshnessGate, M as POST_AUTH_GATES, N as PRE_AUTH_GATES, O as MAX_TOKENS, P as brainstormGate, R as solidReadGate, S as usesTailwindUtilities, U as isApexCommand, V as detectModularArchitecture, W as requiredArchSkill, a as firstHeading, b as detectRequiredSkills, c as EXCLUDE_DIRS, d as buildApexTaskInjection, f as loadApexTaskState, g as detectClaudeMdProjectType, h as buildClaudeMdContext, i as firstComment, j as APEX_GATES, k as capVerbosity, l as PROJECT_INDICATORS, m as buildApexInstruction, n as missingSeoElements, o as parseEnrichment, p as DEV_VERBS, r as descFromText, s as parseEntry, t as isHtmlLike, u as buildApexTaskContext, v as parseBodyDesc, w as frameworkSolidGate, x as skillTriggerGate, y as parseField, z as detectFramework } from "./validate-DCQ8dkdL.mjs";
8
8
  import { d as countFrameworkCodeLines, f as countLines, l as PLUGINS_DIR, p as evaluateFileSize, u as SOLID_REF } from "./home-state-D0RLWP8J.mjs";
9
- import { A as PROJECT_INSTALL, C as PROTECTED_GIT_RE, D as securityGuard, E as CRITICAL_PATTERNS, M as SYSTEM_INSTALL, N as isRalphMode, O as GIT_ASK, P as matchPatterns, S as PROTECTED_FRAGMENTS, T as ASK_PATTERNS, _ as CODE_MUTATORS, a as registerGuard, b as SAFE_PREFIXES, c as GO_DECL_RE, d as PY_MODEL_RE, f as SWIFT_PROTO_RE, g as ASK_WRITERS, h as bashWriteGuard, i as clearUserGuards, j as RALPH_SAFE, k as GIT_BLOCKED, l as JAVA_DECL_RE, m as interfaceSeparationGuard, n as FAIL_CLOSED, o as runGuards, p as TS_DECL_RE, r as GUARDS, s as installGuard, t as evaluate, u as PHP_DECL_RE, v as CODE_REDIRECT, w as protectedPathGuard, x as SESSION_STATE_FRAGMENT, y as FILE_REDIRECT } from "./evaluate-BgFLlqrs.mjs";
9
+ import { A as PROJECT_INSTALL, C as PROTECTED_GIT_RE, D as securityGuard, E as CRITICAL_PATTERNS, M as SYSTEM_INSTALL, N as isRalphMode, O as GIT_ASK, P as matchPatterns, S as PROTECTED_FRAGMENTS, T as ASK_PATTERNS, _ as CODE_MUTATORS, a as registerGuard, b as SAFE_PREFIXES, c as GO_DECL_RE, d as PY_MODEL_RE, f as SWIFT_PROTO_RE, g as ASK_WRITERS, h as bashWriteGuard, i as clearUserGuards, j as RALPH_SAFE, k as GIT_BLOCKED, l as JAVA_DECL_RE, m as interfaceSeparationGuard, n as FAIL_CLOSED, o as runGuards, p as TS_DECL_RE, r as GUARDS, s as installGuard, t as evaluate, u as PHP_DECL_RE, v as CODE_REDIRECT, w as protectedPathGuard, x as SESSION_STATE_FRAGMENT, y as FILE_REDIRECT } from "./evaluate-PuyQPU36.mjs";
10
10
  import { i as resolveSessions, n as formatDocSatisfactionStatus, r as isDocConsulted, t as formatDocDeny } from "./doc-helpers-CWZegVdR.mjs";
11
11
  import { i as parseFrontmatter, n as scoreReferences, r as globToRe, t as routeReferences } from "./router-PKVNBHge.mjs";
12
12
  import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
@@ -1,3 +1,3 @@
1
- import { $ as ASK_PATTERNS, A as FAIL_CLOSED, B as SWIFT_PROTO_RE, C as usesTailwindUtilities, Ct as ModularArchitecture, D as MAX_TOKENS, Dt as isApexCommand, E as MAX_EXA_RESULTS, Et as detectProjectType, F as installGuard, G as CODE_MUTATORS, H as interfaceSeparationGuard, I as GO_DECL_RE, J as SAFE_PREFIXES, K as CODE_REDIRECT, L as JAVA_DECL_RE, M as clearUserGuards, N as registerGuard, O as capVerbosity, Ot as requiredArchSkill, P as runGuards, Q as protectedPathGuard, R as PHP_DECL_RE, S as skillTriggerGate, St as DEV_KEYWORDS, T as frameworkSolidGate, Tt as detectModularArchitecture, U as bashWriteGuard, V as TS_DECL_RE, W as ASK_WRITERS, X as PROTECTED_FRAGMENTS, Y as SESSION_STATE_FRAGMENT, Z as PROTECTED_GIT_RE, _ as DEV_VERBS, _t as SOLID_REF, a as firstHeading, at as evaluate, b as detectClaudeMdProjectType, bt as evaluateFileSize, c as parseEntry, ct as GIT_ASK, d as EXCLUDE_DIRS, dt as RALPH_SAFE, et as CRITICAL_PATTERNS, f as PROJECT_INDICATORS, ft as SYSTEM_INSTALL, g as loadApexTaskState, gt as PLUGINS_DIR, h as buildApexTaskInjection, ht as FileSizeVerdict, i as firstComment, it as GuardContext, j as GUARDS, k as detectCreationIntent, l as parseBodyDesc, lt as GIT_BLOCKED, m as buildApexTaskContext, mt as matchPatterns, n as missingSeoElements, nt as securityGuard, o as TreeEntry, ot as PolicyContext, p as ApexTaskState, pt as isRalphMode, q as FILE_REDIRECT, r as descFromText, rt as Guard, s as parseEnrichment, st as PolicyResult, t as isHtmlLike, tt as LabeledPattern, u as parseField, ut as PROJECT_INSTALL, v as buildApexInstruction, vt as countFrameworkCodeLines, w as SKILL_TRIGGERS, wt as ProjectType, x as detectRequiredSkills, xt as detectFramework, y as buildClaudeMdContext, yt as countLines, z as PY_MODEL_RE } from "../index-BfMsILmg.mjs";
1
+ import { $ as ASK_PATTERNS, A as FAIL_CLOSED, B as SWIFT_PROTO_RE, C as usesTailwindUtilities, Ct as ModularArchitecture, D as MAX_TOKENS, Dt as isApexCommand, E as MAX_EXA_RESULTS, Et as detectProjectType, F as installGuard, G as CODE_MUTATORS, H as interfaceSeparationGuard, I as GO_DECL_RE, J as SAFE_PREFIXES, K as CODE_REDIRECT, L as JAVA_DECL_RE, M as clearUserGuards, N as registerGuard, O as capVerbosity, Ot as requiredArchSkill, P as runGuards, Q as protectedPathGuard, R as PHP_DECL_RE, S as skillTriggerGate, St as DEV_KEYWORDS, T as frameworkSolidGate, Tt as detectModularArchitecture, U as bashWriteGuard, V as TS_DECL_RE, W as ASK_WRITERS, X as PROTECTED_FRAGMENTS, Y as SESSION_STATE_FRAGMENT, Z as PROTECTED_GIT_RE, _ as DEV_VERBS, _t as SOLID_REF, a as firstHeading, at as evaluate, b as detectClaudeMdProjectType, bt as evaluateFileSize, c as parseEntry, ct as GIT_ASK, d as EXCLUDE_DIRS, dt as RALPH_SAFE, et as CRITICAL_PATTERNS, f as PROJECT_INDICATORS, ft as SYSTEM_INSTALL, g as loadApexTaskState, gt as PLUGINS_DIR, h as buildApexTaskInjection, ht as FileSizeVerdict, i as firstComment, it as GuardContext, j as GUARDS, k as detectCreationIntent, l as parseBodyDesc, lt as GIT_BLOCKED, m as buildApexTaskContext, mt as matchPatterns, n as missingSeoElements, nt as securityGuard, o as TreeEntry, ot as PolicyContext, p as ApexTaskState, pt as isRalphMode, q as FILE_REDIRECT, r as descFromText, rt as Guard, s as parseEnrichment, st as PolicyResult, t as isHtmlLike, tt as LabeledPattern, u as parseField, ut as PROJECT_INSTALL, v as buildApexInstruction, vt as countFrameworkCodeLines, w as SKILL_TRIGGERS, wt as ProjectType, x as detectRequiredSkills, xt as detectFramework, y as buildClaudeMdContext, yt as countLines, z as PY_MODEL_RE } from "../index-BUDzFkeo.mjs";
2
2
  import { a as PRE_AUTH_GATES, c as evaluateApex, i as POST_AUTH_GATES, l as freshnessGate, n as ApexContext, o as brainstormGate, r as ApexGate, s as docConsultedGate, t as APEX_GATES, u as solidReadGate } from "../apex-Wdi1nq_w.mjs";
3
3
  export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, ApexContext, ApexGate, ApexTaskState, CODE_MUTATORS, CODE_REDIRECT, CRITICAL_PATTERNS, DEV_KEYWORDS, DEV_VERBS, EXCLUDE_DIRS, FAIL_CLOSED, FILE_REDIRECT, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, GO_DECL_RE, GUARDS, Guard, GuardContext, JAVA_DECL_RE, LabeledPattern, MAX_EXA_RESULTS, MAX_TOKENS, ModularArchitecture, PHP_DECL_RE, PLUGINS_DIR, POST_AUTH_GATES, PRE_AUTH_GATES, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, type PolicyContext, type PolicyResult, ProjectType, RALPH_SAFE, SAFE_PREFIXES, SESSION_STATE_FRAGMENT, SKILL_TRIGGERS, SOLID_REF, SWIFT_PROTO_RE, SYSTEM_INSTALL, TS_DECL_RE, TreeEntry, bashWriteGuard, brainstormGate, buildApexInstruction, buildApexTaskContext, buildApexTaskInjection, buildClaudeMdContext, capVerbosity, clearUserGuards, countFrameworkCodeLines, countLines, descFromText, detectClaudeMdProjectType, detectCreationIntent, detectFramework, detectModularArchitecture, detectProjectType, detectRequiredSkills, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, firstComment, firstHeading, frameworkSolidGate, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, isHtmlLike, isRalphMode, loadApexTaskState, matchPatterns, missingSeoElements, parseBodyDesc, parseEnrichment, parseEntry, parseField, protectedPathGuard, registerGuard, requiredArchSkill, runGuards, securityGuard, skillTriggerGate, solidReadGate, usesTailwindUtilities };
@@ -1,5 +1,5 @@
1
1
  import { A as detectCreationIntent, B as DEV_KEYWORDS, C as SKILL_TRIGGERS, D as MAX_EXA_RESULTS, F as docConsultedGate, H as detectProjectType, I as evaluateApex, L as freshnessGate, M as POST_AUTH_GATES, N as PRE_AUTH_GATES, O as MAX_TOKENS, P as brainstormGate, R as solidReadGate, S as usesTailwindUtilities, U as isApexCommand, V as detectModularArchitecture, W as requiredArchSkill, a as firstHeading, b as detectRequiredSkills, c as EXCLUDE_DIRS, d as buildApexTaskInjection, f as loadApexTaskState, g as detectClaudeMdProjectType, h as buildClaudeMdContext, i as firstComment, j as APEX_GATES, k as capVerbosity, l as PROJECT_INDICATORS, m as buildApexInstruction, n as missingSeoElements, o as parseEnrichment, p as DEV_VERBS, r as descFromText, s as parseEntry, t as isHtmlLike, u as buildApexTaskContext, v as parseBodyDesc, w as frameworkSolidGate, x as skillTriggerGate, y as parseField, z as detectFramework } from "../validate-DCQ8dkdL.mjs";
2
2
  import { d as countFrameworkCodeLines, f as countLines, l as PLUGINS_DIR, p as evaluateFileSize, u as SOLID_REF } from "../home-state-D0RLWP8J.mjs";
3
- import { A as PROJECT_INSTALL, C as PROTECTED_GIT_RE, D as securityGuard, E as CRITICAL_PATTERNS, M as SYSTEM_INSTALL, N as isRalphMode, O as GIT_ASK, P as matchPatterns, S as PROTECTED_FRAGMENTS, T as ASK_PATTERNS, _ as CODE_MUTATORS, a as registerGuard, b as SAFE_PREFIXES, c as GO_DECL_RE, d as PY_MODEL_RE, f as SWIFT_PROTO_RE, g as ASK_WRITERS, h as bashWriteGuard, i as clearUserGuards, j as RALPH_SAFE, k as GIT_BLOCKED, l as JAVA_DECL_RE, m as interfaceSeparationGuard, n as FAIL_CLOSED, o as runGuards, p as TS_DECL_RE, r as GUARDS, s as installGuard, t as evaluate, u as PHP_DECL_RE, v as CODE_REDIRECT, w as protectedPathGuard, x as SESSION_STATE_FRAGMENT, y as FILE_REDIRECT } from "../evaluate-BgFLlqrs.mjs";
3
+ import { A as PROJECT_INSTALL, C as PROTECTED_GIT_RE, D as securityGuard, E as CRITICAL_PATTERNS, M as SYSTEM_INSTALL, N as isRalphMode, O as GIT_ASK, P as matchPatterns, S as PROTECTED_FRAGMENTS, T as ASK_PATTERNS, _ as CODE_MUTATORS, a as registerGuard, b as SAFE_PREFIXES, c as GO_DECL_RE, d as PY_MODEL_RE, f as SWIFT_PROTO_RE, g as ASK_WRITERS, h as bashWriteGuard, i as clearUserGuards, j as RALPH_SAFE, k as GIT_BLOCKED, l as JAVA_DECL_RE, m as interfaceSeparationGuard, n as FAIL_CLOSED, o as runGuards, p as TS_DECL_RE, r as GUARDS, s as installGuard, t as evaluate, u as PHP_DECL_RE, v as CODE_REDIRECT, w as protectedPathGuard, x as SESSION_STATE_FRAGMENT, y as FILE_REDIRECT } from "../evaluate-PuyQPU36.mjs";
4
4
  import "../policy-la_KkjCS.mjs";
5
5
  export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, CRITICAL_PATTERNS, DEV_KEYWORDS, DEV_VERBS, EXCLUDE_DIRS, FAIL_CLOSED, FILE_REDIRECT, GIT_ASK, GIT_BLOCKED, GO_DECL_RE, GUARDS, JAVA_DECL_RE, MAX_EXA_RESULTS, MAX_TOKENS, PHP_DECL_RE, PLUGINS_DIR, POST_AUTH_GATES, PRE_AUTH_GATES, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, RALPH_SAFE, SAFE_PREFIXES, SESSION_STATE_FRAGMENT, SKILL_TRIGGERS, SOLID_REF, SWIFT_PROTO_RE, SYSTEM_INSTALL, TS_DECL_RE, bashWriteGuard, brainstormGate, buildApexInstruction, buildApexTaskContext, buildApexTaskInjection, buildClaudeMdContext, capVerbosity, clearUserGuards, countFrameworkCodeLines, countLines, descFromText, detectClaudeMdProjectType, detectCreationIntent, detectFramework, detectModularArchitecture, detectProjectType, detectRequiredSkills, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, firstComment, firstHeading, frameworkSolidGate, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, isHtmlLike, isRalphMode, loadApexTaskState, matchPatterns, missingSeoElements, parseBodyDesc, parseEnrichment, parseEntry, parseField, protectedPathGuard, registerGuard, requiredArchSkill, runGuards, securityGuard, skillTriggerGate, solidReadGate, usesTailwindUtilities };
@@ -1,5 +1,5 @@
1
1
  import { t as isCodeFile } from "./project-root-3kk7gCOp.mjs";
2
- import { t as evaluate } from "./evaluate-BgFLlqrs.mjs";
2
+ import { t as evaluate } from "./evaluate-PuyQPU36.mjs";
3
3
  import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
4
4
  import { execSync } from "node:child_process";
5
5
  //#region src/cli/run.ts
@@ -1,6 +1,6 @@
1
1
  import { r as projectLayout } from "../layout-C0jaaCQC.mjs";
2
2
  import { a as sanitizeSessionId, c as sessionsDir, i as loadSessionState, n as fuseHarnessHome, o as saveSessionState, r as fusengineCache, s as sessionStatePath, t as claudeHome } from "../home-state-D0RLWP8J.mjs";
3
- import { $ as postEditTypescript, A as trackSkillRead, At as trackFile, B as isProject, C as seoPostToolUse, Ct as gitContext, D as postTrackingSideEffects, Dt as taskContext, E as securityAdvisory, Et as promptSubmitContext, F as dispatchLessons, Ft as securityStateDir, G as getFileDesc, H as loadEnriched, I as cartoSessionStart, It as securityStatePath, K as listChildren, L as generateEcosystemMap, Lt as todayUtc, M as dispatchLifecycle, Mt as isoUtc, N as aipilotPostToolUse, Nt as loadSecurityState, O as trackWatchResearch, Ot as defaultStateDir, P as dispatchAipilot, Pt as saveSecurityState, Q as lessonsStateFileFor, R as writePluginMap, S as postEditContext, St as devContext, T as dispatchMemory, Tt as claudeMdKey, U as mergeLines, V as writeTree, W as countFiles, X as lessonsArchiveFileFor, Z as lessonsFileFor, _ as preCommitGate, _t as sessionStartCore, a as recordActivity, at as validateTeammateOutput, b as extractSymbols, bt as removeOldFiles, c as MCP_TTL_MS, ct as validateTailwind, d as isMcpTool, dt as countLoc, et as trackSessionChanges, f as queryOf, ft as detectSolidProfile, g as gate, gt as runSessionStartCleanups, h as TRIVIAL_BUDGET, ht as readRules, i as respond, it as logToolFailure, j as trackEnrichment, jt as normalizeEvent, k as trackMcpResearch, kt as projectHash, l as WEBFETCH_TTL_MS, lt as validateSolidGate, m as REQUIRED_AGENTS, mt as injectRules, n as activityFor, nt as cleanupSession, o as mcpPostStore, ot as trackAgentMemory, p as DEFAULT_WINDOW_MS, pt as solidDetectStart, r as handlePre, rt as saveApexState, s as mcpPreIntercept, st as subagentCacheContext, t as handleHook, tt as validateRulesLoaded, u as cacheQueryOf, ut as checkFileSize, v as detectDuplication, vt as pruneEmptyDirs, w as seoPostToolUseResponse, wt as projectContext, x as lifecycleStdout, xt as trimLogFile, y as dryGate, yt as purgeTtlTree, z as generateProjectMap } from "../handle-B6yL03we.mjs";
3
+ import { $ as postEditTypescript, A as trackSkillRead, At as trackFile, B as isProject, C as seoPostToolUse, Ct as gitContext, D as postTrackingSideEffects, Dt as taskContext, E as securityAdvisory, Et as promptSubmitContext, F as dispatchLessons, Ft as securityStateDir, G as getFileDesc, H as loadEnriched, I as cartoSessionStart, It as securityStatePath, K as listChildren, L as generateEcosystemMap, Lt as todayUtc, M as dispatchLifecycle, Mt as isoUtc, N as aipilotPostToolUse, Nt as loadSecurityState, O as trackWatchResearch, Ot as defaultStateDir, P as dispatchAipilot, Pt as saveSecurityState, Q as lessonsStateFileFor, R as writePluginMap, S as postEditContext, St as devContext, T as dispatchMemory, Tt as claudeMdKey, U as mergeLines, V as writeTree, W as countFiles, X as lessonsArchiveFileFor, Z as lessonsFileFor, _ as preCommitGate, _t as sessionStartCore, a as recordActivity, at as validateTeammateOutput, b as extractSymbols, bt as removeOldFiles, c as MCP_TTL_MS, ct as validateTailwind, d as isMcpTool, dt as countLoc, et as trackSessionChanges, f as queryOf, ft as detectSolidProfile, g as gate, gt as runSessionStartCleanups, h as TRIVIAL_BUDGET, ht as readRules, i as respond, it as logToolFailure, j as trackEnrichment, jt as normalizeEvent, k as trackMcpResearch, kt as projectHash, l as WEBFETCH_TTL_MS, lt as validateSolidGate, m as REQUIRED_AGENTS, mt as injectRules, n as activityFor, nt as cleanupSession, o as mcpPostStore, ot as trackAgentMemory, p as DEFAULT_WINDOW_MS, pt as solidDetectStart, r as handlePre, rt as saveApexState, s as mcpPreIntercept, st as subagentCacheContext, t as handleHook, tt as validateRulesLoaded, u as cacheQueryOf, ut as checkFileSize, v as detectDuplication, vt as pruneEmptyDirs, w as seoPostToolUseResponse, wt as projectContext, x as lifecycleStdout, xt as trimLogFile, y as dryGate, yt as purgeTtlTree, z as generateProjectMap } from "../handle-DJovWDAy.mjs";
4
4
  //#region src/runtime/storage.ts
5
5
  /**
6
6
  * The project's single state dir (`<root>/.harness`) — neutral + harness-agnostic,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusengine/harness",
3
- "version": "0.1.59",
3
+ "version": "0.1.61",
4
4
  "description": "Harness-agnostic toolkit for AI coding agents: runtime harness detection (Claude Code, Codex, Cursor, Cline, Gemini, Aider...), pure policy core (env config, project/framework detection, SOLID/file-size limits, APEX freshness, guard patterns, portable prompts), cache, project memory, ref routing, state/locks, statusline, per-harness adapters (Claude/Cursor/Cline/Gemini) and a cli-mode harness-check binary. Bun-native, with a built dist for Node + bundlers.",
5
5
  "type": "module",
6
6
  "module": "src/index.ts",