@fusengine/harness 0.1.48 → 0.1.49

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 (49) hide show
  1. package/dist/adapters/claude/index.d.mts +2 -2
  2. package/dist/adapters/claude/index.mjs +2 -2
  3. package/dist/adapters/cline/index.mjs +1 -1
  4. package/dist/adapters/codex/index.d.mts +1 -1
  5. package/dist/adapters/codex/index.mjs +1 -1
  6. package/dist/adapters/cursor/index.d.mts +10 -3
  7. package/dist/adapters/cursor/index.mjs +3 -3
  8. package/dist/adapters/gemini/index.mjs +1 -1
  9. package/dist/adapters/hermes/index.d.mts +54 -0
  10. package/dist/adapters/hermes/index.mjs +2 -0
  11. package/dist/apex-Wdi1nq_w.d.mts +73 -0
  12. package/dist/{claude-U1KTaRG4.mjs → claude-BxC9semG.mjs} +30 -9
  13. package/dist/cli/bin.mjs +5 -5
  14. package/dist/cli/index.mjs +1 -1
  15. package/dist/config/index.d.mts +1 -1
  16. package/dist/config/index.mjs +1 -1
  17. package/dist/detect/index.d.mts +1 -1
  18. package/dist/detect/index.mjs +1 -1
  19. package/dist/{doc-helpers-BNfYWvYv.d.mts → doc-helpers-CEKzGg2u.d.mts} +2 -0
  20. package/dist/{dotenv-B9nM4cuQ.mjs → dotenv-Jj8aL1FL.mjs} +2 -1
  21. package/dist/{evaluate-qYC5xClb.mjs → evaluate-CNAzgxnN.mjs} +3 -85
  22. package/dist/freshness/index.d.mts +1 -1
  23. package/dist/{handle-CCV0ycdr.mjs → handle-D1hHqHvh.mjs} +404 -52
  24. package/dist/{harness-C8Nxxyn_.mjs → harness-Cb9xR8dC.mjs} +6 -3
  25. package/dist/{harness-BPPu5CrN.d.mts → harness-s1Fce_dL.d.mts} +1 -1
  26. package/dist/hermes-DWXCRFZU.mjs +54 -0
  27. package/dist/home-state-D0RLWP8J.mjs +134 -0
  28. package/dist/{index-BqfOFKm7.d.mts → index-BfMsILmg.d.mts} +2 -63
  29. package/dist/{index-BxjzFraL.d.mts → index-DZCLmSoO.d.mts} +11 -9
  30. package/dist/{index-BXPySPxE.d.mts → index-jj_EzZRv.d.mts} +1 -1
  31. package/dist/index.d.mts +7 -6
  32. package/dist/index.mjs +6 -5
  33. package/dist/init/index.d.mts +1 -1
  34. package/dist/policy/index.d.mts +3 -2
  35. package/dist/policy/index.mjs +4 -3
  36. package/dist/prompt/index.d.mts +1 -1
  37. package/dist/{run-CaqeQFYp.mjs → run-DZvP_9xB.mjs} +1 -1
  38. package/dist/runtime/index.d.mts +22 -8
  39. package/dist/runtime/index.mjs +2 -2
  40. package/dist/{session-state-V8Jp-KE5.d.mts → session-state-DMpotbRz.d.mts} +26 -4
  41. package/dist/skill-path-DhItkBzk.mjs +95 -0
  42. package/dist/store-QSSTO3lY.mjs +349 -0
  43. package/dist/tracking/index.d.mts +2 -2
  44. package/dist/tracking/index.mjs +2 -2
  45. package/dist/{types-D56jSgD9.d.mts → types-DVbIl9md.d.mts} +6 -0
  46. package/dist/{validate-ebDjIyOa.mjs → validate-DCQ8dkdL.mjs} +16 -100
  47. package/package.json +7 -2
  48. package/dist/home-state-mKZxP4oZ.mjs +0 -52
  49. package/dist/store-ARx-GQSQ.mjs +0 -200
