@fusengine/harness 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +106 -41
- package/dist/cache/index.d.mts +2 -2
- package/dist/cache/index.mjs +3 -2
- package/dist/{cache-DbPSJ9bC.mjs → cache-BzbX-ztL.mjs} +1 -25
- package/dist/cli/bin.mjs +1 -1
- package/dist/{handle-Cr0YQYz-.mjs → handle-o5np-3T_.mjs} +70 -3
- package/dist/{index-CGFPvQ69.d.mts → index-CQOPshSM.d.mts} +10 -1
- package/dist/{index-B3Ve_bBu.d.mts → index-DPkCX_AR.d.mts} +11 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +4 -3
- package/dist/policy/index.d.mts +2 -2
- package/dist/policy/index.mjs +2 -2
- package/dist/runtime/index.d.mts +17 -1
- package/dist/runtime/index.mjs +2 -2
- package/dist/store-BlaEhjab.mjs +60 -0
- package/dist/{apex-BcJSE-VL.mjs → verbosity-ZYT7tLCw.mjs} +20 -1
- package/package.json +111 -23
package/README.md
CHANGED
|
@@ -1,68 +1,133 @@
|
|
|
1
1
|
# @fusengine/harness
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
A **harness-agnostic enforcement engine** for AI coding agents. It ports the
|
|
4
|
+
guard/gate logic of a Claude Code plugin into one reusable, **Bun-native** npm
|
|
5
|
+
package that runs on **any** harness — Claude Code, OpenAI Codex, Cursor, Cline,
|
|
6
|
+
Gemini CLI — plus a cli-mode fallback for Aider / Windsurf / OpenHands.
|
|
5
7
|
|
|
6
|
-
It splits cleanly into a **pure policy core** (no harness coupling
|
|
7
|
-
adapters** that
|
|
8
|
+
It splits cleanly into a **pure policy core** (no harness coupling, fully tested)
|
|
9
|
+
and **thin per-harness adapters** that map a hook payload to the policy and back
|
|
10
|
+
to that harness's native response.
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
git guards, project memory) was duplicated across Python + TypeScript hooks and
|
|
13
|
-
bound to one harness. This package is the single, tested source of truth — and
|
|
14
|
-
it knows which harness it's running in.
|
|
12
|
+
```
|
|
13
|
+
detect → init (pre+post hooks) → `harness hook` → guards + APEX gates → native deny/ask
|
|
14
|
+
```
|
|
15
15
|
|
|
16
16
|
## Install
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
|
-
|
|
19
|
+
npm i -g @fusengine/harness # for the CLI (harness init/hook/check)
|
|
20
|
+
# or, as a library:
|
|
21
|
+
bun add @fusengine/harness # Bun reads the TS source directly — no build step
|
|
20
22
|
```
|
|
21
23
|
|
|
22
|
-
##
|
|
24
|
+
## Quickstart
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
| `@fusengine/harness/memory` | Per-project "never reproduce" lessons: throttle state, multi-project registry by git root. |
|
|
30
|
-
| `@fusengine/harness/cache` | `compactMarkdown`, `queryHash`, `jaccardSimilar`, atomic JSON I/O, MCP response extraction. |
|
|
31
|
-
| `@fusengine/harness/freshness` | `isDocConsulted` (Context7 + Exa), trivial-edit counter. |
|
|
32
|
-
| `@fusengine/harness/refs` | Frontmatter parsing, glob→regex, SOLID reference scoring/routing. |
|
|
33
|
-
| `@fusengine/harness/state` | Directory locks, daily APEX state, task.json helpers. |
|
|
34
|
-
| `@fusengine/harness/statusline` | Formatters, ANSI colors, progress/gradient bars. |
|
|
35
|
-
| `@fusengine/harness/adapters/claude` | Claude Code adapter: read stdin → policy → `hookSpecificOutput`. |
|
|
36
|
-
|
|
37
|
-
## Usage
|
|
26
|
+
```sh
|
|
27
|
+
cd your-project
|
|
28
|
+
harness init # detects the harness, writes its pre+post hooks
|
|
29
|
+
export FUSE_HARNESS_REFS=.claude/skills # (optional) activate the SOLID-read gate
|
|
30
|
+
```
|
|
38
31
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
That's it. `init` writes the wiring file for the detected harness
|
|
33
|
+
(`.claude/settings.json`, `.codex/hooks.json`, `.cursor/hooks.json`,
|
|
34
|
+
`.gemini/settings.json`, or `.clinerules/hooks/PreToolUse`+`PostToolUse`), each
|
|
35
|
+
pointing at `harness hook <id>`. From then on every tool-use is gated, and the
|
|
36
|
+
session activity (agents run, docs consulted, refs read) is recorded
|
|
37
|
+
automatically under `<harness-dir>/harness/`.
|
|
42
38
|
|
|
43
|
-
|
|
39
|
+
### CLI
|
|
44
40
|
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
| Command | What it does |
|
|
42
|
+
|---------|--------------|
|
|
43
|
+
| `harness init [id]` | Write the pre+post hook wiring for the detected (or named) harness. |
|
|
44
|
+
| `harness hook <id>` | Runtime: read a hook payload on stdin, gate (pre) or record (post), print the native response. (Hooks call this — you don't.) |
|
|
45
|
+
| `harness check` | cli-mode: check staged files in a pre-commit step, exit non-zero on a violation. For harnesses without hooks. |
|
|
46
|
+
|
|
47
|
+
cli-mode (Aider / Windsurf / OpenHands), as a pre-commit step:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
# .husky/pre-commit
|
|
51
|
+
npx harness check
|
|
47
52
|
```
|
|
48
53
|
|
|
49
|
-
|
|
54
|
+
## What it enforces
|
|
55
|
+
|
|
56
|
+
Ten portable guards + the APEX gate chain, all evaluated before a tool runs:
|
|
57
|
+
|
|
58
|
+
| Guard / gate | Fires on |
|
|
59
|
+
|---|---|
|
|
60
|
+
| file-size (SOLID) | a code file over `FUSE_SOLID_MAX_LINES` (default 100) |
|
|
61
|
+
| git | destructive git (`push --force`, `reset --hard`, …) |
|
|
62
|
+
| bash-write | `python3 -c` / `sed -i` / redirects to code files |
|
|
63
|
+
| install | `npm/pip/brew/...` installs (asks) |
|
|
64
|
+
| security | `rm -rf /`, fork bombs, `curl \| sh`; `sudo` (asks) |
|
|
65
|
+
| interface-separation | top-level interface/type/protocol in a component/controller |
|
|
66
|
+
| protected-path | edits to `.claude/plugins\|logs\|cache`, `.git/` |
|
|
67
|
+
| APEX freshness | `explore-codebase` + `research-expert` not run within the window |
|
|
68
|
+
| APEX doc-consulted | Context7 **and** Exa not consulted this session |
|
|
69
|
+
| APEX solid-read | required SOLID refs (from `FUSE_HARNESS_REFS`) not read |
|
|
70
|
+
| brainstorm | creating a new file without brainstorming (when flagged) |
|
|
71
|
+
| MCP verbosity / cache | caps exa `numResults`; serves a fresh cached MCP/WebFetch result |
|
|
72
|
+
|
|
73
|
+
A trivial-edit fast path lets a few tiny (< 5-line, non-`replace_all`) edits
|
|
74
|
+
through per window without the full APEX gates.
|
|
75
|
+
|
|
76
|
+
### Environment
|
|
77
|
+
|
|
78
|
+
| Var | Effect |
|
|
79
|
+
|---|---|
|
|
80
|
+
| `FUSE_SOLID_MAX_LINES` | SOLID file-size limit (default `100`). |
|
|
81
|
+
| `FUSE_HARNESS_REFS` | Directory of `.md` SOLID references → activates `solidReadGate`. |
|
|
82
|
+
| `FUSE_ENFORCE_TTL_SEC` | APEX freshness window in seconds. |
|
|
83
|
+
| `FUSE_LESSONS_THROTTLE_MIN` | Lessons-injection throttle (memory module). |
|
|
84
|
+
|
|
85
|
+
## Library usage
|
|
50
86
|
|
|
51
87
|
```ts
|
|
52
|
-
import {
|
|
88
|
+
import { detectHarness } from "@fusengine/harness/detect";
|
|
89
|
+
import { evaluate } from "@fusengine/harness/policy";
|
|
90
|
+
import { gate } from "@fusengine/harness/runtime";
|
|
91
|
+
|
|
92
|
+
const { id, mode } = detectHarness(); // { id: "cursor", mode: "hook" }
|
|
53
93
|
|
|
54
|
-
|
|
55
|
-
|
|
94
|
+
// stateless guards (file-size, git, security, …)
|
|
95
|
+
const verdict = evaluate({ tool: "Write", filePath: "src/big.ts", content });
|
|
96
|
+
if (verdict.decision !== "allow") console.error(verdict.prompt?.reason);
|
|
97
|
+
|
|
98
|
+
// full gate (stateless + stateful APEX, fed from the session track)
|
|
99
|
+
const prompt = await gate({ sessionId, framework: "react", tool: "Write",
|
|
100
|
+
filePath: "src/Button.tsx", content, now: Date.now(), trackFile });
|
|
56
101
|
```
|
|
57
102
|
|
|
58
|
-
|
|
59
|
-
|
|
103
|
+
The `Prompt` it returns (`{ kind: "block" | "ask" | "inform", title, reason, actions? }`)
|
|
104
|
+
is portable; each adapter maps it to the harness's native shape.
|
|
105
|
+
|
|
106
|
+
## Subpath exports
|
|
107
|
+
|
|
108
|
+
| Subpath | What |
|
|
109
|
+
|---------|------|
|
|
110
|
+
| `./detect` | `detectHarness()` / `detectMode()` — 13 harnesses, `hook` vs `cli`. |
|
|
111
|
+
| `./policy` | `evaluate(ctx)`, the 10 guards, `evaluateApex`, framework detection. |
|
|
112
|
+
| `./runtime` | `handleHook`, `gate`, `recordActivity`, `activityFor`, per-harness storage + MCP intercept. |
|
|
113
|
+
| `./tracking` | Session track: `recordAgent/Doc/RefRead`, `agentsFresh`, trivial-edit counter. |
|
|
114
|
+
| `./refs` | Frontmatter parse, `loadRefs(dir)`, SOLID ref scoring/routing. |
|
|
115
|
+
| `./prompt` | The portable `Prompt` type + `formatPrompt`. |
|
|
116
|
+
| `./cache` | MCP/WebFetch cache: key, lookup/store, compaction, response extraction. |
|
|
117
|
+
| `./memory` | Per-project "never reproduce" lessons. |
|
|
118
|
+
| `./config` `./util` `./state` `./statusline` `./freshness` `./init` `./cli` | env config, project-root, locks, statusline, doc-freshness, wiring templates, staged checks. |
|
|
119
|
+
| `./adapters/{claude,codex,cursor,cline,gemini}` | Thin per-harness adapters. |
|
|
120
|
+
|
|
121
|
+
See [`docs/`](./docs) for per-module guides, and run `bun run docs:api` for the
|
|
122
|
+
generated typedoc API reference.
|
|
60
123
|
|
|
61
124
|
## Develop
|
|
62
125
|
|
|
63
126
|
```sh
|
|
64
|
-
bun test
|
|
65
|
-
bunx tsc --noEmit
|
|
127
|
+
bun test # 105 tests
|
|
128
|
+
bunx tsc --noEmit # typecheck (isolatedDeclarations)
|
|
129
|
+
bun run build # dist + .d.mts via tsdown (for Node/bundler consumers)
|
|
130
|
+
bun run docs:api # generate the typedoc API reference
|
|
66
131
|
```
|
|
67
132
|
|
|
68
|
-
CI runs
|
|
133
|
+
CI runs test + typecheck on every PR. MIT licensed.
|
package/dist/cache/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { IndexSummary, compactMarkdown, extractText, jaccardSimilar, loadIndex, queryHash, summarizeIndex };
|
|
1
|
+
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";
|
|
2
|
+
export { IndexSummary, cacheLookup, cachePath, cacheStore, compactMarkdown, extractText, jaccardSimilar, loadIndex, mcpCacheKey, queryHash, summarizeIndex };
|
package/dist/cache/index.mjs
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
|
|
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";
|
|
3
|
+
export { cacheLookup, cachePath, cacheStore, compactMarkdown, extractText, jaccardSimilar, loadIndex, mcpCacheKey, queryHash, summarizeIndex };
|
|
@@ -83,28 +83,4 @@ function summarizeIndex(index) {
|
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
85
|
//#endregion
|
|
86
|
-
|
|
87
|
-
const MAX_DEPTH = 5;
|
|
88
|
-
/**
|
|
89
|
-
* Extract usable markdown from an MCP `tool_response`: a string, a list of
|
|
90
|
-
* content blocks (non-text blocks skipped), or any JSON structure (fallback).
|
|
91
|
-
* Recurses up to depth 5 to guard against pathological/cyclic structures.
|
|
92
|
-
*/
|
|
93
|
-
function extractText(resp, depth = 0) {
|
|
94
|
-
if (depth >= MAX_DEPTH) return "";
|
|
95
|
-
if (typeof resp === "string") return resp;
|
|
96
|
-
if (Array.isArray(resp)) {
|
|
97
|
-
const parts = resp.filter((b) => typeof b === "object" && b !== null).filter((b) => b.type === "text").map((b) => b.text ?? "");
|
|
98
|
-
if (parts.length) return parts.join("\n\n");
|
|
99
|
-
const joined = resp.filter((b) => Array.isArray(b) || typeof b === "object" && b !== null).map((b) => extractText(b, depth + 1)).filter(Boolean).join("\n\n");
|
|
100
|
-
if (joined) return joined;
|
|
101
|
-
}
|
|
102
|
-
if (!resp) return "";
|
|
103
|
-
try {
|
|
104
|
-
return JSON.stringify(resp);
|
|
105
|
-
} catch {
|
|
106
|
-
return "";
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
//#endregion
|
|
110
|
-
export { jaccardSimilar as a, compactMarkdown as i, loadIndex as n, queryHash as o, summarizeIndex as r, extractText as t };
|
|
86
|
+
export { queryHash as a, jaccardSimilar as i, summarizeIndex as n, compactMarkdown as r, loadIndex as t };
|
package/dist/cli/bin.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { t as detectHarness } from "../harness-C8Nxxyn_.mjs";
|
|
3
3
|
import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-D2na7Z2Z.mjs";
|
|
4
4
|
import { n as writeInitFile, t as initFor } from "../run-Cdp2Ef9B.mjs";
|
|
5
|
-
import { t as handleHook } from "../handle-
|
|
5
|
+
import { t as handleHook } from "../handle-o5np-3T_.mjs";
|
|
6
6
|
//#region src/cli/bin.ts
|
|
7
7
|
/**
|
|
8
8
|
* harness — CLI for @fusengine/harness.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { O as detectFramework, t as evaluate } from "./evaluate-DkTgwHNh.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { n as capVerbosity, o as evaluateApex } from "./verbosity-ZYT7tLCw.mjs";
|
|
3
3
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
4
|
+
import { a as extractText, r as cacheStore, t as cacheLookup } from "./store-BlaEhjab.mjs";
|
|
4
5
|
import { t as loadRefs } from "./loader-BephwI8n.mjs";
|
|
5
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";
|
|
6
7
|
import { join } from "node:path";
|
|
@@ -86,6 +87,64 @@ async function gate(input) {
|
|
|
86
87
|
});
|
|
87
88
|
}
|
|
88
89
|
//#endregion
|
|
90
|
+
//#region src/runtime/mcp.ts
|
|
91
|
+
/** Default freshness for cached MCP/WebFetch results (48h). */
|
|
92
|
+
const MCP_TTL_MS = 1728e5;
|
|
93
|
+
/** MCP doc tools + WebFetch whose calls are cached / verbosity-capped. */
|
|
94
|
+
function isMcpTool(tool) {
|
|
95
|
+
return /context7|exa|webfetch|web_fetch/i.test(tool) || tool === "WebFetch";
|
|
96
|
+
}
|
|
97
|
+
/** The query/url that keys the cache. */
|
|
98
|
+
function queryOf(input) {
|
|
99
|
+
const q = input.query ?? input.url ?? input.libraryId ?? "";
|
|
100
|
+
return typeof q === "string" ? q : JSON.stringify(q);
|
|
101
|
+
}
|
|
102
|
+
function denyWith(id, content) {
|
|
103
|
+
if (id === "claude-code" || id === "codex") return JSON.stringify({ hookSpecificOutput: {
|
|
104
|
+
hookEventName: "PreToolUse",
|
|
105
|
+
permissionDecision: "deny",
|
|
106
|
+
permissionDecisionReason: content
|
|
107
|
+
} });
|
|
108
|
+
if (id === "gemini-cli") return JSON.stringify({
|
|
109
|
+
decision: "deny",
|
|
110
|
+
reason: content
|
|
111
|
+
});
|
|
112
|
+
return "";
|
|
113
|
+
}
|
|
114
|
+
function mutateWith(id, input) {
|
|
115
|
+
if (id === "claude-code" || id === "codex") return JSON.stringify({ hookSpecificOutput: {
|
|
116
|
+
hookEventName: "PreToolUse",
|
|
117
|
+
permissionDecision: "allow",
|
|
118
|
+
updatedInput: input
|
|
119
|
+
} });
|
|
120
|
+
if (id === "gemini-cli") return JSON.stringify({ hookSpecificOutput: { tool_input: input } });
|
|
121
|
+
return "";
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Pre-event MCP interception: serve a fresh cache hit (deny + cached content),
|
|
125
|
+
* else cap exa verbosity (allow + mutated input), else null to allow normally.
|
|
126
|
+
* Harnesses without input-mutation/cache support fall through to null.
|
|
127
|
+
*/
|
|
128
|
+
function mcpPreIntercept(id, tool, input, dir, ttlMs, now) {
|
|
129
|
+
if (!isMcpTool(tool)) return null;
|
|
130
|
+
const cached = cacheLookup(dir, tool, queryOf(input), ttlMs, now);
|
|
131
|
+
if (cached) {
|
|
132
|
+
const served = denyWith(id, cached);
|
|
133
|
+
if (served) return served;
|
|
134
|
+
}
|
|
135
|
+
const capped = capVerbosity(tool, input);
|
|
136
|
+
if (capped) {
|
|
137
|
+
const mutated = mutateWith(id, capped);
|
|
138
|
+
if (mutated) return mutated;
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
/** Post-event: store the MCP/WebFetch response (extracted to markdown) in the cache. */
|
|
143
|
+
function mcpPostStore(tool, input, response, dir) {
|
|
144
|
+
if (!isMcpTool(tool)) return;
|
|
145
|
+
cacheStore(dir, tool, queryOf(input), extractText(response));
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
89
148
|
//#region src/runtime/normalize.ts
|
|
90
149
|
function str(v) {
|
|
91
150
|
return typeof v === "string" ? v : void 0;
|
|
@@ -197,9 +256,12 @@ function harnessTrackDir(id, projectRoot) {
|
|
|
197
256
|
*/
|
|
198
257
|
async function handleHook(id, payload, opts) {
|
|
199
258
|
const event = normalizeEvent(id, payload);
|
|
200
|
-
const
|
|
259
|
+
const dir = harnessTrackDir(id, opts.cwd);
|
|
260
|
+
const file = trackFile(event.sessionId, dir);
|
|
261
|
+
const mcpDir = join(dir, "mcp");
|
|
201
262
|
const framework = detectFramework(event.filePath ?? "", event.content ?? "");
|
|
202
263
|
if (event.phase === "post") {
|
|
264
|
+
mcpPostStore(event.tool, event.input, payload.tool_response ?? payload.tool_output, mcpDir);
|
|
203
265
|
const activity = activityFor({
|
|
204
266
|
tool: event.tool,
|
|
205
267
|
input: event.input,
|
|
@@ -213,6 +275,11 @@ async function handleHook(id, payload, opts) {
|
|
|
213
275
|
exit: 0
|
|
214
276
|
};
|
|
215
277
|
}
|
|
278
|
+
const intercept = mcpPreIntercept(id, event.tool, event.input, mcpDir, MCP_TTL_MS, opts.now);
|
|
279
|
+
if (intercept !== null) return {
|
|
280
|
+
stdout: intercept,
|
|
281
|
+
exit: 0
|
|
282
|
+
};
|
|
216
283
|
const prompt = await gate({
|
|
217
284
|
sessionId: event.sessionId,
|
|
218
285
|
framework,
|
|
@@ -234,4 +301,4 @@ async function handleHook(id, payload, opts) {
|
|
|
234
301
|
};
|
|
235
302
|
}
|
|
236
303
|
//#endregion
|
|
237
|
-
export { trackFile as a,
|
|
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 };
|
|
@@ -198,4 +198,13 @@ declare function runGuards(ctx: GuardContext): Prompt | null;
|
|
|
198
198
|
*/
|
|
199
199
|
declare function detectCreationIntent(prompt: string): boolean;
|
|
200
200
|
//#endregion
|
|
201
|
-
|
|
201
|
+
//#region src/policy/verbosity.d.ts
|
|
202
|
+
/** Max results an exa MCP call may request. */
|
|
203
|
+
declare const MAX_EXA_RESULTS = 3;
|
|
204
|
+
/**
|
|
205
|
+
* Cap an exa MCP call to {@link MAX_EXA_RESULTS} results. Returns the capped
|
|
206
|
+
* input (a mutation for the harness to apply) when a cap is needed, else null.
|
|
207
|
+
*/
|
|
208
|
+
declare function capVerbosity(tool: string, input: Record<string, unknown>): Record<string, unknown> | null;
|
|
209
|
+
//#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 };
|
|
@@ -27,4 +27,14 @@ declare function summarizeIndex(index: unknown[]): IndexSummary;
|
|
|
27
27
|
*/
|
|
28
28
|
declare function extractText(resp: unknown, depth?: number): string;
|
|
29
29
|
//#endregion
|
|
30
|
-
|
|
30
|
+
//#region src/cache/store.d.ts
|
|
31
|
+
/** Stable 16-char key from a tool name + query (FNV-1a, non-crypto). */
|
|
32
|
+
declare function mcpCacheKey(tool: string, query: string): string;
|
|
33
|
+
/** Path of a cache entry under `dir`. */
|
|
34
|
+
declare function cachePath(dir: string, tool: string, query: string): string;
|
|
35
|
+
/** Read a cached entry if it exists and is fresh (mtime within `ttlMs`), else null. */
|
|
36
|
+
declare function cacheLookup(dir: string, tool: string, query: string, ttlMs: number, now: number): string | null;
|
|
37
|
+
/** Store a cache entry (creates the dir; no-op on empty content). */
|
|
38
|
+
declare function cacheStore(dir: string, tool: string, query: string, content: string): void;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { extractText as a, summarizeIndex as c, queryHash as d, mcpCacheKey as i, compactMarkdown as l, cachePath as n, IndexSummary as o, cacheStore as r, loadIndex as s, cacheLookup as t, jaccardSimilar 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
|
-
import { a as
|
|
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
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";
|
|
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
|
|
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";
|
|
9
9
|
import { n as RouteResult, r as ScoredRef, t as RefMeta } from "./types-CY5qT2X1.mjs";
|
|
10
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";
|
|
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_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, 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, 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, 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 };
|
package/dist/index.mjs
CHANGED
|
@@ -7,12 +7,13 @@ import { i as isApexCommand, n as DEV_KEYWORDS, r as detectProjectType, t as det
|
|
|
7
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";
|
|
8
8
|
import { i as resolveSessions, n as formatDocSatisfactionStatus, r as isDocConsulted, t as formatDocDeny } from "./doc-helpers-Dd_x1-tZ.mjs";
|
|
9
9
|
import { i as parseFrontmatter, n as scoreReferences, r as globToRe, t as routeReferences } from "./router-Dj3AfgBE.mjs";
|
|
10
|
-
import { a as
|
|
10
|
+
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
11
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
12
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
|
|
13
|
+
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";
|
|
14
15
|
import { t as incrementTrivialEditCounter } from "./freshness-BK9Xg6oG.mjs";
|
|
15
16
|
import { n as toRefMeta, t as loadRefs } from "./loader-BephwI8n.mjs";
|
|
16
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";
|
|
17
18
|
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";
|
|
18
|
-
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_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, 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, 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 };
|
|
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 };
|
package/dist/policy/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as
|
|
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, 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, countLines, detectCreationIntent, detectFramework, detectProjectType, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, matchPatterns, protectedPathGuard, runGuards, securityGuard, solidReadGate };
|
|
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 };
|
package/dist/policy/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { i as isApexCommand, n as DEV_KEYWORDS, r as detectProjectType, t as detectCreationIntent } from "../policy-Djmqxow2.mjs";
|
|
2
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";
|
|
3
|
-
import { a as
|
|
4
|
-
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, CRITICAL_PATTERNS, DEV_KEYWORDS, GIT_ASK, GIT_BLOCKED, GUARDS, 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, countLines, detectCreationIntent, detectFramework, detectProjectType, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, matchPatterns, protectedPathGuard, runGuards, securityGuard, solidReadGate };
|
|
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 };
|
package/dist/runtime/index.d.mts
CHANGED
|
@@ -105,6 +105,22 @@ declare function normalizeEvent(id: string, payload: Record<string, unknown>): N
|
|
|
105
105
|
*/
|
|
106
106
|
declare function respond(id: string, prompt: Prompt): string;
|
|
107
107
|
//#endregion
|
|
108
|
+
//#region src/runtime/mcp.d.ts
|
|
109
|
+
/** Default freshness for cached MCP/WebFetch results (48h). */
|
|
110
|
+
declare const MCP_TTL_MS = 1728e5;
|
|
111
|
+
/** MCP doc tools + WebFetch whose calls are cached / verbosity-capped. */
|
|
112
|
+
declare function isMcpTool(tool: string): boolean;
|
|
113
|
+
/** The query/url that keys the cache. */
|
|
114
|
+
declare function queryOf(input: Record<string, unknown>): string;
|
|
115
|
+
/**
|
|
116
|
+
* Pre-event MCP interception: serve a fresh cache hit (deny + cached content),
|
|
117
|
+
* else cap exa verbosity (allow + mutated input), else null to allow normally.
|
|
118
|
+
* Harnesses without input-mutation/cache support fall through to null.
|
|
119
|
+
*/
|
|
120
|
+
declare function mcpPreIntercept(id: string, tool: string, input: Record<string, unknown>, dir: string, ttlMs: number, now: number): string | null;
|
|
121
|
+
/** Post-event: store the MCP/WebFetch response (extracted to markdown) in the cache. */
|
|
122
|
+
declare function mcpPostStore(tool: string, input: Record<string, unknown>, response: unknown, dir: string): void;
|
|
123
|
+
//#endregion
|
|
108
124
|
//#region src/runtime/handle.d.ts
|
|
109
125
|
/** Options for {@link handleHook} (caller supplies the clock + project root). */
|
|
110
126
|
interface HandleOptions {
|
|
@@ -126,4 +142,4 @@ interface HandleOutcome {
|
|
|
126
142
|
*/
|
|
127
143
|
declare function handleHook(id: string, payload: Record<string, unknown>, opts: HandleOptions): Promise<HandleOutcome>;
|
|
128
144
|
//#endregion
|
|
129
|
-
export { Activity, DEFAULT_WINDOW_MS, GateInput, HandleOptions, HandleOutcome, NormalizedEvent, REQUIRED_AGENTS, TRIVIAL_BUDGET, ToolEvent, activityFor, gate, handleHook, harnessTrackDir, normalizeEvent, recordActivity, respond, trackFile };
|
|
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 };
|
package/dist/runtime/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as trackFile, c as
|
|
2
|
-
export { DEFAULT_WINDOW_MS, REQUIRED_AGENTS, TRIVIAL_BUDGET, activityFor, gate, handleHook, harnessTrackDir, normalizeEvent, recordActivity, respond, trackFile };
|
|
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 };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
//#region src/cache/mcp-response.ts
|
|
4
|
+
const MAX_DEPTH = 5;
|
|
5
|
+
/**
|
|
6
|
+
* Extract usable markdown from an MCP `tool_response`: a string, a list of
|
|
7
|
+
* content blocks (non-text blocks skipped), or any JSON structure (fallback).
|
|
8
|
+
* Recurses up to depth 5 to guard against pathological/cyclic structures.
|
|
9
|
+
*/
|
|
10
|
+
function extractText(resp, depth = 0) {
|
|
11
|
+
if (depth >= MAX_DEPTH) return "";
|
|
12
|
+
if (typeof resp === "string") return resp;
|
|
13
|
+
if (Array.isArray(resp)) {
|
|
14
|
+
const parts = resp.filter((b) => typeof b === "object" && b !== null).filter((b) => b.type === "text").map((b) => b.text ?? "");
|
|
15
|
+
if (parts.length) return parts.join("\n\n");
|
|
16
|
+
const joined = resp.filter((b) => Array.isArray(b) || typeof b === "object" && b !== null).map((b) => extractText(b, depth + 1)).filter(Boolean).join("\n\n");
|
|
17
|
+
if (joined) return joined;
|
|
18
|
+
}
|
|
19
|
+
if (!resp) return "";
|
|
20
|
+
try {
|
|
21
|
+
return JSON.stringify(resp);
|
|
22
|
+
} catch {
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/cache/store.ts
|
|
28
|
+
/** Stable 16-char key from a tool name + query (FNV-1a, non-crypto). */
|
|
29
|
+
function mcpCacheKey(tool, query) {
|
|
30
|
+
const s = `${tool}\n${query}`;
|
|
31
|
+
let h = 2166136261;
|
|
32
|
+
for (let i = 0; i < s.length; i++) {
|
|
33
|
+
h ^= s.charCodeAt(i);
|
|
34
|
+
h = Math.imul(h, 16777619);
|
|
35
|
+
}
|
|
36
|
+
return (h >>> 0).toString(16).padStart(8, "0") + (s.length >>> 0).toString(16).padStart(8, "0");
|
|
37
|
+
}
|
|
38
|
+
/** Path of a cache entry under `dir`. */
|
|
39
|
+
function cachePath(dir, tool, query) {
|
|
40
|
+
return join(dir, `${mcpCacheKey(tool, query)}.md`);
|
|
41
|
+
}
|
|
42
|
+
/** Read a cached entry if it exists and is fresh (mtime within `ttlMs`), else null. */
|
|
43
|
+
function cacheLookup(dir, tool, query, ttlMs, now) {
|
|
44
|
+
const path = cachePath(dir, tool, query);
|
|
45
|
+
try {
|
|
46
|
+
if (!existsSync(path) || now - statSync(path).mtimeMs > ttlMs) return null;
|
|
47
|
+
return readFileSync(path, "utf8");
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Store a cache entry (creates the dir; no-op on empty content). */
|
|
53
|
+
function cacheStore(dir, tool, query, content) {
|
|
54
|
+
if (!content) return;
|
|
55
|
+
const path = cachePath(dir, tool, query);
|
|
56
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
57
|
+
writeFileSync(path, content);
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
export { extractText as a, mcpCacheKey as i, cachePath as n, cacheStore as r, cacheLookup as t };
|
|
@@ -52,4 +52,23 @@ function evaluateApex(ctx, gates = APEX_GATES) {
|
|
|
52
52
|
return gates.reduce((hit, gate) => hit ?? gate(ctx), null);
|
|
53
53
|
}
|
|
54
54
|
//#endregion
|
|
55
|
-
|
|
55
|
+
//#region src/policy/verbosity.ts
|
|
56
|
+
/** Exa MCP tools whose result count is capped (Context7 has no verbosity knob). */
|
|
57
|
+
const EXA_TOOLS = /exa__web_search|exa__get_code_context|exa_web_search|exa_get_code_context/i;
|
|
58
|
+
/** Max results an exa MCP call may request. */
|
|
59
|
+
const MAX_EXA_RESULTS = 3;
|
|
60
|
+
/**
|
|
61
|
+
* Cap an exa MCP call to {@link MAX_EXA_RESULTS} results. Returns the capped
|
|
62
|
+
* input (a mutation for the harness to apply) when a cap is needed, else null.
|
|
63
|
+
*/
|
|
64
|
+
function capVerbosity(tool, input) {
|
|
65
|
+
if (!EXA_TOOLS.test(tool)) return null;
|
|
66
|
+
const n = input.numResults;
|
|
67
|
+
if (typeof n === "number" && n <= 3) return null;
|
|
68
|
+
return {
|
|
69
|
+
...input,
|
|
70
|
+
numResults: 3
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { docConsultedGate as a, solidReadGate as c, brainstormGate as i, capVerbosity as n, evaluateApex as o, APEX_GATES as r, freshnessGate as s, MAX_EXA_RESULTS as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fusengine/harness",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
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",
|
|
@@ -9,27 +9,111 @@
|
|
|
9
9
|
"harness": "./dist/cli/bin.mjs"
|
|
10
10
|
},
|
|
11
11
|
"exports": {
|
|
12
|
-
".": {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"./
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"./
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"./
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"./
|
|
12
|
+
".": {
|
|
13
|
+
"bun": "./src/index.ts",
|
|
14
|
+
"types": "./dist/index.d.mts",
|
|
15
|
+
"import": "./dist/index.mjs"
|
|
16
|
+
},
|
|
17
|
+
"./config": {
|
|
18
|
+
"bun": "./src/config/index.ts",
|
|
19
|
+
"types": "./dist/config/index.d.mts",
|
|
20
|
+
"import": "./dist/config/index.mjs"
|
|
21
|
+
},
|
|
22
|
+
"./util": {
|
|
23
|
+
"bun": "./src/util/index.ts",
|
|
24
|
+
"types": "./dist/util/index.d.mts",
|
|
25
|
+
"import": "./dist/util/index.mjs"
|
|
26
|
+
},
|
|
27
|
+
"./detect": {
|
|
28
|
+
"bun": "./src/detect/index.ts",
|
|
29
|
+
"types": "./dist/detect/index.d.mts",
|
|
30
|
+
"import": "./dist/detect/index.mjs"
|
|
31
|
+
},
|
|
32
|
+
"./policy": {
|
|
33
|
+
"bun": "./src/policy/index.ts",
|
|
34
|
+
"types": "./dist/policy/index.d.mts",
|
|
35
|
+
"import": "./dist/policy/index.mjs"
|
|
36
|
+
},
|
|
37
|
+
"./prompt": {
|
|
38
|
+
"bun": "./src/prompt/index.ts",
|
|
39
|
+
"types": "./dist/prompt/index.d.mts",
|
|
40
|
+
"import": "./dist/prompt/index.mjs"
|
|
41
|
+
},
|
|
42
|
+
"./memory": {
|
|
43
|
+
"bun": "./src/memory/index.ts",
|
|
44
|
+
"types": "./dist/memory/index.d.mts",
|
|
45
|
+
"import": "./dist/memory/index.mjs"
|
|
46
|
+
},
|
|
47
|
+
"./cache": {
|
|
48
|
+
"bun": "./src/cache/index.ts",
|
|
49
|
+
"types": "./dist/cache/index.d.mts",
|
|
50
|
+
"import": "./dist/cache/index.mjs"
|
|
51
|
+
},
|
|
52
|
+
"./freshness": {
|
|
53
|
+
"bun": "./src/freshness/index.ts",
|
|
54
|
+
"types": "./dist/freshness/index.d.mts",
|
|
55
|
+
"import": "./dist/freshness/index.mjs"
|
|
56
|
+
},
|
|
57
|
+
"./refs": {
|
|
58
|
+
"bun": "./src/refs/index.ts",
|
|
59
|
+
"types": "./dist/refs/index.d.mts",
|
|
60
|
+
"import": "./dist/refs/index.mjs"
|
|
61
|
+
},
|
|
62
|
+
"./state": {
|
|
63
|
+
"bun": "./src/state/index.ts",
|
|
64
|
+
"types": "./dist/state/index.d.mts",
|
|
65
|
+
"import": "./dist/state/index.mjs"
|
|
66
|
+
},
|
|
67
|
+
"./statusline": {
|
|
68
|
+
"bun": "./src/statusline/index.ts",
|
|
69
|
+
"types": "./dist/statusline/index.d.mts",
|
|
70
|
+
"import": "./dist/statusline/index.mjs"
|
|
71
|
+
},
|
|
72
|
+
"./cli": {
|
|
73
|
+
"bun": "./src/cli/index.ts",
|
|
74
|
+
"types": "./dist/cli/index.d.mts",
|
|
75
|
+
"import": "./dist/cli/index.mjs"
|
|
76
|
+
},
|
|
77
|
+
"./init": {
|
|
78
|
+
"bun": "./src/init/index.ts",
|
|
79
|
+
"types": "./dist/init/index.d.mts",
|
|
80
|
+
"import": "./dist/init/index.mjs"
|
|
81
|
+
},
|
|
82
|
+
"./tracking": {
|
|
83
|
+
"bun": "./src/tracking/index.ts",
|
|
84
|
+
"types": "./dist/tracking/index.d.mts",
|
|
85
|
+
"import": "./dist/tracking/index.mjs"
|
|
86
|
+
},
|
|
87
|
+
"./runtime": {
|
|
88
|
+
"bun": "./src/runtime/index.ts",
|
|
89
|
+
"types": "./dist/runtime/index.d.mts",
|
|
90
|
+
"import": "./dist/runtime/index.mjs"
|
|
91
|
+
},
|
|
92
|
+
"./adapters/claude": {
|
|
93
|
+
"bun": "./src/adapters/claude/index.ts",
|
|
94
|
+
"types": "./dist/adapters/claude/index.d.mts",
|
|
95
|
+
"import": "./dist/adapters/claude/index.mjs"
|
|
96
|
+
},
|
|
97
|
+
"./adapters/codex": {
|
|
98
|
+
"bun": "./src/adapters/codex/index.ts",
|
|
99
|
+
"types": "./dist/adapters/codex/index.d.mts",
|
|
100
|
+
"import": "./dist/adapters/codex/index.mjs"
|
|
101
|
+
},
|
|
102
|
+
"./adapters/cursor": {
|
|
103
|
+
"bun": "./src/adapters/cursor/index.ts",
|
|
104
|
+
"types": "./dist/adapters/cursor/index.d.mts",
|
|
105
|
+
"import": "./dist/adapters/cursor/index.mjs"
|
|
106
|
+
},
|
|
107
|
+
"./adapters/cline": {
|
|
108
|
+
"bun": "./src/adapters/cline/index.ts",
|
|
109
|
+
"types": "./dist/adapters/cline/index.d.mts",
|
|
110
|
+
"import": "./dist/adapters/cline/index.mjs"
|
|
111
|
+
},
|
|
112
|
+
"./adapters/gemini": {
|
|
113
|
+
"bun": "./src/adapters/gemini/index.ts",
|
|
114
|
+
"types": "./dist/adapters/gemini/index.d.mts",
|
|
115
|
+
"import": "./dist/adapters/gemini/index.mjs"
|
|
116
|
+
}
|
|
33
117
|
},
|
|
34
118
|
"files": ["dist", "README.md", "LICENSE"],
|
|
35
119
|
"license": "MIT",
|
|
@@ -37,6 +121,7 @@
|
|
|
37
121
|
"scripts": {
|
|
38
122
|
"test": "bun test",
|
|
39
123
|
"typecheck": "tsc --noEmit",
|
|
124
|
+
"docs:api": "typedoc",
|
|
40
125
|
"build": "tsdown src/index.ts src/config/index.ts src/util/index.ts src/detect/index.ts src/policy/index.ts src/prompt/index.ts src/memory/index.ts src/cache/index.ts src/freshness/index.ts src/refs/index.ts src/state/index.ts src/statusline/index.ts src/cli/index.ts src/cli/bin.ts src/init/index.ts src/tracking/index.ts src/runtime/index.ts src/adapters/claude/index.ts src/adapters/codex/index.ts src/adapters/cursor/index.ts src/adapters/cline/index.ts src/adapters/gemini/index.ts --dts --format esm --clean --out-dir dist",
|
|
41
126
|
"prepublishOnly": "bun test && tsc --noEmit && bun run build"
|
|
42
127
|
},
|
|
@@ -47,11 +132,14 @@
|
|
|
47
132
|
"@vercel/detect-agent": "*"
|
|
48
133
|
},
|
|
49
134
|
"peerDependenciesMeta": {
|
|
50
|
-
"@vercel/detect-agent": {
|
|
135
|
+
"@vercel/detect-agent": {
|
|
136
|
+
"optional": true
|
|
137
|
+
}
|
|
51
138
|
},
|
|
52
139
|
"devDependencies": {
|
|
53
140
|
"@types/bun": "latest",
|
|
54
141
|
"tsdown": "^0.22.3",
|
|
142
|
+
"typedoc": "^0.28.19",
|
|
55
143
|
"typescript": "^6.0.3"
|
|
56
144
|
}
|
|
57
145
|
}
|