@fusengine/harness 0.1.13 → 0.1.15

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 (45) hide show
  1. package/README.md +1 -1
  2. package/dist/adapters/claude/index.mjs +1 -1
  3. package/dist/adapters/cline/index.mjs +1 -1
  4. package/dist/adapters/codex/index.mjs +1 -1
  5. package/dist/adapters/cursor/index.mjs +1 -1
  6. package/dist/adapters/gemini/index.mjs +1 -1
  7. package/dist/cache/index.mjs +1 -1
  8. package/dist/{claude-B3aJwu6O.mjs → claude-0DB5pb4d.mjs} +1 -1
  9. package/dist/cli/bin.mjs +6 -4
  10. package/dist/cli/index.mjs +1 -1
  11. package/dist/config/index.d.mts +2 -2
  12. package/dist/config/index.mjs +4 -2
  13. package/dist/config-la_KkjCS.mjs +1 -0
  14. package/dist/{evaluate-DkTgwHNh.mjs → evaluate-urGmc24B.mjs} +17 -10
  15. package/dist/freshness/index.mjs +1 -1
  16. package/dist/{freshness-BK9Xg6oG.mjs → freshness-CezohJHo.mjs} +1 -1
  17. package/dist/{handle-o5np-3T_.mjs → handle-D_cJGRjJ.mjs} +12 -28
  18. package/dist/{index-CQOPshSM.d.mts → index-BKSKTgnV.d.mts} +1 -5
  19. package/dist/index-C8dk1Alr.d.mts +70 -0
  20. package/dist/{index-BEM-mQMC.d.mts → index-DLYhervv.d.mts} +3 -1
  21. package/dist/index.d.mts +4 -4
  22. package/dist/index.mjs +10 -9
  23. package/dist/init/index.mjs +1 -1
  24. package/dist/layout-C0jaaCQC.mjs +30 -0
  25. package/dist/memory/index.d.mts +2 -2
  26. package/dist/memory/index.mjs +2 -2
  27. package/dist/{memory-BVNt4Ary.mjs → memory-BkoEbdec.mjs} +8 -3
  28. package/dist/policy/index.d.mts +2 -2
  29. package/dist/policy/index.mjs +3 -3
  30. package/dist/{run-D2na7Z2Z.mjs → run-CsWHdBJp.mjs} +2 -2
  31. package/dist/{run-Cdp2Ef9B.mjs → run-D91N4ul1.mjs} +7 -2
  32. package/dist/runtime/index.d.mts +8 -8
  33. package/dist/runtime/index.mjs +12 -2
  34. package/dist/state/index.mjs +1 -1
  35. package/dist/{state-Bc4wdnCG.mjs → state-Cs0Y0MG_.mjs} +1 -1
  36. package/dist/{store-gtbP-Bv9.mjs → store-BnHpq2ZB.mjs} +1 -1
  37. package/dist/tracking/index.mjs +1 -1
  38. package/dist/util/index.mjs +1 -1
  39. package/package.json +1 -1
  40. package/dist/index-B-z0CCiU.d.mts +0 -37
  41. package/dist/{json-io-RH82El2J.mjs → json-io-xpTDuvtn.mjs} +1 -1
  42. package/dist/{policy-Djmqxow2.mjs → policy-Cpa5aWek.mjs} +1 -1
  43. package/dist/{project-root-C4ks_q1G.mjs → project-root-ff0_poWU.mjs} +1 -1
  44. package/dist/{store-BlaEhjab.mjs → store-DeIsfMg5.mjs} +1 -1
  45. /package/dist/{config-BG55s6HZ.mjs → ttl-BG55s6HZ.mjs} +0 -0
package/README.md CHANGED
@@ -34,7 +34,7 @@ That's it. `init` writes the wiring file for the detected harness
34
34
  `.gemini/settings.json`, or `.clinerules/hooks/PreToolUse`+`PostToolUse`), each
35
35
  pointing at `harness hook <id>`. From then on every tool-use is gated, and the
36
36
  session activity (agents run, docs consulted, refs read) is recorded
37
- automatically under `<harness-dir>/harness/`.
37
+ automatically under `<project>/.harness/` (track, cache, memory).
38
38
 
39
39
  ### CLI
40
40
 
@@ -1,2 +1,2 @@
1
- import { a as readClaudeInput, i as guard, n as denyResponse, o as toClaudeResponse, r as fileSizeGuard, t as contextResponse } from "../../claude-B3aJwu6O.mjs";
1
+ import { a as readClaudeInput, i as guard, n as denyResponse, o as toClaudeResponse, r as fileSizeGuard, t as contextResponse } from "../../claude-0DB5pb4d.mjs";
2
2
  export { contextResponse, denyResponse, fileSizeGuard, guard, readClaudeInput, toClaudeResponse };
@@ -1,4 +1,4 @@
1
- import { t as evaluate } from "../../evaluate-DkTgwHNh.mjs";
1
+ import { t as evaluate } from "../../evaluate-urGmc24B.mjs";
2
2
  import { t as formatPrompt } from "../../types-ernB1Dy3.mjs";
3
3
  //#region src/adapters/cline/index.ts
4
4
  /**
@@ -1,2 +1,2 @@
1
- import { a as readClaudeInput, i as guard, n as denyResponse, t as contextResponse } from "../../claude-B3aJwu6O.mjs";
1
+ import { a as readClaudeInput, i as guard, n as denyResponse, t as contextResponse } from "../../claude-0DB5pb4d.mjs";
2
2
  export { contextResponse, denyResponse, guard, readClaudeInput as readCodexInput };
@@ -1,4 +1,4 @@
1
- import { t as evaluate } from "../../evaluate-DkTgwHNh.mjs";
1
+ import { t as evaluate } from "../../evaluate-urGmc24B.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-DkTgwHNh.mjs";
1
+ import { t as evaluate } from "../../evaluate-urGmc24B.mjs";
2
2
  import { t as formatPrompt } from "../../types-ernB1Dy3.mjs";
3
3
  //#region src/adapters/gemini/index.ts
4
4
  /**
@@ -1,3 +1,3 @@
1
1
  import { a as queryHash, i as jaccardSimilar, n as summarizeIndex, r as compactMarkdown, t as loadIndex } from "../cache-BzbX-ztL.mjs";
2
- import { a as extractText, i as mcpCacheKey, n as cachePath, r as cacheStore, t as cacheLookup } from "../store-BlaEhjab.mjs";
2
+ import { a as extractText, i as mcpCacheKey, n as cachePath, r as cacheStore, t as cacheLookup } from "../store-DeIsfMg5.mjs";
3
3
  export { cacheLookup, cachePath, cacheStore, compactMarkdown, extractText, jaccardSimilar, loadIndex, mcpCacheKey, queryHash, summarizeIndex };
@@ -1,4 +1,4 @@
1
- import { t as evaluate } from "./evaluate-DkTgwHNh.mjs";
1
+ import { t as evaluate } from "./evaluate-urGmc24B.mjs";
2
2
  import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
3
3
  //#region src/adapters/claude/index.ts
4
4
  /**
package/dist/cli/bin.mjs CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ import { r as resolveTtlSec } from "../ttl-BG55s6HZ.mjs";
2
3
  import { t as detectHarness } from "../harness-C8Nxxyn_.mjs";
3
- import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-D2na7Z2Z.mjs";
4
- import { n as writeInitFile, t as initFor } from "../run-Cdp2Ef9B.mjs";
5
- import { t as handleHook } from "../handle-o5np-3T_.mjs";
4
+ import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-CsWHdBJp.mjs";
5
+ import { n as writeInitFile, t as initFor } from "../run-D91N4ul1.mjs";
6
+ import { t as handleHook } from "../handle-D_cJGRjJ.mjs";
6
7
  //#region src/cli/bin.ts
7
8
  /**
8
9
  * harness — CLI for @fusengine/harness.
@@ -27,7 +28,8 @@ if (cmd === "hook") {
27
28
  const outcome = await handleHook(process.argv[3] ?? detectHarness().id, await readStdin(), {
28
29
  now: Date.now(),
29
30
  cwd: process.cwd(),
30
- refsDir: process.env.FUSE_HARNESS_REFS
31
+ refsDir: process.env.FUSE_HARNESS_REFS,
32
+ windowMs: resolveTtlSec(process.env) * 1e3
31
33
  });
32
34
  if (outcome.stdout) process.stdout.write(outcome.stdout);
33
35
  process.exit(outcome.exit);
@@ -1,2 +1,2 @@
1
- import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-D2na7Z2Z.mjs";
1
+ import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-CsWHdBJp.mjs";
2
2
  export { checkStaged, stagedContent, stagedFiles };
@@ -1,2 +1,2 @@
1
- import { a as DEFAULT_TTL_SEC, c as ttlLabel, i as splitTarget, l as parseEnvInt, n as MAX_LINES_ENV_KEY, o as TTL_ENV_KEY, r as resolveMaxLines, s as resolveTtlSec, t as DEFAULT_MAX_LINES } from "../index-B-z0CCiU.mjs";
2
- export { DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, MAX_LINES_ENV_KEY, TTL_ENV_KEY, parseEnvInt, resolveMaxLines, resolveTtlSec, splitTarget, ttlLabel };
1
+ import { a as DEFAULT_MAX_LINES, c as splitTarget, d as resolveTtlSec, f as ttlLabel, i as projectLayout, l as DEFAULT_TTL_SEC, n as STATE_GITIGNORE, o as MAX_LINES_ENV_KEY, p as parseEnvInt, r as STATE_ROOT, s as resolveMaxLines, t as ProjectLayout, u as TTL_ENV_KEY } from "../index-C8dk1Alr.mjs";
2
+ export { DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, MAX_LINES_ENV_KEY, ProjectLayout, STATE_GITIGNORE, STATE_ROOT, TTL_ENV_KEY, parseEnvInt, projectLayout, resolveMaxLines, resolveTtlSec, splitTarget, ttlLabel };
@@ -1,3 +1,5 @@
1
1
  import { a as parseEnvInt, i as splitTarget, n as MAX_LINES_ENV_KEY, r as resolveMaxLines, t as DEFAULT_MAX_LINES } from "../limits-CHn8AIL1.mjs";
2
- import { i as ttlLabel, n as TTL_ENV_KEY, r as resolveTtlSec, t as DEFAULT_TTL_SEC } from "../config-BG55s6HZ.mjs";
3
- export { DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, MAX_LINES_ENV_KEY, TTL_ENV_KEY, parseEnvInt, resolveMaxLines, resolveTtlSec, splitTarget, ttlLabel };
2
+ import { i as ttlLabel, n as TTL_ENV_KEY, r as resolveTtlSec, t as DEFAULT_TTL_SEC } from "../ttl-BG55s6HZ.mjs";
3
+ import { n as STATE_ROOT, r as projectLayout, t as STATE_GITIGNORE } from "../layout-C0jaaCQC.mjs";
4
+ import "../config-la_KkjCS.mjs";
5
+ export { DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, MAX_LINES_ENV_KEY, STATE_GITIGNORE, STATE_ROOT, TTL_ENV_KEY, parseEnvInt, projectLayout, resolveMaxLines, resolveTtlSec, splitTarget, ttlLabel };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,5 @@
1
1
  import { i as splitTarget, r as resolveMaxLines } from "./limits-CHn8AIL1.mjs";
2
- import { t as isCodeFile } from "./project-root-C4ks_q1G.mjs";
2
+ import { t as isCodeFile } from "./project-root-ff0_poWU.mjs";
3
3
  //#region src/policy/detect-framework.ts
4
4
  /**
5
5
  * Detect the framework from a file path extension + content patterns.
@@ -82,7 +82,9 @@ const SYSTEM_INSTALL = [
82
82
  /brew upgrade/,
83
83
  /brew cask/,
84
84
  /apt install/,
85
- /apt-get install/
85
+ /apt-get install/,
86
+ /dnf install/,
87
+ /pacman -S/
86
88
  ];
87
89
  /** Project-level package installs. */