@@ -1,2 +1,2 @@
1
- import { a as fileSizeGuard, c as systemMessage, i as denyResponse, l as toClaudeResponse, n as blockResponse, o as guard, r as contextResponse, s as readClaudeInput, t as ClaudeHookInput } from "../../index-BxjzFraL.mjs";
2
- export { ClaudeHookInput, blockResponse, contextResponse, denyResponse, fileSizeGuard, guard, readClaudeInput, systemMessage, toClaudeResponse };
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";
2
+ export { ClaudeHookInput, attachSystemMessage, blockResponse, contextResponse, denyResponse, fileSizeGuard, guard, informResponse, readClaudeInput, systemMessage, toClaudeResponse };
@@ -1,2 +1,2 @@
1
- import { a as guard, c as toClaudeResponse, i as fileSizeGuard, n as contextResponse, o as readClaudeInput, r as denyResponse, s as systemMessage, t as blockResponse } from "../../claude-U1KTaRG4.mjs";
2
- export { blockResponse, contextResponse, denyResponse, fileSizeGuard, guard, readClaudeInput, systemMessage, toClaudeResponse };
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-BxC9semG.mjs";
2
+ export { attachSystemMessage, blockResponse, contextResponse, denyResponse, fileSizeGuard, guard, informResponse, readClaudeInput, systemMessage, toClaudeResponse };
@@ -1,4 +1,4 @@
1
- import { t as evaluate } from "../../evaluate-qYC5xClb.mjs";
1
+ import { t as evaluate } from "../../evaluate-CNAzgxnN.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 { i as denyResponse, o as guard, r as contextResponse, s as readClaudeInput, t as ClaudeHookInput } from "../../index-BxjzFraL.mjs";
1
+ import { a as denyResponse, i as contextResponse, l as readClaudeInput, s as guard, t as ClaudeHookInput } from "../../index-DZCLmSoO.mjs";
2
2
  export { type ClaudeHookInput as CodexHookInput, contextResponse, denyResponse, guard, readClaudeInput as readCodexInput };
@@ -1,2 +1,2 @@
1
- import { a as guard, n as contextResponse, o as readClaudeInput, r as denyResponse } from "../../claude-U1KTaRG4.mjs";
1
+ import { c as readClaudeInput, i as denyResponse, o as guard, r as contextResponse } from "../../claude-BxC9semG.mjs";
2
2
  export { contextResponse, denyResponse, guard, readClaudeInput as readCodexInput };
@@ -14,12 +14,19 @@ interface CursorEditPayload {
14
14
  new_string: string;
15
15
  }[];
16
16
  }
17
- /** `beforeShellExecution` stdout response. */
17
+ /**
18
+ * `beforeShellExecution` stdout response. Message keys are snake_case:
19
+ * Cursor silently ignores camelCase `userMessage`/`agentMessage` (#141516,
20
+ * regression persists through v2.0.77+ — forum #142589), matching the
21
+ * schema respond.ts emits for the cursor harness.
22
+ */
18
23
  interface CursorResponse {
19
24
  permission: "allow" | "deny" | "ask";
20
25
  continue?: boolean;
21
- userMessage?: string;
22
- agentMessage?: string;
26
+ /** User-visible message — snake_case required (#141516, #142589). */
27
+ user_message?: string;
28
+ /** Agent-visible message — snake_case required (#141516, #142589). */
29
+ agent_message?: string;
23
30
  }
24
31
  //#endregion
25
32
  //#region src/adapters/cursor/index.d.ts
@@ -1,4 +1,4 @@
1
- import { t as evaluate } from "../../evaluate-qYC5xClb.mjs";
1
+ import { t as evaluate } from "../../evaluate-CNAzgxnN.mjs";
2
2
  import { t as formatPrompt } from "../../types-ernB1Dy3.mjs";
3
3
  //#region src/adapters/cursor/index.ts
4
4
  /**
@@ -19,8 +19,8 @@ function beforeShellExecution(payload) {
19
19
  return {
20
20
  permission: toPermission(r.prompt.kind),
21
21
  continue: false,
22
- userMessage: msg,
23
- agentMessage: msg
22
+ user_message: msg,
23
+ agent_message: msg
24
24
  };
25
25
  }
26
26
  /** Observe a file edit (Cursor cannot block here). Returns the verdict for logging. */
@@ -1,4 +1,4 @@
1
- import { t as evaluate } from "../../evaluate-qYC5xClb.mjs";
1
+ import { t as evaluate } from "../../evaluate-CNAzgxnN.mjs";
2
2
  import { t as formatPrompt } from "../../types-ernB1Dy3.mjs";
3
3
  //#region src/adapters/gemini/index.ts
4
4
  /**
@@ -0,0 +1,54 @@
1
+ import { t as Prompt } from "../../types-DVbIl9md.mjs";
2
+
3
+ //#region src/adapters/hermes/interfaces/types.d.ts
4
+ /**
5
+ * Hermes Agent (NousResearch/hermes-agent) shell-hook wire types. Verified 2026
6
+ * against hermes-agent.nousresearch.com/docs/user-guide/features/hooks: hooks
7
+ * are configured in `~/.hermes/config.yaml` under `hooks:`, receive a JSON
8
+ * payload on stdin, and answer with JSON on stdout.
9
+ */
10
+ /** `pre_tool_call` stdin payload (subset). Same wire shape as Claude Code. */
11
+ interface HermesHookInput {
12
+ hook_event_name?: string;
13
+ tool_name?: string;
14
+ tool_input?: {
15
+ path?: string;
16
+ file_path?: string;
17
+ content?: string;
18
+ command?: string;
19
+ };
20
+ session_id?: string;
21
+ cwd?: string;
22
+ }
23
+ /**
24
+ * Hook stdout response. `{}` allows; `{ decision: "block", reason }` cancels
25
+ * the tool call; `{ context }` injects LLM context (honored on `pre_llm_call`).
26
+ * Hermes has NO Claude-style `permissionDecision` and no interactive "ask".
27
+ */
28
+ interface HermesResponse {
29
+ decision?: "block";
30
+ reason?: string;
31
+ context?: string;
32
+ }
33
+ //#endregion
34
+ //#region src/adapters/hermes/index.d.ts
35
+ /** Read & parse the Hermes hook stdin payload (same wire shape as Claude). */
36
+ declare function readHermesInput(): Promise<HermesHookInput>;
37
+ /**
38
+ * Render a portable {@link Prompt} as a Hermes hook response string:
39
+ * `block` -> `{decision:"block",reason}`; `ask`/`inform` -> non-blocking
40
+ * `{context}` — Hermes has no interactive "ask" state, so both degrade to
41
+ * context injection, mirroring the gemini-cli/cline routing in `respond()`.
42
+ * @param prompt - The portable policy prompt.
43
+ * @returns The native Hermes JSON response string.
44
+ */
45
+ declare function toHermesResponse(prompt: Prompt): string;
46
+ /**
47
+ * Run the bundled policy over a Hermes `pre_tool_call` payload and return the
48
+ * native response string, or null to allow (the hook then prints `{}`).
49
+ * @param input - Parsed `pre_tool_call` stdin payload.
50
+ * @returns Native response JSON, or null when the tool use is allowed.
51
+ */
52
+ declare function guard(input: HermesHookInput): string | null;
53
+ //#endregion
54
+ export { type HermesHookInput, type HermesResponse, guard, readHermesInput, toHermesResponse };
@@ -0,0 +1,2 @@
1
+ import { n as readHermesInput, r as toHermesResponse, t as guard } from "../../hermes-DWXCRFZU.mjs";
2
+ export { guard, readHermesInput, toHermesResponse };
@@ -0,0 +1,73 @@
1
+ import { t as Prompt } from "./types-DVbIl9md.mjs";
2
+ import { t as AuthEntry } from "./doc-helpers-CEKzGg2u.mjs";
3
+ import { t as RefMeta } from "./types-CY5qT2X1.mjs";
4
+
5
+ //#region src/policy/apex-gates.d.ts
6
+ /** Gate: the routed SOLID references for this edit must have been read within the TTL. */
7
+ declare const solidReadGate: ApexGate;
8
+ /** Gate: the required prior agents (explore + research) must have run within the window. */
9
+ declare const freshnessGate: ApexGate;
10
+ //#endregion
11
+ //#region src/policy/apex.d.ts
12
+ /**
13
+ * Session context for the stateful APEX gates. The harness adapter supplies this
14
+ * (the package owns the gate LOGIC; recording the session activity is the
15
+ * adapter's tracking layer).
16
+ */
17
+ interface ApexContext {
18
+ sessionId: string;
19
+ framework: string;
20
+ filePath: string;
21
+ content: string;
22
+ /** Doc-consultation authorizations from session state (Context7/Exa). */
23
+ authorizations?: Record<string, AuthEntry>;
24
+ /** Available SOLID references for the framework's skill. */
25
+ refs?: RefMeta[];
26
+ /** Absolute paths of SOLID refs already read this session. */
27
+ refsRead?: string[];
28
+ /**
29
+ * Epoch-ms read timestamp per `refsRead` path (parity track-solid-reads.py
30
+ * `solid_reads[].timestamp`). PARITY: the TTL applies ONLY to solidReadGate —
31
+ * Python TTL-izes SOLID reads exclusively (require-solid-read.py); the
32
+ * skill-trigger/design/shadcn gates stay session-scoped (no TTL). A path in
33
+ * `refsRead` with no stamp here (tracks recorded before this field existed)
34
+ * counts as read — backward compat.
35
+ */
36
+ refsReadAt?: Record<string, number>;
37
+ /** Current epoch ms for the SOLID-read TTL check; absent → reads never expire. */
38
+ now?: number;
39
+ /** Whether the required prior agents (explore + research) ran within the freshness window. */
40
+ agentsFresh?: boolean;
41
+ /** Names of REQUIRED_AGENTS that have NOT run fresh (subset), for a precise freshnessGate message. Absent → generic wording. */
42
+ missingAgents?: string[];
43
+ /** Freshness window in ms: labels the block messages' TTL (e.g. "2min") and bounds the SOLID-read TTL in solidReadGate. */
44
+ windowMs?: number;
45
+ /** Whether brainstorming is required for this edit (creation intent on a new file). */
46
+ brainstormRequired?: boolean;
47
+ /** Whether the brainstorming agent ran within the window. */
48
+ brainstormFresh?: boolean;
49
+ }
50
+ /** A single APEX gate: returns a blocking {@link Prompt}, or null to pass. */
51
+ type ApexGate = (ctx: ApexContext) => Prompt | null;
52
+ /** Gate: BOTH Context7 AND Exa (or a web fallback alone) must have been consulted this session. */
53
+ declare const docConsultedGate: ApexGate;
54
+ /** Gate: brainstorming must precede creating new files when flagged. */
55
+ declare const brainstormGate: ApexGate;
56
+ /** Gates that run BEFORE Check 1 (harness-specific brainstorm + agent freshness, pre-dating enforce-apex-phases). */
57
+ declare const PRE_AUTH_GATES: ReadonlyArray<ApexGate>;
58
+ /** Gates that run AFTER Check 1: Check 2 (doc consulted once per session) then the SOLID refs. */
59
+ declare const POST_AUTH_GATES: ReadonlyArray<ApexGate>;
60
+ /**
61
+ * Default APEX gate chain (brainstorm, freshness, docs, SOLID refs). Check 1
62
+ * (`apexAuthorizationGate`, policy/apex-authorization.ts) is wired BETWEEN the
63
+ * PRE and POST sub-chains by the runtime (gate-apex.ts), which owns its
64
+ * target-write side effect — parity enforce-apex-phases.ts Check1 -> Check2.
65
+ */
66
+ declare const APEX_GATES: ReadonlyArray<ApexGate>;
67
+ /**
68
+ * Run the APEX gates (chain-of-responsibility): the first failing gate's prompt
69
+ * wins; null means every gate passed (allow).
70
+ */
71
+ declare function evaluateApex(ctx: ApexContext, gates?: ReadonlyArray<ApexGate>): Prompt | null;
72
+ //#endregion
73
+ export { PRE_AUTH_GATES as a, evaluateApex as c, POST_AUTH_GATES as i, freshnessGate as l, ApexContext as n, brainstormGate as o, ApexGate as r, docConsultedGate as s, APEX_GATES as t, solidReadGate as u };
@@ -1,4 +1,4 @@
1
- import { t as evaluate } from "./evaluate-qYC5xClb.mjs";
1
+ import { t as evaluate } from "./evaluate-CNAzgxnN.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";
@@ -96,12 +96,9 @@ function denyResponse(event, reason) {
96
96
  } });