88
90
  const PROJECT_INSTALL = [
@@ -228,18 +230,13 @@ function interfaceSeparationGuard(ctx) {
228
230
  }
229
231
  //#endregion
230
232
  //#region src/policy/guards/install.ts
231
- /** Project-level dependency installs (npm/yarn/pnpm/bun/pip/cargo/go/gem/composer). */
232
- const PROJECT_INSTALL_RE = /\b(?:npm\s+(?:install|i)|(?:yarn|pnpm|bun)\s+add|pip3?\s+install|cargo\s+install|go\s+install|gem\s+install|composer\s+require)\b/;
233
- /** System-level package installs (brew/apt/apt-get/dnf/pacman). */
234
- const SYSTEM_INSTALL_RE = /\b(?:brew\s+install|apt(?:-get)?\s+install|dnf\s+install|pacman\s+-S)\b/;
235
233
  /** Asks for confirmation before a dependency or system package install. */
236
234
  function installGuard(ctx) {
237
235
  if (ctx.tool !== "Bash" || !ctx.command) return null;
238
- const cmd = ctx.command;
239
- if (PROJECT_INSTALL_RE.test(cmd) || SYSTEM_INSTALL_RE.test(cmd)) return {
236
+ if (matchPatterns(ctx.command, PROJECT_INSTALL) || matchPatterns(ctx.command, SYSTEM_INSTALL)) return {
240
237
  kind: "ask",
241
238
  title: "Dependency install",
242
- reason: `This command installs packages: ${cmd.trim()}`,
239
+ reason: `This command installs packages: ${ctx.command.trim()}`,
243
240
  actions: ["Confirm this install is intended"]
244
241
  };
245
242
  return null;
@@ -289,6 +286,16 @@ function evaluate(ctx) {
289
286
  }
290
287
  };
291
288
  }
289
+ if (ctx.command && matchPatterns(ctx.command, GIT_ASK)) return {
290
+ decision: "deny",
291
+ message: `Git operation requires confirmation: ${ctx.command}`,
292
+ prompt: {
293
+ kind: "ask",
294
+ title: "Confirm git operation",
295
+ reason: `Authorize: ${ctx.command.trim()}`,
296
+ actions: ["Approve if this git operation is intended"]
297
+ }
298
+ };
292
299
  if (ctx.filePath && isCodeFile(ctx.filePath) && ctx.content !== void 0) {
293
300
  const verdict = evaluateFileSize(countLines(ctx.content), ctx.maxLines);
294
301
  if (!verdict.ok) return {
@@ -313,4 +320,4 @@ function evaluate(ctx) {
313
320
  };
314
321
  }
315
322
  //#endregion
316
- export { PROJECT_INSTALL as C, evaluateFileSize as D, countLines as E, detectFramework as O, GIT_BLOCKED as S, matchPatterns as T, protectedPathGuard as _, SYSTEM_INSTALL_RE as a, securityGuard as b, PY_MODEL_RE as c, interfaceSeparationGuard as d, ASK_WRITERS as f, PROTECTED_FRAGMENTS as g, bashWriteGuard as h, PROJECT_INSTALL_RE as i, SWIFT_PROTO_RE as l, CODE_REDIRECT as m, GUARDS as n, installGuard as o, CODE_MUTATORS as p, runGuards as r, PHP_DECL_RE as s, evaluate as t, TS_DECL_RE as u, ASK_PATTERNS as v, SYSTEM_INSTALL as w, GIT_ASK as x, CRITICAL_PATTERNS as y };
323
+ export { matchPatterns as C, detectFramework as E, SYSTEM_INSTALL as S, evaluateFileSize as T, CRITICAL_PATTERNS as _, PHP_DECL_RE as a, GIT_BLOCKED as b, TS_DECL_RE as c, CODE_MUTATORS as d, CODE_REDIRECT as f, ASK_PATTERNS as g, protectedPathGuard as h, installGuard as i, interfaceSeparationGuard as l, PROTECTED_FRAGMENTS as m, GUARDS as n, PY_MODEL_RE as o, bashWriteGuard as p, runGuards as r, SWIFT_PROTO_RE as s, evaluate as t, ASK_WRITERS as u, securityGuard as v, countLines as w, PROJECT_INSTALL as x, GIT_ASK as y };
@@ -1,3 +1,3 @@
1
1
  import { i as resolveSessions, n as formatDocSatisfactionStatus, r as isDocConsulted, t as formatDocDeny } from "../doc-helpers-Dd_x1-tZ.mjs";
2
- import { t as incrementTrivialEditCounter } from "../freshness-BK9Xg6oG.mjs";
2
+ import { t as incrementTrivialEditCounter } from "../freshness-CezohJHo.mjs";
3
3
  export { formatDocDeny, formatDocSatisfactionStatus, incrementTrivialEditCounter, isDocConsulted, resolveSessions };
@@ -1,4 +1,4 @@
1
- import { n as readJsonFile, r as writeJsonFile, t as ensureDir } from "./json-io-RH82El2J.mjs";
1
+ import { n as readJsonFile, r as writeJsonFile, t as ensureDir } from "./json-io-xpTDuvtn.mjs";
2
2
  import { dirname } from "node:path";
3
3
  //#region src/freshness/trivial-edit-counter.ts
4
4
  /**
@@ -1,9 +1,10 @@
1
- import { O as detectFramework, t as evaluate } from "./evaluate-DkTgwHNh.mjs";
1
+ import { r as projectLayout } from "./layout-C0jaaCQC.mjs";
2
+ import { E as detectFramework, t as evaluate } from "./evaluate-urGmc24B.mjs";
2
3
  import { n as capVerbosity, o as evaluateApex } from "./verbosity-ZYT7tLCw.mjs";
3
4
  import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
4
- import { a as extractText, r as cacheStore, t as cacheLookup } from "./store-BlaEhjab.mjs";
5
+ import { a as extractText, r as cacheStore, t as cacheLookup } from "./store-DeIsfMg5.mjs";
5
6
  import { t as loadRefs } from "./loader-BephwI8n.mjs";
6
- import { a as recordAgent, c as recordRefRead, l as recordTrivialEdit, n as saveTrack, r as agentsFresh, s as recordDoc, t as loadTrack, u as trivialCount } from "./store-gtbP-Bv9.mjs";
7
+ import { a as recordAgent, c as recordRefRead, l as recordTrivialEdit, n as saveTrack, r as agentsFresh, s as recordDoc, t as loadTrack, u as trivialCount } from "./store-BnHpq2ZB.mjs";
7
8
  import { join } from "node:path";
8
9
  import { tmpdir } from "node:os";
9
10
  //#region src/runtime/activity.ts
@@ -48,8 +49,8 @@ function activityFor(event) {
48
49
  //#region src/runtime/gate.ts
49
50
  /** Prior agents the freshness gate requires before a code edit. */
50
51
  const REQUIRED_AGENTS = ["explore-codebase", "research-expert"];
51
- /** Default freshness window for {@link REQUIRED_AGENTS} (4 min, the APEX TTL). */
52
- const DEFAULT_WINDOW_MS = 24e4;
52
+ /** Default freshness window for {@link REQUIRED_AGENTS} (2 min — matches the plugin's `FUSE_ENFORCE_TTL_SEC` default). */
53
+ const DEFAULT_WINDOW_MS = 12e4;
53
54
  /** Trivial edits allowed within the window before the full APEX gates apply. */
54
55
  const TRIVIAL_BUDGET = 4;
55
56
  /**
@@ -66,7 +67,7 @@ async function gate(input) {
66
67
  });
67
68
  if (quick.decision !== "allow" && quick.prompt) return quick.prompt;
68
69
  if (!input.filePath) return null;
69
- const window = input.windowMs ?? 24e4;
70
+ const window = input.windowMs ?? 12e4;
70
71
  const track = await loadTrack(input.trackFile);
71
72
  const lineCount = input.content === void 0 ? Number.POSITIVE_INFINITY : input.content.split("\n").length;
72
73
  if (!input.isReplaceAll && lineCount < 5 && trivialCount(track, window, input.now) < 4) {
@@ -229,24 +230,6 @@ function respond(id, prompt) {
229
230
  }
230
231
  }
231
232
  //#endregion
232
- //#region src/runtime/storage.ts
233
- /** Per-harness config dir (relative to the project root) where state lives. */
234
- const STATE_DIR = {
235
- "claude-code": ".claude",
236
- codex: ".codex",
237
- cursor: ".cursor",
238
- "gemini-cli": ".gemini",
239
- cline: ".clinerules"
240
- };
241
- /**
242
- * Directory where a harness's fuse-harness track files live — under that
243
- * harness's own config dir (`.claude`, `.codex`, …) so state sits next to its
244
- * hooks. Falls back to `.fuse-harness` for harnesses without a known config dir.
245
- */
246
- function harnessTrackDir(id, projectRoot) {
247
- return join(projectRoot, STATE_DIR[id] ?? ".fuse-harness", "harness");
248
- }
249
- //#endregion
250
233
  //#region src/runtime/handle.ts
251
234
  /**
252
235
  * The full hook handler: on a PRE event it gates the tool-use (stateless guards
@@ -256,9 +239,9 @@ function harnessTrackDir(id, projectRoot) {
256
239
  */
257
240
  async function handleHook(id, payload, opts) {
258
241
  const event = normalizeEvent(id, payload);
259
- const dir = harnessTrackDir(id, opts.cwd);
260
- const file = trackFile(event.sessionId, dir);
261
- const mcpDir = join(dir, "mcp");
242
+ const layout = projectLayout(opts.cwd);
243
+ const file = trackFile(event.sessionId, layout.trackDir);
244
+ const mcpDir = layout.cacheDir;
262
245
  const framework = detectFramework(event.filePath ?? "", event.content ?? "");
263
246
  if (event.phase === "post") {
264
247
  mcpPostStore(event.tool, event.input, payload.tool_response ?? payload.tool_output, mcpDir);
@@ -289,6 +272,7 @@ async function handleHook(id, payload, opts) {
289
272
  command: event.command,
290
273
  refs: opts.refsDir ? await loadRefs(opts.refsDir) : void 0,
291
274
  isReplaceAll: event.input.replace_all === true,
275
+ windowMs: opts.windowMs,
292
276
  now: opts.now,
293
277
  trackFile: file
294
278
  });
@@ -301,4 +285,4 @@ async function handleHook(id, payload, opts) {
301
285
  };
302
286
  }
303
287
  //#endregion
304
- export { trackFile as a, isMcpTool as c, queryOf as d, DEFAULT_WINDOW_MS as f, activityFor as g, gate as h, recordActivity as i, mcpPostStore as l, TRIVIAL_BUDGET as m, harnessTrackDir as n, normalizeEvent as o, REQUIRED_AGENTS as p, respond as r, MCP_TTL_MS as s, handleHook as t, mcpPreIntercept as u };
288
+ export { normalizeEvent as a, mcpPostStore as c, DEFAULT_WINDOW_MS as d, REQUIRED_AGENTS as f, activityFor as h, trackFile as i, mcpPreIntercept as l, gate as m, respond as n, MCP_TTL_MS as o, TRIVIAL_BUDGET as p, recordActivity as r, isMcpTool as s, handleHook as t, queryOf as u };
@@ -177,10 +177,6 @@ declare const SWIFT_PROTO_RE: RegExp;
177
177
  declare function interfaceSeparationGuard(ctx: GuardContext): Prompt | null;
178
178
  //#endregion
179
179
  //#region src/policy/guards/install.d.ts
180
- /** Project-level dependency installs (npm/yarn/pnpm/bun/pip/cargo/go/gem/composer). */
181
- declare const PROJECT_INSTALL_RE: RegExp;
182
- /** System-level package installs (brew/apt/apt-get/dnf/pacman). */
183
- declare const SYSTEM_INSTALL_RE: RegExp;
184
180
  /** Asks for confirmation before a dependency or system package install. */
185
181
  declare function installGuard(ctx: GuardContext): Prompt | null;
186
182
  //#endregion
@@ -207,4 +203,4 @@ declare const MAX_EXA_RESULTS = 3;
207
203
  */
208
204
  declare function capVerbosity(tool: string, input: Record<string, unknown>): Record<string, unknown> | null;
209
205
  //#endregion
210
- export { evaluateApex as A, matchPatterns as B, Guard as C, ApexGate as D, ApexContext as E, evaluate as F, DEV_KEYWORDS as G, countLines as H, GIT_ASK as I, isApexCommand as J, ProjectType as K, GIT_BLOCKED as L, solidReadGate as M, PolicyContext as N, brainstormGate as O, PolicyResult as P, PROJECT_INSTALL as R, securityGuard as S, APEX_GATES as T, evaluateFileSize as U, FileSizeVerdict as V, detectFramework as W, bashWriteGuard as _, runGuards as a, ASK_PATTERNS as b, installGuard as c, SWIFT_PROTO_RE as d, TS_DECL_RE as f, CODE_REDIRECT as g, CODE_MUTATORS as h, GUARDS as i, freshnessGate as j, docConsultedGate as k, PHP_DECL_RE as l, ASK_WRITERS as m, capVerbosity as n, PROJECT_INSTALL_RE as o, interfaceSeparationGuard as p, detectProjectType as q, detectCreationIntent as r, SYSTEM_INSTALL_RE as s, MAX_EXA_RESULTS as t, PY_MODEL_RE as u, PROTECTED_FRAGMENTS as v, GuardContext as w, CRITICAL_PATTERNS as x, protectedPathGuard as y, SYSTEM_INSTALL as z };
206
+ export { solidReadGate as A, countLines as B, APEX_GATES as C, docConsultedGate as D, brainstormGate as E, GIT_BLOCKED as F, detectProjectType as G, detectFramework as H, PROJECT_INSTALL as I, isApexCommand as K, SYSTEM_INSTALL as L, PolicyResult as M, evaluate as N, evaluateApex as O, GIT_ASK as P, matchPatterns as R, GuardContext as S, ApexGate as T, DEV_KEYWORDS as U, evaluateFileSize as V, ProjectType as W, protectedPathGuard as _, runGuards as a, securityGuard as b, PY_MODEL_RE as c, interfaceSeparationGuard as d, ASK_WRITERS as f, PROTECTED_FRAGMENTS as g, bashWriteGuard as h, GUARDS as i, PolicyContext as j, freshnessGate as k, SWIFT_PROTO_RE as l, CODE_REDIRECT as m, capVerbosity as n, installGuard as o, CODE_MUTATORS as p, detectCreationIntent as r, PHP_DECL_RE as s, MAX_EXA_RESULTS as t, TS_DECL_RE as u, ASK_PATTERNS as v, ApexContext as w, Guard as x, CRITICAL_PATTERNS as y, FileSizeVerdict as z };
@@ -0,0 +1,70 @@
1
+ //#region src/config/env.d.ts
2
+ /**
3
+ * Robust integer-from-env parser.
4
+ * undefined / empty / whitespace / NaN / float / <= 0 all fall back to `fallback`.
5
+ * `Number("")` is 0, so the empty guard is required before `Number()`.
6
+ */
7
+ declare function parseEnvInt(raw: string | undefined, fallback: number): number;
8
+ //#endregion
9
+ //#region src/config/ttl.d.ts
10
+ /** Default enforcement-freshness window, in seconds (2 minutes). */
11
+ declare const DEFAULT_TTL_SEC = 120;
12
+ /** Default env var name carrying the TTL override. */
13
+ declare const TTL_ENV_KEY = "FUSE_ENFORCE_TTL_SEC";
14
+ /**
15
+ * Resolve the enforcement TTL (seconds) from an env map.
16
+ * @param env - environment map (defaults to `process.env`)
17
+ * @param key - env var name (defaults to `FUSE_ENFORCE_TTL_SEC`)
18
+ */
19
+ declare function resolveTtlSec(env?: Record<string, string | undefined>, key?: string): number;
20
+ /** Human label for a TTL: 120 -> "2min", 240 -> "4min", 90 -> "90s". */
21
+ declare function ttlLabel(sec: number): string;
22
+ //#endregion
23
+ //#region src/config/limits.d.ts
24
+ /** Default SOLID max lines per file. */
25
+ declare const DEFAULT_MAX_LINES = 100;
26
+ /** Default env var name carrying the max-lines override. */
27
+ declare const MAX_LINES_ENV_KEY = "FUSE_SOLID_MAX_LINES";
28
+ /**
29
+ * Resolve the SOLID max-lines limit from an env map.
30
+ * @param env - environment map (defaults to `process.env`)
31
+ * @param key - env var name (defaults to `FUSE_SOLID_MAX_LINES`)
32
+ */
33
+ declare function resolveMaxLines(env?: Record<string, string | undefined>, key?: string): number;
34
+ /** Advisory module-split headroom = `maxLines - 10` (never below 1). */
35
+ declare function splitTarget(maxLines: number): number;
36
+ //#endregion
37
+ //#region src/config/layout.d.ts
38
+ /** Top-level project state dir — neutral + harness-agnostic (not `.claude/…`). */
39
+ declare const STATE_ROOT = ".harness";
40
+ /**
41
+ * The selective `.gitignore` for `.harness/`: ignore the machine-local session
42
+ * state (track, cache, throttle) but keep the curated `memory/LESSON.md`.
43
+ */
44
+ declare const STATE_GITIGNORE = "track/\ncache/\nmemory/state.json\n";
45
+ /** Every project-state path the package uses, derived from one project root. */
46
+ interface ProjectLayout {
47
+ root: string;
48
+ /** `<root>/.harness` — the single state dir. */
49
+ stateDir: string;
50
+ /** `<root>/.harness/track` — session track files. */
51
+ trackDir: string;
52
+ /** `<root>/.harness/cache` — MCP/WebFetch cache. */
53
+ cacheDir: string;
54
+ /** `<root>/.harness/memory` — lessons + throttle. */
55
+ memoryDir: string;
56
+ /** `<root>/.harness/memory/LESSON.md` — curated, committable. */
57
+ lessonsFile: string;
58
+ /** `<root>/.harness/memory/state.json` — machine-local throttle. */
59
+ memoryStateFile: string;
60
+ /** `<root>/.harness/.gitignore`. */
61
+ gitignoreFile: string;
62
+ }
63
+ /**
64
+ * The single source of truth for where every package file lives under a project
65
+ * root. All modules (runtime storage, memory, init) derive their paths here —
66
+ * nothing hardcodes a directory name elsewhere.
67
+ */
68
+ declare function projectLayout(root: string): ProjectLayout;
69
+ //#endregion
70
+ export { DEFAULT_MAX_LINES as a, splitTarget as c, resolveTtlSec as d, ttlLabel as f, projectLayout as i, DEFAULT_TTL_SEC as l, STATE_GITIGNORE as n, MAX_LINES_ENV_KEY as o, parseEnvInt as p, STATE_ROOT as r, resolveMaxLines as s, ProjectLayout as t, TTL_ENV_KEY as u };
@@ -6,6 +6,8 @@ interface ReminderState {
6
6
  }
7
7
  /** Absolute path of the per-project throttle state file. */
8
8
  declare function stateFileFor(root: string): string;
9
+ /** Absolute path of the per-project curated lessons file (committable). */
10
+ declare function lessonsFileFor(root: string): string;
9
11
  /** Read both throttle timestamps; missing/corrupt fields default to 0. */
10
12
  declare function readState(file: string): ReminderState;
11
13
  /** Persist one field without clobbering the other (read-modify-write). */
@@ -27,4 +29,4 @@ declare function addRoot(root: string, home?: string): void;
27
29
  /** Ensure `<memoryDir>/.gitignore` ignores the machine-local `state.json`. */
28
30
  declare function ensureMemoryGitignore(memoryDir: string): void;
29
31
  //#endregion
30
- export { ReminderState as a, setStateField as c, registryFile as i, stateFileFor as l, addRoot as n, nowStamp as o, readRoots as r, readState as s, ensureMemoryGitignore as t, throttleMs as u };
32
+ export { ReminderState as a, readState as c, throttleMs as d, registryFile as i, setStateField as l, addRoot as n, lessonsFileFor as o, readRoots as r, nowStamp as s, ensureMemoryGitignore as t, stateFileFor as u };
package/dist/index.d.mts CHANGED
@@ -1,14 +1,14 @@
1
1
  import { n as PromptKind, r as formatPrompt, t as Prompt } from "./types-D56jSgD9.mjs";
2
2
  import { a as extractText, c as summarizeIndex, d as queryHash, i as mcpCacheKey, l as compactMarkdown, n as cachePath, o as IndexSummary, r as cacheStore, s as loadIndex, t as cacheLookup, u as jaccardSimilar } from "./index-DPkCX_AR.mjs";
3
- import { a as DEFAULT_TTL_SEC, c as ttlLabel, i as splitTarget, l as parseEnvInt, n as MAX_LINES_ENV_KEY, o as TTL_ENV_KEY, r as resolveMaxLines, s as resolveTtlSec, t as DEFAULT_MAX_LINES } from "./index-B-z0CCiU.mjs";
3
+ import { a as DEFAULT_MAX_LINES, c as splitTarget, d as resolveTtlSec, f as ttlLabel, i as projectLayout, l as DEFAULT_TTL_SEC, n as STATE_GITIGNORE, o as MAX_LINES_ENV_KEY, p as parseEnvInt, r as STATE_ROOT, s as resolveMaxLines, t as ProjectLayout, u as TTL_ENV_KEY } from "./index-C8dk1Alr.mjs";
4
4
  import { a as detectHarness, i as HarnessVia, n as HarnessInfo, o as detectMode, r as HarnessMode, s as modeFor, t as HarnessId } from "./harness-DwJskkz_.mjs";
5
5
  import { a as isDocConsulted, i as formatDocSatisfactionStatus, n as DocSatisfactionStatus, o as resolveSessions, r as formatDocDeny, t as AuthEntry } from "./doc-helpers-CG1nuf-c.mjs";
6
6
  import { t as incrementTrivialEditCounter } from "./index-BOBXQ91y.mjs";
7
7
  import { i as compactJson, n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./index-C1vLIMwN.mjs";
8
- import { A as evaluateApex, B as matchPatterns, C as Guard, D as ApexGate, E as ApexContext, F as evaluate, G as DEV_KEYWORDS, H as countLines, I as GIT_ASK, J as isApexCommand, K as ProjectType, L as GIT_BLOCKED, M as solidReadGate, N as PolicyContext, O as brainstormGate, P as PolicyResult, R as PROJECT_INSTALL, S as securityGuard, T as APEX_GATES, U as evaluateFileSize, V as FileSizeVerdict, W as detectFramework, _ as bashWriteGuard, a as runGuards, b as ASK_PATTERNS, c as installGuard, d as SWIFT_PROTO_RE, f as TS_DECL_RE, g as CODE_REDIRECT, h as CODE_MUTATORS, i as GUARDS, j as freshnessGate, k as docConsultedGate, l as PHP_DECL_RE, m as ASK_WRITERS, n as capVerbosity, o as PROJECT_INSTALL_RE, p as interfaceSeparationGuard, q as detectProjectType, r as detectCreationIntent, s as SYSTEM_INSTALL_RE, t as MAX_EXA_RESULTS, u as PY_MODEL_RE, v as PROTECTED_FRAGMENTS, w as GuardContext, x as CRITICAL_PATTERNS, y as protectedPathGuard, z as SYSTEM_INSTALL } from "./index-CQOPshSM.mjs";
8
+ import { A as solidReadGate, B as countLines, C as APEX_GATES, D as docConsultedGate, E as brainstormGate, F as GIT_BLOCKED, G as detectProjectType, H as detectFramework, I as PROJECT_INSTALL, K as isApexCommand, L as SYSTEM_INSTALL, M as PolicyResult, N as evaluate, O as evaluateApex, P as GIT_ASK, R as matchPatterns, S as GuardContext, T as ApexGate, U as DEV_KEYWORDS, V as evaluateFileSize, W as ProjectType, _ as protectedPathGuard, a as runGuards, b as securityGuard, c as PY_MODEL_RE, d as interfaceSeparationGuard, f as ASK_WRITERS, g as PROTECTED_FRAGMENTS, h as bashWriteGuard, i as GUARDS, j as PolicyContext, k as freshnessGate, l as SWIFT_PROTO_RE, m as CODE_REDIRECT, n as capVerbosity, o as installGuard, p as CODE_MUTATORS, r as detectCreationIntent, s as PHP_DECL_RE, t as MAX_EXA_RESULTS, u as TS_DECL_RE, v as ASK_PATTERNS, w as ApexContext, x as Guard, y as CRITICAL_PATTERNS, z as FileSizeVerdict } from "./index-BKSKTgnV.mjs";
9
9
  import { n as RouteResult, r as ScoredRef, t as RefMeta } from "./types-CY5qT2X1.mjs";
10
- import { a as ReminderState, c as setStateField, i as registryFile, l as stateFileFor, n as addRoot, o as nowStamp, r as readRoots, s as readState, t as ensureMemoryGitignore, u as throttleMs } from "./index-BEM-mQMC.mjs";
10
+ 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";
11
11
  import { a as globToRe, i as scoreReferences, n as toRefMeta, o as parseFrontmatter, r as routeReferences, t as loadRefs } from "./index-hL_r6tlc.mjs";
12
12
  import { a as taskStart, c as ensureStateDir, d as stateFilePath, f as acquireLock, i as taskCreate, l as loadState, n as ApexTaskFile, o as ApexState, r as taskComplete, s as apexStateDir, t as ApexTask, u as saveState } from "./index-CPoF_hLP.mjs";
13
13
  import { _ as TIME_INTERVALS, a as formatCost, c as formatTokens, d as colors, f as progressiveColor, g as PROGRESS_CHARS, h as PROGRESS_BAR_DEFAULTS, i as formatBasename, l as ColorFn, m as GRADIENT_BLOCKS, n as generateGradientBar, o as formatPath, p as COLOR_THRESHOLDS, r as generateProgressBar, s as formatTimeLeft, t as ProgressBarOptions, u as Palette } from "./index-BWK8slRi.mjs";
14
- export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, ApexContext, ApexGate, ApexState, ApexTask, ApexTaskFile, AuthEntry, CODE_MUTATORS, CODE_REDIRECT, COLOR_THRESHOLDS, CRITICAL_PATTERNS, ColorFn, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, DocSatisfactionStatus, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, GRADIENT_BLOCKS, GUARDS, Guard, GuardContext, HarnessId, HarnessInfo, HarnessMode, HarnessVia, IndexSummary, MAX_EXA_RESULTS, MAX_LINES_ENV_KEY, PHP_DECL_RE, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INSTALL, PROJECT_INSTALL_RE, PROTECTED_FRAGMENTS, PY_MODEL_RE, Palette, PolicyContext, PolicyResult, ProgressBarOptions, ProjectType, Prompt, PromptKind, RefMeta, ReminderState, RouteResult, SWIFT_PROTO_RE, SYSTEM_INSTALL, SYSTEM_INSTALL_RE, ScoredRef, TIME_INTERVALS, TS_DECL_RE, TTL_ENV_KEY, acquireLock, addRoot, apexStateDir, bashWriteGuard, brainstormGate, cacheLookup, cachePath, cacheStore, capVerbosity, colors, compactJson, compactMarkdown, countLines, detectCreationIntent, detectFramework, detectHarness, detectMode, detectProjectType, docConsultedGate, ensureMemoryGitignore, ensureStateDir, evaluate, evaluateApex, evaluateFileSize, extractText, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, installGuard, interfaceSeparationGuard, isApexCommand, isCodeFile, isDocConsulted, jaccardSimilar, loadIndex, loadRefs, loadState, matchPatterns, mcpCacheKey, modeFor, nowStamp, parseEnvInt, parseFrontmatter, progressiveColor, projectRoot, projectRootOrNull, protectedPathGuard, queryHash, readRoots, readState, registryFile, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, runGuards, saveState, scoreReferences, securityGuard, setStateField, solidReadGate, splitTarget, stateFileFor, stateFilePath, summarizeIndex, taskComplete, taskCreate, taskStart, throttleMs, toRefMeta, ttlLabel };
14
+ export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, ApexContext, ApexGate, ApexState, ApexTask, ApexTaskFile, AuthEntry, CODE_MUTATORS, CODE_REDIRECT, COLOR_THRESHOLDS, CRITICAL_PATTERNS, ColorFn, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, DocSatisfactionStatus, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, GRADIENT_BLOCKS, GUARDS, Guard, GuardContext, HarnessId, HarnessInfo, HarnessMode, HarnessVia, IndexSummary, MAX_EXA_RESULTS, MAX_LINES_ENV_KEY, PHP_DECL_RE, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PY_MODEL_RE, Palette, PolicyContext, PolicyResult, ProgressBarOptions, ProjectLayout, ProjectType, Prompt, PromptKind, RefMeta, ReminderState, RouteResult, STATE_GITIGNORE, STATE_ROOT, SWIFT_PROTO_RE, SYSTEM_INSTALL, ScoredRef, TIME_INTERVALS, TS_DECL_RE, TTL_ENV_KEY, acquireLock, addRoot, apexStateDir, bashWriteGuard, brainstormGate, cacheLookup, cachePath, cacheStore, capVerbosity, colors, compactJson, compactMarkdown, countLines, detectCreationIntent, detectFramework, detectHarness, detectMode, detectProjectType, docConsultedGate, ensureMemoryGitignore, ensureStateDir, evaluate, evaluateApex, evaluateFileSize, extractText, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, installGuard, interfaceSeparationGuard, isApexCommand, isCodeFile, isDocConsulted, jaccardSimilar, lessonsFileFor, loadIndex, loadRefs, loadState, matchPatterns, mcpCacheKey, modeFor, nowStamp, parseEnvInt, parseFrontmatter, progressiveColor, projectLayout, projectRoot, projectRootOrNull, protectedPathGuard, queryHash, readRoots, readState, registryFile, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, runGuards, saveState, scoreReferences, securityGuard, setStateField, solidReadGate, splitTarget, stateFileFor, stateFilePath, summarizeIndex, taskComplete, taskCreate, taskStart, throttleMs, toRefMeta, ttlLabel };
package/dist/index.mjs CHANGED
@@ -1,19 +1,20 @@
1
1
  import { a as parseEnvInt, i as splitTarget, n as MAX_LINES_ENV_KEY, r as resolveMaxLines, t as DEFAULT_MAX_LINES } from "./limits-CHn8AIL1.mjs";
2
- import { i as ttlLabel, n as TTL_ENV_KEY, r as resolveTtlSec, t as DEFAULT_TTL_SEC } from "./config-BG55s6HZ.mjs";
2
+ import { i as ttlLabel, n as TTL_ENV_KEY, r as resolveTtlSec, t as DEFAULT_TTL_SEC } from "./ttl-BG55s6HZ.mjs";
3
+ import { n as STATE_ROOT, r as projectLayout, t as STATE_GITIGNORE } from "./layout-C0jaaCQC.mjs";
3
4
  import { t as compactJson } from "./compact-json-DK2nX-MK.mjs";
4
- import { n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./project-root-C4ks_q1G.mjs";
5
+ import { n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./project-root-ff0_poWU.mjs";
5
6
  import { n as detectMode, r as modeFor, t as detectHarness } from "./harness-C8Nxxyn_.mjs";
6
- import { i as isApexCommand, n as DEV_KEYWORDS, r as detectProjectType, t as detectCreationIntent } from "./policy-Djmqxow2.mjs";
7
- import { C as PROJECT_INSTALL, D as evaluateFileSize, E as countLines, O as detectFramework, S as GIT_BLOCKED, T as matchPatterns, _ as protectedPathGuard, a as SYSTEM_INSTALL_RE, b as securityGuard, c as PY_MODEL_RE, d as interfaceSeparationGuard, f as ASK_WRITERS, g as PROTECTED_FRAGMENTS, h as bashWriteGuard, i as PROJECT_INSTALL_RE, l as SWIFT_PROTO_RE, m as CODE_REDIRECT, n as GUARDS, o as installGuard, p as CODE_MUTATORS, r as runGuards, s as PHP_DECL_RE, t as evaluate, u as TS_DECL_RE, v as ASK_PATTERNS, w as SYSTEM_INSTALL, x as GIT_ASK, y as CRITICAL_PATTERNS } from "./evaluate-DkTgwHNh.mjs";
7
+ import { i as isApexCommand, n as DEV_KEYWORDS, r as detectProjectType, t as detectCreationIntent } from "./policy-Cpa5aWek.mjs";
8
+ import { C as matchPatterns, E as detectFramework, S as SYSTEM_INSTALL, T as evaluateFileSize, _ as CRITICAL_PATTERNS, a as PHP_DECL_RE, b as GIT_BLOCKED, c as TS_DECL_RE, d as CODE_MUTATORS, f as CODE_REDIRECT, g as ASK_PATTERNS, h as protectedPathGuard, i as installGuard, l as interfaceSeparationGuard, m as PROTECTED_FRAGMENTS, n as GUARDS, o as PY_MODEL_RE, p as bashWriteGuard, r as runGuards, s as SWIFT_PROTO_RE, t as evaluate, u as ASK_WRITERS, v as securityGuard, w as countLines, x as PROJECT_INSTALL, y as GIT_ASK } from "./evaluate-urGmc24B.mjs";
8
9
  import { i as resolveSessions, n as formatDocSatisfactionStatus, r as isDocConsulted, t as formatDocDeny } from "./doc-helpers-Dd_x1-tZ.mjs";
9
10
  import { i as parseFrontmatter, n as scoreReferences, r as globToRe, t as routeReferences } from "./router-Dj3AfgBE.mjs";
10
11
  import { a as docConsultedGate, c as solidReadGate, i as brainstormGate, n as capVerbosity, o as evaluateApex, r as APEX_GATES, s as freshnessGate, t as MAX_EXA_RESULTS } from "./verbosity-ZYT7tLCw.mjs";
11
12
  import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
12
- import { a as readState, c as throttleMs, i as nowStamp, l as ensureMemoryGitignore, n as readRoots, o as setStateField, r as registryFile, s as stateFileFor, t as addRoot } from "./memory-BVNt4Ary.mjs";
13
+ import { a as nowStamp, c as stateFileFor, i as lessonsFileFor, l as throttleMs, n as readRoots, o as readState, r as registryFile, s as setStateField, t as addRoot, u as ensureMemoryGitignore } from "./memory-BkoEbdec.mjs";
13
14
  import { a as queryHash, i as jaccardSimilar, n as summarizeIndex, r as compactMarkdown, t as loadIndex } from "./cache-BzbX-ztL.mjs";
14
- import { a as extractText, i as mcpCacheKey, n as cachePath, r as cacheStore, t as cacheLookup } from "./store-BlaEhjab.mjs";
15
- import { t as incrementTrivialEditCounter } from "./freshness-BK9Xg6oG.mjs";
15
+ import { a as extractText, i as mcpCacheKey, n as cachePath, r as cacheStore, t as cacheLookup } from "./store-DeIsfMg5.mjs";
16
+ import { t as incrementTrivialEditCounter } from "./freshness-CezohJHo.mjs";
16
17
  import { n as toRefMeta, t as loadRefs } from "./loader-BephwI8n.mjs";
17
- import { a as ensureStateDir, c as stateFilePath, i as apexStateDir, l as acquireLock, n as taskCreate, o as loadState, r as taskStart, s as saveState, t as taskComplete } from "./state-Bc4wdnCG.mjs";
18
+ import { a as ensureStateDir, c as stateFilePath, i as apexStateDir, l as acquireLock, n as taskCreate, o as loadState, r as taskStart, s as saveState, t as taskComplete } from "./state-Cs0Y0MG_.mjs";
18
19
  import { a as formatPath, c as colors, d as GRADIENT_BLOCKS, f as PROGRESS_BAR_DEFAULTS, i as formatCost, l as progressiveColor, m as TIME_INTERVALS, n as generateProgressBar, o as formatTimeLeft, p as PROGRESS_CHARS, r as formatBasename, s as formatTokens, t as generateGradientBar, u as COLOR_THRESHOLDS } from "./statusline-D87eUNXl.mjs";
19
- export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, COLOR_THRESHOLDS, CRITICAL_PATTERNS, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, GIT_ASK, GIT_BLOCKED, GRADIENT_BLOCKS, GUARDS, MAX_EXA_RESULTS, MAX_LINES_ENV_KEY, PHP_DECL_RE, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INSTALL, PROJECT_INSTALL_RE, PROTECTED_FRAGMENTS, PY_MODEL_RE, SWIFT_PROTO_RE, SYSTEM_INSTALL, SYSTEM_INSTALL_RE, TIME_INTERVALS, TS_DECL_RE, TTL_ENV_KEY, acquireLock, addRoot, apexStateDir, bashWriteGuard, brainstormGate, cacheLookup, cachePath, cacheStore, capVerbosity, colors, compactJson, compactMarkdown, countLines, detectCreationIntent, detectFramework, detectHarness, detectMode, detectProjectType, docConsultedGate, ensureMemoryGitignore, ensureStateDir, evaluate, evaluateApex, evaluateFileSize, extractText, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, installGuard, interfaceSeparationGuard, isApexCommand, isCodeFile, isDocConsulted, jaccardSimilar, loadIndex, loadRefs, loadState, matchPatterns, mcpCacheKey, modeFor, nowStamp, parseEnvInt, parseFrontmatter, progressiveColor, projectRoot, projectRootOrNull, protectedPathGuard, queryHash, readRoots, readState, registryFile, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, runGuards, saveState, scoreReferences, securityGuard, setStateField, solidReadGate, splitTarget, stateFileFor, stateFilePath, summarizeIndex, taskComplete, taskCreate, taskStart, throttleMs, toRefMeta, ttlLabel };
20
+ export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, COLOR_THRESHOLDS, CRITICAL_PATTERNS, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, GIT_ASK, GIT_BLOCKED, GRADIENT_BLOCKS, GUARDS, MAX_EXA_RESULTS, MAX_LINES_ENV_KEY, PHP_DECL_RE, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PY_MODEL_RE, STATE_GITIGNORE, STATE_ROOT, SWIFT_PROTO_RE, SYSTEM_INSTALL, TIME_INTERVALS, TS_DECL_RE, TTL_ENV_KEY, acquireLock, addRoot, apexStateDir, bashWriteGuard, brainstormGate, cacheLookup, cachePath, cacheStore, capVerbosity, colors, compactJson, compactMarkdown, countLines, detectCreationIntent, detectFramework, detectHarness, detectMode, detectProjectType, docConsultedGate, ensureMemoryGitignore, ensureStateDir, evaluate, evaluateApex, evaluateFileSize, extractText, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, installGuard, interfaceSeparationGuard, isApexCommand, isCodeFile, isDocConsulted, jaccardSimilar, lessonsFileFor, loadIndex, loadRefs, loadState, matchPatterns, mcpCacheKey, modeFor, nowStamp, parseEnvInt, parseFrontmatter, progressiveColor, projectLayout, projectRoot, projectRootOrNull, protectedPathGuard, queryHash, readRoots, readState, registryFile, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, runGuards, saveState, scoreReferences, securityGuard, setStateField, solidReadGate, splitTarget, stateFileFor, stateFilePath, summarizeIndex, taskComplete, taskCreate, taskStart, throttleMs, toRefMeta, ttlLabel };
@@ -1,2 +1,2 @@
1
- import { a as codexInit, i as clineInit, n as writeInitFile, o as cursorInit, r as claudeInit, s as geminiInit, t as initFor } from "../run-Cdp2Ef9B.mjs";
1
+ import { a as codexInit, i as clineInit, n as writeInitFile, o as cursorInit, r as claudeInit, s as geminiInit, t as initFor } from "../run-D91N4ul1.mjs";
2
2
  export { claudeInit, clineInit, codexInit, cursorInit, geminiInit, initFor, writeInitFile };
@@ -0,0 +1,30 @@
1
+ import { join } from "node:path";
2
+ //#region src/config/layout.ts
3
+ /** Top-level project state dir — neutral + harness-agnostic (not `.claude/…`). */
4
+ const STATE_ROOT = ".harness";
5
+ /**
6
+ * The selective `.gitignore` for `.harness/`: ignore the machine-local session
7
+ * state (track, cache, throttle) but keep the curated `memory/LESSON.md`.
8
+ */
9
+ const STATE_GITIGNORE = "track/\ncache/\nmemory/state.json\n";
10
+ /**
11
+ * The single source of truth for where every package file lives under a project
12
+ * root. All modules (runtime storage, memory, init) derive their paths here —
13
+ * nothing hardcodes a directory name elsewhere.
14
+ */
15
+ function projectLayout(root) {
16
+ const stateDir = join(root, STATE_ROOT);
17
+ const memoryDir = join(stateDir, "memory");
18
+ return {
19
+ root,
20
+ stateDir,
21
+ trackDir: join(stateDir, "track"),
22
+ cacheDir: join(stateDir, "cache"),
23
+ memoryDir,
24
+ lessonsFile: join(memoryDir, "LESSON.md"),
25
+ memoryStateFile: join(memoryDir, "state.json"),
26
+ gitignoreFile: join(stateDir, ".gitignore")
27
+ };
28
+ }
29
+ //#endregion
30
+ export { STATE_ROOT as n, projectLayout as r, STATE_GITIGNORE as t };
@@ -1,2 +1,2 @@
1
- import { a as ReminderState, c as setStateField, i as registryFile, l as stateFileFor, n as addRoot, o as nowStamp, r as readRoots, s as readState, t as ensureMemoryGitignore, u as throttleMs } from "../index-BEM-mQMC.mjs";
2
- export { ReminderState, addRoot, ensureMemoryGitignore, nowStamp, readRoots, readState, registryFile, setStateField, stateFileFor, throttleMs };
1
+ 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";
2
+ export { ReminderState, addRoot, ensureMemoryGitignore, lessonsFileFor, nowStamp, readRoots, readState, registryFile, setStateField, stateFileFor, throttleMs };
@@ -1,2 +1,2 @@
1
- import { a as readState, c as throttleMs, i as nowStamp, l as ensureMemoryGitignore, n as readRoots, o as setStateField, r as registryFile, s as stateFileFor, t as addRoot } from "../memory-BVNt4Ary.mjs";
2
- export { addRoot, ensureMemoryGitignore, nowStamp, readRoots, readState, registryFile, setStateField, stateFileFor, throttleMs };
1
+ import { a as nowStamp, c as stateFileFor, i as lessonsFileFor, l as throttleMs, n as readRoots, o as readState, r as registryFile, s as setStateField, t as addRoot, u as ensureMemoryGitignore } from "../memory-BkoEbdec.mjs";
2
+ export { addRoot, ensureMemoryGitignore, lessonsFileFor, nowStamp, readRoots, readState, registryFile, setStateField, stateFileFor, throttleMs };
@@ -1,5 +1,6 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
1
+ import { r as projectLayout } from "./layout-C0jaaCQC.mjs";
2
2
  import { dirname } from "node:path";
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
4
  //#region src/memory/gitignore.ts
4
5
  /** Ensure `<memoryDir>/.gitignore` ignores the machine-local `state.json`. */
5
6
  function ensureMemoryGitignore(memoryDir) {
@@ -14,7 +15,11 @@ function ensureMemoryGitignore(memoryDir) {
14
15
  //#region src/memory/state.ts
15
16
  /** Absolute path of the per-project throttle state file. */
16
17
  function stateFileFor(root) {
17
- return `${root}/MEMORY/state.json`;
18
+ return projectLayout(root).memoryStateFile;
19
+ }
20
+ /** Absolute path of the per-project curated lessons file (committable). */
21
+ function lessonsFileFor(root) {
22
+ return projectLayout(root).lessonsFile;
18
23
  }
19
24
  function num(v) {
20
25
  return typeof v === "number" && Number.isFinite(v) ? v : 0;
@@ -91,4 +96,4 @@ function addRoot(root, home) {
91
96
  } catch {}
92
97
  }
93
98
  //#endregion
94
- export { readState as a, throttleMs as c, nowStamp as i, ensureMemoryGitignore as l, readRoots as n, setStateField as o, registryFile as r, stateFileFor as s, addRoot as t };
99
+ export { nowStamp as a, stateFileFor as c, lessonsFileFor as i, throttleMs as l, readRoots as n, readState as o, registryFile as r, setStateField as s, addRoot as t, ensureMemoryGitignore as u };
@@ -1,2 +1,2 @@
1
- import { A as evaluateApex, B as matchPatterns, C as Guard, D as ApexGate, E as ApexContext, F as evaluate, G as DEV_KEYWORDS, H as countLines, I as GIT_ASK, J as isApexCommand, K as ProjectType, L as GIT_BLOCKED, M as solidReadGate, N as PolicyContext, O as brainstormGate, P as PolicyResult, R as PROJECT_INSTALL, S as securityGuard, T as APEX_GATES, U as evaluateFileSize, V as FileSizeVerdict, W as detectFramework, _ as bashWriteGuard, a as runGuards, b as ASK_PATTERNS, c as installGuard, d as SWIFT_PROTO_RE, f as TS_DECL_RE, g as CODE_REDIRECT, h as CODE_MUTATORS, i as GUARDS, j as freshnessGate, k as docConsultedGate, l as PHP_DECL_RE, m as ASK_WRITERS, n as capVerbosity, o as PROJECT_INSTALL_RE, p as interfaceSeparationGuard, q as detectProjectType, r as detectCreationIntent, s as SYSTEM_INSTALL_RE, t as MAX_EXA_RESULTS, u as PY_MODEL_RE, v as PROTECTED_FRAGMENTS, w as GuardContext, x as CRITICAL_PATTERNS, y as protectedPathGuard, z as SYSTEM_INSTALL } from "../index-CQOPshSM.mjs";
2
- export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, ApexContext, ApexGate, CODE_MUTATORS, CODE_REDIRECT, CRITICAL_PATTERNS, DEV_KEYWORDS, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, GUARDS, Guard, GuardContext, MAX_EXA_RESULTS, PHP_DECL_RE, PROJECT_INSTALL, PROJECT_INSTALL_RE, PROTECTED_FRAGMENTS, PY_MODEL_RE, PolicyContext, PolicyResult, ProjectType, SWIFT_PROTO_RE, SYSTEM_INSTALL, SYSTEM_INSTALL_RE, TS_DECL_RE, bashWriteGuard, brainstormGate, capVerbosity, countLines, detectCreationIntent, detectFramework, detectProjectType, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, matchPatterns, protectedPathGuard, runGuards, securityGuard, solidReadGate };
1
+ import { A as solidReadGate, B as countLines, C as APEX_GATES, D as docConsultedGate, E as brainstormGate, F as GIT_BLOCKED, G as detectProjectType, H as detectFramework, I as PROJECT_INSTALL, K as isApexCommand, L as SYSTEM_INSTALL, M as PolicyResult, N as evaluate, O as evaluateApex, P as GIT_ASK, R as matchPatterns, S as GuardContext, T as ApexGate, U as DEV_KEYWORDS, V as evaluateFileSize, W as ProjectType, _ as protectedPathGuard, a as runGuards, b as securityGuard, c as PY_MODEL_RE, d as interfaceSeparationGuard, f as ASK_WRITERS, g as PROTECTED_FRAGMENTS, h as bashWriteGuard, i as GUARDS, j as PolicyContext, k as freshnessGate, l as SWIFT_PROTO_RE, m as CODE_REDIRECT, n as capVerbosity, o as installGuard, p as CODE_MUTATORS, r as detectCreationIntent, s as PHP_DECL_RE, t as MAX_EXA_RESULTS, u as TS_DECL_RE, v as ASK_PATTERNS, w as ApexContext, x as Guard, y as CRITICAL_PATTERNS, z as FileSizeVerdict } from "../index-BKSKTgnV.mjs";
2
+ export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, ApexContext, ApexGate, CODE_MUTATORS, CODE_REDIRECT, CRITICAL_PATTERNS, DEV_KEYWORDS, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, GUARDS, Guard, GuardContext, MAX_EXA_RESULTS, PHP_DECL_RE, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PY_MODEL_RE, PolicyContext, PolicyResult, ProjectType, SWIFT_PROTO_RE, SYSTEM_INSTALL, TS_DECL_RE, bashWriteGuard, brainstormGate, capVerbosity, countLines, detectCreationIntent, detectFramework, detectProjectType, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, matchPatterns, protectedPathGuard, runGuards, securityGuard, solidReadGate };
@@ -1,4 +1,4 @@
1
- import { i as isApexCommand, n as DEV_KEYWORDS, r as detectProjectType, t as detectCreationIntent } from "../policy-Djmqxow2.mjs";
2
- import { C as PROJECT_INSTALL, D as evaluateFileSize, E as countLines, O as detectFramework, S as GIT_BLOCKED, T as matchPatterns, _ as protectedPathGuard, a as SYSTEM_INSTALL_RE, b as securityGuard, c as PY_MODEL_RE, d as interfaceSeparationGuard, f as ASK_WRITERS, g as PROTECTED_FRAGMENTS, h as bashWriteGuard, i as PROJECT_INSTALL_RE, l as SWIFT_PROTO_RE, m as CODE_REDIRECT, n as GUARDS, o as installGuard, p as CODE_MUTATORS, r as runGuards, s as PHP_DECL_RE, t as evaluate, u as TS_DECL_RE, v as ASK_PATTERNS, w as SYSTEM_INSTALL, x as GIT_ASK, y as CRITICAL_PATTERNS } from "../evaluate-DkTgwHNh.mjs";
1
+ import { i as isApexCommand, n as DEV_KEYWORDS, r as detectProjectType, t as detectCreationIntent } from "../policy-Cpa5aWek.mjs";
2
+ import { C as matchPatterns, E as detectFramework, S as SYSTEM_INSTALL, T as evaluateFileSize, _ as CRITICAL_PATTERNS, a as PHP_DECL_RE, b as GIT_BLOCKED, c as TS_DECL_RE, d as CODE_MUTATORS, f as CODE_REDIRECT, g as ASK_PATTERNS, h as protectedPathGuard, i as installGuard, l as interfaceSeparationGuard, m as PROTECTED_FRAGMENTS, n as GUARDS, o as PY_MODEL_RE, p as bashWriteGuard, r as runGuards, s as SWIFT_PROTO_RE, t as evaluate, u as ASK_WRITERS, v as securityGuard, w as countLines, x as PROJECT_INSTALL, y as GIT_ASK } from "../evaluate-urGmc24B.mjs";
3
3
  import { a as docConsultedGate, c as solidReadGate, i as brainstormGate, n as capVerbosity, o as evaluateApex, r as APEX_GATES, s as freshnessGate, t as MAX_EXA_RESULTS } from "../verbosity-ZYT7tLCw.mjs";
4
- export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, CRITICAL_PATTERNS, DEV_KEYWORDS, GIT_ASK, GIT_BLOCKED, GUARDS, MAX_EXA_RESULTS, PHP_DECL_RE, PROJECT_INSTALL, PROJECT_INSTALL_RE, PROTECTED_FRAGMENTS, PY_MODEL_RE, SWIFT_PROTO_RE, SYSTEM_INSTALL, SYSTEM_INSTALL_RE, TS_DECL_RE, bashWriteGuard, brainstormGate, capVerbosity, countLines, detectCreationIntent, detectFramework, detectProjectType, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, matchPatterns, protectedPathGuard, runGuards, securityGuard, solidReadGate };
4
+ export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, CRITICAL_PATTERNS, DEV_KEYWORDS, GIT_ASK, GIT_BLOCKED, GUARDS, MAX_EXA_RESULTS, PHP_DECL_RE, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PY_MODEL_RE, SWIFT_PROTO_RE, SYSTEM_INSTALL, TS_DECL_RE, bashWriteGuard, brainstormGate, capVerbosity, countLines, detectCreationIntent, detectFramework, detectProjectType, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, matchPatterns, protectedPathGuard, runGuards, securityGuard, solidReadGate };
@@ -1,5 +1,5 @@
1
- import { t as isCodeFile } from "./project-root-C4ks_q1G.mjs";
2
- import { t as evaluate } from "./evaluate-DkTgwHNh.mjs";
1
+ import { t as isCodeFile } from "./project-root-ff0_poWU.mjs";
2
+ import { t as evaluate } from "./evaluate-urGmc24B.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,5 +1,6 @@
1
- import { chmodSync, mkdirSync, writeFileSync } from "node:fs";
1
+ import { n as STATE_ROOT, t as STATE_GITIGNORE } from "./layout-C0jaaCQC.mjs";
2
2
  import { dirname, join } from "node:path";