97
97
  }
98
98
  /**
99
- * A PostToolUse (and Stop/UserPromptSubmit) `block` response. These events
100
- * ignore `hookSpecificOutput.permissionDecision` (a PreToolUse-only field) and
101
- * only honor the top-level `decision`/`reason` keys, which feed the reason back
102
- * to Claude as automated feedback.
103
- * @param reason - The block feedback shown to Claude.
104
- * @returns The native `{decision:"block",reason}` response string.
99
+ * A PostToolUse (and Stop/UserPromptSubmit) `block` response: those events
100
+ * ignore `permissionDecision` (PreToolUse-only) and only honor the top-level
101
+ * `decision`/`reason` keys, which feed `reason` back to Claude.
105
102
  */
106
103
  function blockResponse(reason) {
107
104
  return JSON.stringify({
@@ -120,10 +117,33 @@ function contextResponse(event, text) {
120
117
  function systemMessage(text) {
121
118
  return JSON.stringify({ systemMessage: text });
122
119
  }
120
+ /** An `inform` response with a user-visible notice (Python `allow_pass`/`post_pass` parity): a pure pass reuses {@link systemMessage} alone; with agent-facing `context` both channels merge in one JSON. */
121
+ function informResponse(event, notice, context) {
122
+ if (!context) return systemMessage(notice);
123
+ return JSON.stringify({
124
+ systemMessage: notice,
125
+ hookSpecificOutput: {
126
+ hookEventName: event,
127
+ additionalContext: context
128
+ }
129
+ });
130
+ }
131
+ /** Attach a user-visible `systemMessage` onto an already-rendered hook stdout JSON ({@link systemMessage} alone when empty/unparseable). */
132
+ function attachSystemMessage(stdout, notice) {
133
+ try {
134
+ return JSON.stringify({
135
+ ...JSON.parse(stdout),
136
+ systemMessage: notice
137
+ });
138
+ } catch {
139
+ return systemMessage(notice);
140
+ }
141
+ }
123
142
  /**
124
143
  * Render a portable {@link Prompt} as a Claude Code hook response:
125
144
  * `block` → `permissionDecision: deny`, `ask` → `permissionDecision: ask`
126
- * (interactive confirm), `inform` → `additionalContext`.
145
+ * (interactive confirm), `inform` → `additionalContext` — plus the user-visible
146
+ * `systemMessage` channel when the prompt carries a `userMessage`.
127
147
  */
128
148
  function toClaudeResponse(event, prompt) {
129
149
  const reason = formatPrompt(prompt);
@@ -133,6 +153,7 @@ function toClaudeResponse(event, prompt) {
133
153
  permissionDecision: "ask",
134
154
  permissionDecisionReason: reason
135
155
  } });
156
+ if (prompt.userMessage) return informResponse(event, prompt.userMessage, prompt.reason ? reason : "");
136
157
  return contextResponse(event, reason);
137
158
  }
138
159
  /**
@@ -152,4 +173,4 @@ function guard(input) {
152
173
  /** @deprecated use {@link guard}. Kept for back-compat. */
153
174
  const fileSizeGuard = guard;
154
175
  //#endregion
155
- export { guard as a, toClaudeResponse as c, readText as d, sleep as f, fileSizeGuard as i, collectFiles as l, writeText as m, contextResponse as n, readClaudeInput as o, spawnCapture as p, denyResponse as r, systemMessage as s, blockResponse as t, pathExists as u };
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 };
package/dist/cli/bin.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { r as loadDotenv, s as resolveTtlSec } from "../dotenv-B9nM4cuQ.mjs";
3
- import { t as detectHarness } from "../harness-C8Nxxyn_.mjs";
4
- import { t as claudeHome } from "../home-state-mKZxP4oZ.mjs";
5
- import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-CaqeQFYp.mjs";
2
+ import { r as loadDotenv, s as resolveTtlSec } from "../dotenv-Jj8aL1FL.mjs";
3
+ import { t as detectHarness } from "../harness-Cb9xR8dC.mjs";
4
+ import { t as claudeHome } from "../home-state-D0RLWP8J.mjs";
5
+ import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-DZvP_9xB.mjs";
6
6
  import { n as writeInitFile, t as initFor } from "../run-Do2JltgU.mjs";
7
- import { Mt as todayUtc, t as handleHook } from "../handle-CCV0ycdr.mjs";
7
+ import { Mt as todayUtc, t as handleHook } from "../handle-D1hHqHvh.mjs";
8
8
  import { delimiter, dirname, 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-CaqeQFYp.mjs";
1
+ import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-DZvP_9xB.mjs";
2
2
  export { checkStaged, stagedContent, stagedFiles };
@@ -1,2 +1,2 @@
1
- import { _ as parseEnvInt, a as ProjectLayout, c as projectLayout, d as resolveMaxLines, f as splitTarget, g as ttlLabel, h as resolveTtlSec, i as parseEnvFile, l as DEFAULT_MAX_LINES, m as TTL_ENV_KEY, n as envCandidates, o as STATE_GITIGNORE, p as DEFAULT_TTL_SEC, r as loadDotenv, s as STATE_ROOT, t as HOME_DIR, u as MAX_LINES_ENV_KEY } from "../index-BXPySPxE.mjs";
1
+ import { _ as parseEnvInt, a as ProjectLayout, c as projectLayout, d as resolveMaxLines, f as splitTarget, g as ttlLabel, h as resolveTtlSec, i as parseEnvFile, l as DEFAULT_MAX_LINES, m as TTL_ENV_KEY, n as envCandidates, o as STATE_GITIGNORE, p as DEFAULT_TTL_SEC, r as loadDotenv, s as STATE_ROOT, t as HOME_DIR, u as MAX_LINES_ENV_KEY } from "../index-jj_EzZRv.mjs";
2
2
  export { DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, HOME_DIR, MAX_LINES_ENV_KEY, ProjectLayout, STATE_GITIGNORE, STATE_ROOT, TTL_ENV_KEY, envCandidates, loadDotenv, parseEnvFile, parseEnvInt, projectLayout, resolveMaxLines, resolveTtlSec, splitTarget, ttlLabel };
@@ -1,5 +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 { a as DEFAULT_TTL_SEC, c as ttlLabel, i as parseEnvFile, n as envCandidates, o as TTL_ENV_KEY, r as loadDotenv, s as resolveTtlSec, t as HOME_DIR } from "../dotenv-B9nM4cuQ.mjs";
2
+ import { a as DEFAULT_TTL_SEC, c as ttlLabel, i as parseEnvFile, n as envCandidates, o as TTL_ENV_KEY, r as loadDotenv, s as resolveTtlSec, t as HOME_DIR } from "../dotenv-Jj8aL1FL.mjs";
3
3
  import { n as STATE_ROOT, r as projectLayout, t as STATE_GITIGNORE } from "../layout-C0jaaCQC.mjs";
4
4
  import "../config-la_KkjCS.mjs";
5
5
  export { DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, HOME_DIR, MAX_LINES_ENV_KEY, STATE_GITIGNORE, STATE_ROOT, TTL_ENV_KEY, envCandidates, loadDotenv, parseEnvFile, parseEnvInt, projectLayout, resolveMaxLines, resolveTtlSec, splitTarget, ttlLabel };
@@ -1,2 +1,2 @@
1
- import { a as HarnessInfo, i as HarnessId, n as detectMode, o as HarnessMode, r as modeFor, s as HarnessVia, t as detectHarness } from "../harness-BPPu5CrN.mjs";
1
+ import { a as HarnessInfo, i as HarnessId, n as detectMode, o as HarnessMode, r as modeFor, s as HarnessVia, t as detectHarness } from "../harness-s1Fce_dL.mjs";
2
2
  export { type HarnessId, type HarnessInfo, type HarnessMode, type HarnessVia, detectHarness, detectMode, modeFor };
@@ -1,3 +1,3 @@
1
- import { n as detectMode, r as modeFor, t as detectHarness } from "../harness-C8Nxxyn_.mjs";
1
+ import { n as detectMode, r as modeFor, t as detectHarness } from "../harness-Cb9xR8dC.mjs";
2
2
  import "../detect-la_KkjCS.mjs";
3
3
  export { detectHarness, detectMode, modeFor };
@@ -12,6 +12,8 @@ interface AuthEntry {
12
12
  session?: string;
13
13
  doc_sessions?: string[];
14
14
  read_paths?: string[];
15
+ /** ISO stamp of the latest doc/skill consultation for this framework (Check-1 TTL, parity enforce-apex-phases.ts). */
16
+ doc_consulted?: string;
15
17
  }
16
18
  /** Resolve the sessions array from an auth entry (legacy fallback). */
17
19
  declare function resolveSessions(auth: AuthEntry | undefined): string[];
@@ -41,7 +41,8 @@ const HOME_DIR = {
41
41
  cursor: ".cursor",
42
42
  cline: ".clinerules",
43
43
  "gemini-cli": ".gemini",
44
- opencode: ".opencode"
44
+ opencode: ".opencode",
45
+ hermes: ".hermes"
45
46
  };
46
47
  /** Parse a `.env` file into a key→value map (`export KEY="v"` or `KEY=v`). */
47
48
  function parseEnvFile(path) {
@@ -1,89 +1,7 @@
1
- import { i as splitTarget, r as resolveMaxLines } from "./limits-CHn8AIL1.mjs";
2
- import { r as fusengineCache, t as claudeHome } from "./home-state-mKZxP4oZ.mjs";
3
- import { basename, dirname, join, normalize } from "node:path";
1
+ import { f as countLines, p as evaluateFileSize, r as fusengineCache, t as claudeHome } from "./home-state-D0RLWP8J.mjs";
2
+ import { dirname, join, normalize } from "node:path";
4
3
  import { existsSync } from "node:fs";
5
4
  import { homedir } from "node:os";
6
- //#region src/policy/file-size.ts
7
- /**
8
- * Fixed marketplace plugins root — parity with Python `enforce-file-size.py`'s
9
- * literal, unexpanded `~/...` string. Exported: reused by
10
- * `policy/apex.ts::solidReadGate` for its "no reference matched" deny message.
11
- */
12
- const PLUGINS_DIR = "~/.claude/plugins/marketplaces/fusengine-plugins/plugins";
13
- /**
14
- * Skill-dir fragment per framework — parity with Python
15
- * `enforce-file-size.py::get_solid_ref()` (falls back to `generic/`). Exported:
16
- * reused by `policy/apex.ts::solidReadGate` (see {@link PLUGINS_DIR}).
17
- */
18
- const SOLID_REF = {
19
- react: "react-expert/skills/solid-react/",
20
- nextjs: "nextjs-expert/skills/solid-nextjs/",
21
- laravel: "laravel-expert/skills/solid-php/",
22
- swift: "swift-apple-expert/skills/solid-swift/"
23
- };
24
- /**
25
- * Count physical lines — parity with the Python `enforce-file-size.py`
26
- * (`sum(1 for _ in f)`): every line counts (blanks and comments included), and a
27
- * single trailing newline does not add a phantom line. The SOLID ceiling is
28
- * measured on raw file length, not substantive code, to match the upstream plugin.
29
- */
30
- function countLines(content) {
31
- if (content === "") return 0;
32
- return content.split("\n").length - (content.endsWith("\n") ? 1 : 0);
33
- }
34
- /**
35
- * Count non-empty, non-comment lines — parity with the Python `count_code_lines`
36
- * shared by the framework-specific SOLID validators: `_shared/scripts/validate_solid_common.py`
37
- * (imported by `nextjs-expert/scripts/validate-nextjs-solid.py` and
38
- * `swift-apple-expert/scripts/validate-swift-solid.py`), duplicated verbatim in
39
- * `react-expert/scripts/validate-react-solid.py` / `laravel-expert/scripts/validate-laravel-solid.py`.
40
- * Strips blank lines and lines starting with `//` or `*` — a SINGLE fixed rule
41
- * for all 4 callers (the Python `comment` param defaults to, and every real
42
- * call site leaves it at, `"//"` — never per-language despite covering
43
- * ts/tsx/js/jsx, php and swift).
44
- *
45
- * Deliberately distinct from two other "code-only" counters already in this
46
- * repo, neither of which is a faithful substitute here:
47
- * - `countLoc` (`runtime/lifecycle/check-file-size.ts`): a genuinely
48
- * per-language table (PHP additionally strips `#`, Python strips
49
- * `#`/`"""`/`'''`) — ported from the unrelated `solid/scripts/check-file-size.py`.
50
- * - `countCodeLines` (`runtime/lifecycle/aipilot/solid-compliance.ts`): also
51
- * strips `#` — ported from `ai-pilot/scripts/check-solid-compliance.py`.
52
- * Reusing either would silently strip PHP `#`/Python-style comments that the
53
- * real react/nextjs/laravel/swift validators do NOT strip.
54
- * @param content - The file content to measure.
55
- */
56
- function countFrameworkCodeLines(content) {
57
- let count = 0;
58
- for (const raw of content.split("\n")) {
59
- const line = raw.trim();
60
- if (!line || line.startsWith("//") || line.startsWith("*")) continue;
61
- count++;
62
- }
63
- return count;
64
- }
65
- /**
66
- * Evaluate a file's line count against the SOLID limit.
67
- * @param lines - the file's line count
68
- * @param max - the limit (defaults to `resolveMaxLines()`)
69
- */
70
- function evaluateFileSize(lines, max = resolveMaxLines(), filePath = "", framework = "generic", displayLines = lines) {
71
- if (lines <= max) return {
72
- ok: true,
73
- lines,
74
- max,
75
- message: null
76
- };
77
- const split = splitTarget(max);
78
- const fname = filePath ? basename(filePath) : "file";
79
- return {
80
- ok: false,
81
- lines,
82
- max,
83
- message: `BLOCKED: '${fname}' has ${displayLines} lines (max: ${max}). TO SPLIT: 1) Read SOLID rules: ${PLUGINS_DIR}/${SOLID_REF[framework] ?? "generic/"} 2) Create new module files (<${split} lines each) 3) Use Write to replace '${fname}' with <${max} lines version.`
84
- };
85
- }
86
- //#endregion
87
5
  //#region src/policy/patterns.ts
88
6
  /**
89
7
  * Guard pattern data, ported verbatim from the fusengine git/install guards.
@@ -820,4 +738,4 @@ function evaluate(ctx) {
820
738
  };
821
739
  }
822
740
  //#endregion
823
- export { PROJECT_INSTALL as A, PROTECTED_GIT_RE as C, securityGuard as D, CRITICAL_PATTERNS as E, PLUGINS_DIR as F, SOLID_REF as I, countFrameworkCodeLines as L, SYSTEM_INSTALL as M, isRalphMode as N, GIT_ASK as O, matchPatterns as P, countLines as R, PROTECTED_FRAGMENTS as S, ASK_PATTERNS as T, CODE_MUTATORS as _, registerGuard as a, SAFE_PREFIXES as b, GO_DECL_RE as c, PY_MODEL_RE as d, SWIFT_PROTO_RE as f, ASK_WRITERS as g, bashWriteGuard as h, clearUserGuards as i, RALPH_SAFE as j, GIT_BLOCKED as k, JAVA_DECL_RE as l, interfaceSeparationGuard as m, FAIL_CLOSED as n, runGuards as o, TS_DECL_RE as p, GUARDS as r, installGuard as s, evaluate as t, PHP_DECL_RE as u, CODE_REDIRECT as v, protectedPathGuard as w, SESSION_STATE_FRAGMENT as x, FILE_REDIRECT as y, evaluateFileSize as z };
741
+ export { PROJECT_INSTALL as A, PROTECTED_GIT_RE as C, securityGuard as D, CRITICAL_PATTERNS as E, SYSTEM_INSTALL as M, isRalphMode as N, GIT_ASK as O, matchPatterns as P, PROTECTED_FRAGMENTS as S, ASK_PATTERNS as T, CODE_MUTATORS as _, registerGuard as a, SAFE_PREFIXES as b, GO_DECL_RE as c, PY_MODEL_RE as d, SWIFT_PROTO_RE as f, ASK_WRITERS as g, bashWriteGuard as h, clearUserGuards as i, RALPH_SAFE as j, GIT_BLOCKED as k, JAVA_DECL_RE as l, interfaceSeparationGuard as m, FAIL_CLOSED as n, runGuards as o, TS_DECL_RE as p, GUARDS as r, installGuard as s, evaluate as t, PHP_DECL_RE as u, CODE_REDIRECT as v, protectedPathGuard as w, SESSION_STATE_FRAGMENT as x, FILE_REDIRECT as y };
@@ -1,3 +1,3 @@
1
- import { a as isDocConsulted, i as formatDocSatisfactionStatus, n as DocSatisfactionStatus, o as resolveSessions, r as formatDocDeny, t as AuthEntry } from "../doc-helpers-BNfYWvYv.mjs";
1
+ import { a as isDocConsulted, i as formatDocSatisfactionStatus, n as DocSatisfactionStatus, o as resolveSessions, r as formatDocDeny, t as AuthEntry } from "../doc-helpers-CEKzGg2u.mjs";
2
2
  import { t as incrementTrivialEditCounter } from "../index-BOBXQ91y.mjs";
3
3
  export { AuthEntry, DocSatisfactionStatus, formatDocDeny, formatDocSatisfactionStatus, incrementTrivialEditCounter, isDocConsulted, resolveSessions };