3
+ import { chmodSync, mkdirSync, writeFileSync } from "node:fs";
3
4
  //#region src/init/templates.ts
4
5
  function json(obj) {
5
6
  return `${JSON.stringify(obj, null, 2)}\n`;
@@ -114,7 +115,11 @@ const RUNNERS = {
114
115
  */
115
116
  function initFor(id, command = `npx harness hook ${id}`) {
116
117
  const make = RUNNERS[id];
117
- return make ? make(command) : null;
118
+ if (!make) return null;
119
+ return [...make(command), {
120
+ path: `${STATE_ROOT}/.gitignore`,
121
+ content: STATE_GITIGNORE
122
+ }];
118
123
  }
119
124
  /** Write an {@link InitFile} under `root` (creates dirs; `chmod +x` when executable). */
120
125
  function writeInitFile(root, file) {
@@ -1,5 +1,4 @@
1
1
  import { t as Prompt } from "../types-D56jSgD9.mjs";
2
- import { t as HarnessId } from "../harness-DwJskkz_.mjs";
3
2
  import { t as RefMeta } from "../types-CY5qT2X1.mjs";
4
3
 
5
4
  //#region src/runtime/paths.d.ts
@@ -8,11 +7,10 @@ declare function trackFile(sessionId: string, baseDir?: string): string;
8
7
  //#endregion
9
8
  //#region src/runtime/storage.d.ts
10
9
  /**
11
- * Directory where a harness's fuse-harness track files liveunder that
12
- * harness's own config dir (`.claude`, `.codex`, …) so state sits next to its
13
- * hooks. Falls back to `.fuse-harness` for harnesses without a known config dir.
10
+ * The project's single state dir (`<root>/.harness`)neutral + harness-agnostic,
11
+ * derived from {@link projectLayout}. (Was the per-harness `.claude/harness`.)
14
12
  */
15
- declare function harnessTrackDir(id: HarnessId, projectRoot: string): string;
13
+ declare function harnessStateDir(root: string): string;
16
14
  //#endregion
17
15
  //#region src/runtime/record.d.ts
18
16
  /** A unit of session activity to record (discriminated union on `kind`). */
@@ -54,8 +52,8 @@ declare function activityFor(event: ToolEvent): Activity | null;
54
52
  //#region src/runtime/gate.d.ts
55
53
  /** Prior agents the freshness gate requires before a code edit. */
56
54
  declare const REQUIRED_AGENTS: ReadonlyArray<string>;
57
- /** Default freshness window for {@link REQUIRED_AGENTS} (4 min, the APEX TTL). */
58
- declare const DEFAULT_WINDOW_MS = 24e4;
55
+ /** Default freshness window for {@link REQUIRED_AGENTS} (2 min — matches the plugin's `FUSE_ENFORCE_TTL_SEC` default). */
56
+ declare const DEFAULT_WINDOW_MS = 12e4;
59
57
  /** Trivial edits allowed within the window before the full APEX gates apply. */
60
58
  declare const TRIVIAL_BUDGET = 4;
61
59
  /** A tool-use to gate, plus the session pointers needed for the stateful gates. */
@@ -128,6 +126,8 @@ interface HandleOptions {
128
126
  cwd: string;
129
127
  /** Directory of SOLID reference `.md` files for `solidReadGate` (else inert). */
130
128
  refsDir?: string;
129
+ /** APEX freshness window in ms (from `FUSE_ENFORCE_TTL_SEC`). */
130
+ windowMs?: number;
131
131
  }
132
132
  /** What the hook bin should print + exit with. */
133
133
  interface HandleOutcome {
@@ -142,4 +142,4 @@ interface HandleOutcome {
142
142
  */
143
143
  declare function handleHook(id: string, payload: Record<string, unknown>, opts: HandleOptions): Promise<HandleOutcome>;
144
144
  //#endregion
145
- export { Activity, DEFAULT_WINDOW_MS, GateInput, HandleOptions, HandleOutcome, MCP_TTL_MS, NormalizedEvent, REQUIRED_AGENTS, TRIVIAL_BUDGET, ToolEvent, activityFor, gate, handleHook, harnessTrackDir, isMcpTool, mcpPostStore, mcpPreIntercept, normalizeEvent, queryOf, recordActivity, respond, trackFile };
145
+ export { Activity, DEFAULT_WINDOW_MS, GateInput, HandleOptions, HandleOutcome, MCP_TTL_MS, NormalizedEvent, REQUIRED_AGENTS, TRIVIAL_BUDGET, ToolEvent, activityFor, gate, handleHook, harnessStateDir, isMcpTool, mcpPostStore, mcpPreIntercept, normalizeEvent, queryOf, recordActivity, respond, trackFile };
@@ -1,2 +1,12 @@
1
- import { a as trackFile, c as isMcpTool, d as queryOf, f as DEFAULT_WINDOW_MS, g as activityFor, h as gate, i as recordActivity, l as mcpPostStore, m as TRIVIAL_BUDGET, n as harnessTrackDir, o as normalizeEvent, p as REQUIRED_AGENTS, r as respond, s as MCP_TTL_MS, t as handleHook, u as mcpPreIntercept } from "../handle-o5np-3T_.mjs";
2
- export { DEFAULT_WINDOW_MS, MCP_TTL_MS, REQUIRED_AGENTS, TRIVIAL_BUDGET, activityFor, gate, handleHook, harnessTrackDir, isMcpTool, mcpPostStore, mcpPreIntercept, normalizeEvent, queryOf, recordActivity, respond, trackFile };
1
+ import { r as projectLayout } from "../layout-C0jaaCQC.mjs";
2
+ import { a as normalizeEvent, c as mcpPostStore, d as DEFAULT_WINDOW_MS, f as REQUIRED_AGENTS, h as activityFor, i as trackFile, l as mcpPreIntercept, m as gate, n as respond, o as MCP_TTL_MS, p as TRIVIAL_BUDGET, r as recordActivity, s as isMcpTool, t as handleHook, u as queryOf } from "../handle-D_cJGRjJ.mjs";
3
+ //#region src/runtime/storage.ts
4
+ /**
5
+ * The project's single state dir (`<root>/.harness`) — neutral + harness-agnostic,
6
+ * derived from {@link projectLayout}. (Was the per-harness `.claude/harness`.)
7
+ */
8
+ function harnessStateDir(root) {
9
+ return projectLayout(root).stateDir;
10
+ }
11
+ //#endregion
12
+ export { DEFAULT_WINDOW_MS, MCP_TTL_MS, REQUIRED_AGENTS, TRIVIAL_BUDGET, activityFor, gate, handleHook, harnessStateDir, isMcpTool, mcpPostStore, mcpPreIntercept, normalizeEvent, queryOf, recordActivity, respond, trackFile };
@@ -1,2 +1,2 @@
1
- import { a as ensureStateDir, c as stateFilePath, i as apexStateDir, l as acquireLock, n as taskCreate, o as loadState, r as taskStart, s as saveState, t as taskComplete } from "../state-Bc4wdnCG.mjs";
1
+ import { a as ensureStateDir, c as stateFilePath, i as apexStateDir, l as acquireLock, n as taskCreate, o as loadState, r as taskStart, s as saveState, t as taskComplete } from "../state-Cs0Y0MG_.mjs";
2
2
  export { acquireLock, apexStateDir, ensureStateDir, loadState, saveState, stateFilePath, taskComplete, taskCreate, taskStart };
@@ -1,4 +1,4 @@
1
- import { n as readJsonFile, r as writeJsonFile, t as ensureDir } from "./json-io-RH82El2J.mjs";
1
+ import { n as readJsonFile, r as writeJsonFile, t as ensureDir } from "./json-io-xpTDuvtn.mjs";
2
2
  import { mkdir, rmdir } from "node:fs/promises";
3
3
  //#region src/state/lock.ts
4
4
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
@@ -1,4 +1,4 @@
1
- import { n as readJsonFile, r as writeJsonFile } from "./json-io-RH82El2J.mjs";
1
+ import { n as readJsonFile, r as writeJsonFile } from "./json-io-xpTDuvtn.mjs";
2
2
  //#region src/tracking/session-state.ts
3
3
  /** A fresh, empty track. */
4
4
  function emptyTrack() {
@@ -1,2 +1,2 @@
1
- import { a as recordAgent, c as recordRefRead, i as emptyTrack, l as recordTrivialEdit, n as saveTrack, o as recordBrainstormRequired, r as agentsFresh, s as recordDoc, t as loadTrack, u as trivialCount } from "../store-gtbP-Bv9.mjs";
1
+ import { a as recordAgent, c as recordRefRead, i as emptyTrack, l as recordTrivialEdit, n as saveTrack, o as recordBrainstormRequired, r as agentsFresh, s as recordDoc, t as loadTrack, u as trivialCount } from "../store-BnHpq2ZB.mjs";
2
2
  export { agentsFresh, emptyTrack, loadTrack, recordAgent, recordBrainstormRequired, recordDoc, recordRefRead, recordTrivialEdit, saveTrack, trivialCount };
@@ -1,4 +1,4 @@
1
1
  import { t as compactJson } from "../compact-json-DK2nX-MK.mjs";
2
- import { n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "../project-root-C4ks_q1G.mjs";
2
+ import { n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "../project-root-ff0_poWU.mjs";
3
3
  import "../util-la_KkjCS.mjs";
4
4
  export { compactJson, isCodeFile, projectRoot, projectRootOrNull };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusengine/harness",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
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",
@@ -1,37 +0,0 @@
1
- //#region src/config/env.d.ts
2
- /**
3
- * Robust integer-from-env parser.
4
- * undefined / empty / whitespace / NaN / float / <= 0 all fall back to `fallback`.
5
- * `Number("")` is 0, so the empty guard is required before `Number()`.
6
- */
7
- declare function parseEnvInt(raw: string | undefined, fallback: number): number;
8
- //#endregion
9
- //#region src/config/ttl.d.ts
10
- /** Default enforcement-freshness window, in seconds (2 minutes). */
11
- declare const DEFAULT_TTL_SEC = 120;
12
- /** Default env var name carrying the TTL override. */
13
- declare const TTL_ENV_KEY = "FUSE_ENFORCE_TTL_SEC";
14
- /**
15
- * Resolve the enforcement TTL (seconds) from an env map.
16
- * @param env - environment map (defaults to `process.env`)
17
- * @param key - env var name (defaults to `FUSE_ENFORCE_TTL_SEC`)
18
- */
19
- declare function resolveTtlSec(env?: Record<string, string | undefined>, key?: string): number;
20
- /** Human label for a TTL: 120 -> "2min", 240 -> "4min", 90 -> "90s". */
21
- declare function ttlLabel(sec: number): string;
22
- //#endregion
23
- //#region src/config/limits.d.ts
24
- /** Default SOLID max lines per file. */
25
- declare const DEFAULT_MAX_LINES = 100;
26
- /** Default env var name carrying the max-lines override. */
27
- declare const MAX_LINES_ENV_KEY = "FUSE_SOLID_MAX_LINES";
28
- /**
29
- * Resolve the SOLID max-lines limit from an env map.
30
- * @param env - environment map (defaults to `process.env`)
31
- * @param key - env var name (defaults to `FUSE_SOLID_MAX_LINES`)
32
- */
33
- declare function resolveMaxLines(env?: Record<string, string | undefined>, key?: string): number;
34
- /** Advisory module-split headroom = `maxLines - 10` (never below 1). */
35
- declare function splitTarget(maxLines: number): number;
36
- //#endregion
37
- export { DEFAULT_TTL_SEC as a, ttlLabel as c, splitTarget as i, parseEnvInt as l, MAX_LINES_ENV_KEY as n, TTL_ENV_KEY as o, resolveMaxLines as r, resolveTtlSec as s, DEFAULT_MAX_LINES as t };
@@ -1,6 +1,6 @@
1
1
  import { t as compactJson } from "./compact-json-DK2nX-MK.mjs";
2
- import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
3
2
  import { dirname } from "node:path";
3
+ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { mkdir } from "node:fs/promises";
6
6
  //#region src/util/json-io.ts
@@ -1,5 +1,5 @@
1
- import { existsSync } from "node:fs";
2
1
  import { join } from "node:path";
2
+ import { existsSync } from "node:fs";
3
3
  //#region src/policy/detect-project.ts
4
4
  /** Keywords that signal a development task (APEX trigger). */
5
5
  const DEV_KEYWORDS = /\b(implement|create|build|fix|add|refactor|develop|feature|bug|update|modify|change|write|code)\b/i;
@@ -1,5 +1,5 @@
1
- import { existsSync } from "node:fs";
2
1
  import { dirname, resolve } from "node:path";
2
+ import { existsSync } from "node:fs";
3
3
  //#region src/util/project-root.ts
4
4
  /** Source-code file extensions worth tracking as "code was written". */
5
5
  const CODE_EXT = /\.(ts|tsx|js|jsx|py|php|swift|go|rs|rb|java|vue|svelte|astro|css|kt|dart|cpp|c)$/;
@@ -1,5 +1,5 @@
1
- import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
1
  import { dirname, join } from "node:path";
2
+ import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
3
3
  //#region src/cache/mcp-response.ts
4
4
  const MAX_DEPTH = 5;
5
5
  /**
File without changes