@fusengine/harness 0.1.9 → 0.1.11
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/dist/adapters/claude/index.mjs +1 -1
- package/dist/adapters/cline/index.mjs +1 -1
- package/dist/adapters/codex/index.mjs +1 -1
- package/dist/adapters/cursor/index.mjs +1 -1
- package/dist/adapters/gemini/index.mjs +1 -1
- package/dist/{apex-gGrHzvM2.mjs → apex-BcJSE-VL.mjs} +10 -2
- package/dist/{claude-DbzjbxmO.mjs → claude-B3aJwu6O.mjs} +1 -1
- package/dist/cli/bin.mjs +2 -2
- package/dist/cli/index.mjs +1 -1
- package/dist/evaluate-DkTgwHNh.mjs +316 -0
- package/dist/{handle-Lz_thVKr.mjs → handle-Cr0YQYz-.mjs} +19 -8
- package/dist/index-CGFPvQ69.d.mts +201 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +4 -4
- package/dist/policy/index.d.mts +2 -2
- package/dist/policy/index.mjs +4 -4
- package/dist/{policy-C_pXmeNB.mjs → policy-Djmqxow2.mjs} +13 -1
- package/dist/{run-h_2LNEA8.mjs → run-D2na7Z2Z.mjs} +1 -1
- package/dist/runtime/index.d.mts +7 -4
- package/dist/runtime/index.mjs +2 -2
- package/dist/{store-BWvwnnf6.mjs → store-gtbP-Bv9.mjs} +36 -10
- package/dist/tracking/index.d.mts +15 -4
- package/dist/tracking/index.mjs +2 -2
- package/package.json +1 -1
- package/dist/evaluate-CsYyUucy.mjs +0 -152
- package/dist/index-BbJucvaG.d.mts +0 -118
|
@@ -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-
|
|
1
|
+
import { a as readClaudeInput, i as guard, n as denyResponse, o as toClaudeResponse, r as fileSizeGuard, t as contextResponse } from "../../claude-B3aJwu6O.mjs";
|
|
2
2
|
export { contextResponse, denyResponse, fileSizeGuard, guard, readClaudeInput, toClaudeResponse };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as readClaudeInput, i as guard, n as denyResponse, t as contextResponse } from "../../claude-
|
|
1
|
+
import { a as readClaudeInput, i as guard, n as denyResponse, t as contextResponse } from "../../claude-B3aJwu6O.mjs";
|
|
2
2
|
export { contextResponse, denyResponse, guard, readClaudeInput as readCodexInput };
|
|
@@ -30,8 +30,16 @@ const freshnessGate = (ctx) => ctx.agentsFresh === false ? {
|
|
|
30
30
|
reason: `Run explore-codebase and research-expert (within the freshness window) before editing ${ctx.framework}.`,
|
|
31
31
|
actions: ["Launch the explore-codebase agent", "Launch the research-expert agent"]
|
|
32
32
|
} : null;
|
|
33
|
-
/**
|
|
33
|
+
/** Gate: brainstorming must precede creating new files when flagged. */
|
|
34
|
+
const brainstormGate = (ctx) => ctx.brainstormRequired && ctx.brainstormFresh === false ? {
|
|
35
|
+
kind: "block",
|
|
36
|
+
title: "APEX: brainstorm first",
|
|
37
|
+
reason: `Creation intent detected — brainstorm before creating new ${ctx.framework} files.`,
|
|
38
|
+
actions: ["Launch the brainstorming agent"]
|
|
39
|
+
} : null;
|
|
40
|
+
/** Default APEX gate chain (brainstorm, freshness, docs, SOLID refs). */
|
|
34
41
|
const APEX_GATES = [
|
|
42
|
+
brainstormGate,
|
|
35
43
|
freshnessGate,
|
|
36
44
|
docConsultedGate,
|
|
37
45
|
solidReadGate
|
|
@@ -44,4 +52,4 @@ function evaluateApex(ctx, gates = APEX_GATES) {
|
|
|
44
52
|
return gates.reduce((hit, gate) => hit ?? gate(ctx), null);
|
|
45
53
|
}
|
|
46
54
|
//#endregion
|
|
47
|
-
export {
|
|
55
|
+
export { freshnessGate as a, evaluateApex as i, brainstormGate as n, solidReadGate as o, docConsultedGate as r, APEX_GATES as t };
|
package/dist/cli/bin.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { t as detectHarness } from "../harness-C8Nxxyn_.mjs";
|
|
3
|
-
import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-
|
|
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-Cr0YQYz-.mjs";
|
|
6
6
|
//#region src/cli/bin.ts
|
|
7
7
|
/**
|
|
8
8
|
* harness — CLI for @fusengine/harness.
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-
|
|
1
|
+
import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-D2na7Z2Z.mjs";
|
|
2
2
|
export { checkStaged, stagedContent, stagedFiles };
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { i as splitTarget, r as resolveMaxLines } from "./limits-CHn8AIL1.mjs";
|
|
2
|
+
import { t as isCodeFile } from "./project-root-C4ks_q1G.mjs";
|
|
3
|
+
//#region src/policy/detect-framework.ts
|
|
4
|
+
/**
|
|
5
|
+
* Detect the framework from a file path extension + content patterns.
|
|
6
|
+
* Aligned with the fusengine require-solid-read detection (distinct from
|
|
7
|
+
* {@link detectProjectType}, which scans config files on disk).
|
|
8
|
+
*/
|
|
9
|
+
function detectFramework(filePath, content) {
|
|
10
|
+
if (/\.(tsx?|jsx?|vue|svelte)$/.test(filePath) || /from ['"]react|useState|className=/.test(content)) {
|
|
11
|
+
if (/(page|layout|loading|error|route)\.(ts|tsx)$/.test(filePath) || /use client|use server/.test(content)) return "nextjs";
|
|
12
|
+
return "react";
|
|
13
|
+
}
|
|
14
|
+
if (/\.swift$/.test(filePath)) return "swift";
|
|
15
|
+
if (/\.php$/.test(filePath)) return "laravel";
|
|
16
|
+
if (/\.java$/.test(filePath)) return "java";
|
|
17
|
+
if (/\.go$/.test(filePath)) return "go";
|
|
18
|
+
if (/\.rb$/.test(filePath)) return "ruby";
|
|
19
|
+
if (/\.rs$/.test(filePath)) return "rust";
|
|
20
|
+
if (/\.css$/.test(filePath) || /@tailwind|@apply/.test(content)) return "tailwind";
|
|
21
|
+
return "generic";
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/policy/file-size.ts
|
|
25
|
+
/** Count lines in file content (empty string = 0). */
|
|
26
|
+
function countLines(content) {
|
|
27
|
+
return content === "" ? 0 : content.split("\n").length;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Evaluate a file's line count against the SOLID limit.
|
|
31
|
+
* @param lines - the file's line count
|
|
32
|
+
* @param max - the limit (defaults to `resolveMaxLines()`)
|
|
33
|
+
*/
|
|
34
|
+
function evaluateFileSize(lines, max = resolveMaxLines()) {
|
|
35
|
+
if (lines <= max) return {
|
|
36
|
+
ok: true,
|
|
37
|
+
lines,
|
|
38
|
+
max,
|
|
39
|
+
message: null
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
ok: false,
|
|
43
|
+
lines,
|
|
44
|
+
max,
|
|
45
|
+
message: `File has ${lines} lines (max: ${max}). Split into modules under ${splitTarget(max)} lines (Single Responsibility).`
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/policy/patterns.ts
|
|
50
|
+
/**
|
|
51
|
+
* Guard pattern data, ported verbatim from the fusengine git/install guards.
|
|
52
|
+
* Note (faithful): `git push.*--force` also matches `--force-with-lease` —
|
|
53
|
+
* preserved from the source guard.
|
|
54
|
+
*/
|
|
55
|
+
/** Destructive git operations to block outright. */
|
|
56
|
+
const GIT_BLOCKED = [
|
|
57
|
+
/git push.*--force/,
|
|
58
|
+
/git push.*-f/,
|
|
59
|
+
/git reset.*--hard/,
|
|
60
|
+
/git clean.*-fd/,
|
|
61
|
+
/git branch.*-D/,
|
|
62
|
+
/git rebase.*--force/
|
|
63
|
+
];
|
|
64
|
+
/** Git operations that warrant a confirmation prompt. */
|
|
65
|
+
const GIT_ASK = [
|
|
66
|
+
/git push/,
|
|
67
|
+
/git checkout/,
|
|
68
|
+
/git reset/,
|
|
69
|
+
/git rebase/,
|
|
70
|
+
/git merge/,
|
|
71
|
+
/git stash/,
|
|
72
|
+
/git clean/,
|
|
73
|
+
/git rm/,
|
|
74
|
+
/git mv/,
|
|
75
|
+
/git restore/,
|
|
76
|
+
/git revert/,
|
|
77
|
+
/git cherry-pick/
|
|
78
|
+
];
|
|
79
|
+
/** System-level package installs (need confirmation). */
|
|
80
|
+
const SYSTEM_INSTALL = [
|
|
81
|
+
/brew install/,
|
|
82
|
+
/brew upgrade/,
|
|
83
|
+
/brew cask/,
|
|
84
|
+
/apt install/,
|
|
85
|
+
/apt-get install/
|
|
86
|
+
];
|
|
87
|
+
/** Project-level package installs. */
|
|
88
|
+
const PROJECT_INSTALL = [
|
|
89
|
+
/npm install/,
|
|
90
|
+
/npm i /,
|
|
91
|
+
/yarn add/,
|
|
92
|
+
/pnpm add/,
|
|
93
|
+
/pip install/,
|
|
94
|
+
/pip3 install/,
|
|
95
|
+
/composer require/,
|
|
96
|
+
/bun add/,
|
|
97
|
+
/bun install/,
|
|
98
|
+
/cargo install/,
|
|
99
|
+
/go install/,
|
|
100
|
+
/gem install/,
|
|
101
|
+
/pipx install/
|
|
102
|
+
];
|
|
103
|
+
/** True when `cmd` matches any pattern in `patterns`. */
|
|
104
|
+
function matchPatterns(cmd, patterns) {
|
|
105
|
+
return patterns.some((re) => re.test(cmd));
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region src/policy/guards/security.ts
|
|
109
|
+
/** Critical patterns that must always be blocked. */
|
|
110
|
+
const CRITICAL_PATTERNS = [
|
|
111
|
+
/\brm\s+(?:-[a-z]*\s+)*-[a-z]*[rf][a-z]*\s+(?:-[a-z]+\s+)*(?:\/|~)(?:\s|$)/,
|
|
112
|
+
/:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;\s*:/,
|
|
113
|
+
/\b(?:curl|wget)\b[^|]*\|\s*(?:sudo\s+)?(?:ba|z|da|k)?sh\b/,
|
|
114
|
+
/\bchmod\s+(?:-[a-zA-Z]+\s+)*777\s+\//,
|
|
115
|
+
/\bmkfs(?:\.[a-z0-9]+)?\b/,
|
|
116
|
+
/\bdd\b[^\n]*\bif=\/dev\/zero\b[^\n]*\bof=\/dev\//
|
|
117
|
+
];
|
|
118
|
+
/** Patterns that warrant explicit confirmation before running. */
|
|
119
|
+
const ASK_PATTERNS = [
|
|
120
|
+
/\bsudo\s/,
|
|
121
|
+
/\bchmod\s+(?:-[a-zA-Z]+\s+)*777\b/,
|
|
122
|
+
/\bchown\s+-R\b/,
|
|
123
|
+
/\beval\s/,
|
|
124
|
+
/(?:>|>>|\btee\b)\s*\/etc\//
|
|
125
|
+
];
|
|
126
|
+
/** Guards against dangerous Bash commands (critical → block, sensitive → ask). */
|
|
127
|
+
function securityGuard(ctx) {
|
|
128
|
+
if (ctx.tool !== "Bash" || !ctx.command) return null;
|
|
129
|
+
const cmd = ctx.command;
|
|
130
|
+
for (const re of CRITICAL_PATTERNS) if (re.test(cmd)) return {
|
|
131
|
+
kind: "block",
|
|
132
|
+
title: "Dangerous command",
|
|
133
|
+
reason: "Command matches a destructive pattern (recursive root delete, fork bomb, remote-script piped to a shell, world-writable root, filesystem format, or disk overwrite).",
|
|
134
|
+
actions: ["Remove the destructive command", "Scope the operation to a specific safe path"]
|
|
135
|
+
};
|
|
136
|
+
for (const re of ASK_PATTERNS) if (re.test(cmd)) return {
|
|
137
|
+
kind: "ask",
|
|
138
|
+
title: "Dangerous command",
|
|
139
|
+
reason: "Command requests elevated privileges or alters sensitive targets (sudo, chmod 777, recursive chown, eval, or writing to /etc).",
|
|
140
|
+
actions: ["Confirm this command is intended", "Run with least privilege"]
|
|
141
|
+
};
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region src/policy/guards/protected-path.ts
|
|
146
|
+
/** Path fragments that mark a location as internal/generated state (off-limits to Write/Edit). */
|
|
147
|
+
const PROTECTED_FRAGMENTS = [
|
|
148
|
+
".claude/plugins/marketplaces",
|
|
149
|
+
".claude/plugins/cache",
|
|
150
|
+
".claude/logs/00-apex",
|
|
151
|
+
".claude/fusengine-cache",
|
|
152
|
+
".git/"
|
|
153
|
+
];
|
|
154
|
+
/** Blocks direct edits to internal/generated state directories. */
|
|
155
|
+
function protectedPathGuard(ctx) {
|
|
156
|
+
if ((ctx.tool === "Write" || ctx.tool === "Edit") && ctx.filePath) {
|
|
157
|
+
const path = ctx.filePath;
|
|
158
|
+
if (PROTECTED_FRAGMENTS.some((fragment) => path.includes(fragment))) return {
|
|
159
|
+
kind: "block",
|
|
160
|
+
title: "Protected path",
|
|
161
|
+
reason: "This is internal/generated state — do not edit it directly.",
|
|
162
|
+
actions: ["Edit the source, not the generated/cache copy"]
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
//#endregion
|
|
168
|
+
//#region src/policy/guards/bash-write.ts
|
|
169
|
+
/** Redirect (`>`/`>>`) targeting a code-file extension. */
|
|
170
|
+
const CODE_REDIRECT = /(?:>>?)\s*[^\s|;&]*\.(?:ts|tsx|js|jsx|py|go|rb|rs|java|kt|php|swift|vue|svelte|c|cpp|h)\b/;
|
|
171
|
+
/** Interpreters / tools that mutate source in place, plus heredoc-into-file. */
|
|
172
|
+
const CODE_MUTATORS = /\bpython3?\s+-c\b|\bsed\s+-i\b|\bperl\s+-i\b|\bawk\s+-i\s+inplace\b|\bpatch\s+|<<[-~]?\s*['"]?\w+['"]?[\s\S]*?>/;
|
|
173
|
+
/** Redirect to a non-code file, or other ambiguous file writers (ASK). */
|
|
174
|
+
const ASK_WRITERS = /(?:>>?\s*[^\s|;&]+)|\btee\s+|\bdd\s+of=|\bnode\s+-e\b.*(?:writeFile|appendFile)|\bruby\s+-e\b.*File\.write/;
|
|
175
|
+
/**
|
|
176
|
+
* Blocks shell commands that mutate code files in place (and heredocs/redirects
|
|
177
|
+
* to source files); asks before other file-writing shell commands. Forces use
|
|
178
|
+
* of the Write/Edit tool so APEX/SOLID checks are not bypassed.
|
|
179
|
+
*/
|
|
180
|
+
function bashWriteGuard(ctx) {
|
|
181
|
+
if (ctx.tool !== "Bash" || !ctx.command) return null;
|
|
182
|
+
const cmd = ctx.command;
|
|
183
|
+
if (CODE_MUTATORS.test(cmd) || CODE_REDIRECT.test(cmd)) return {
|
|
184
|
+
kind: "block",
|
|
185
|
+
title: "Bash write to code file",
|
|
186
|
+
reason: "Shell in-place edits / redirects to source files bypass APEX/SOLID checks.",
|
|
187
|
+
actions: ["Use the Write/Edit tool instead"]
|
|
188
|
+
};
|
|
189
|
+
if (ASK_WRITERS.test(cmd)) return {
|
|
190
|
+
kind: "ask",
|
|
191
|
+
title: "Bash file write",
|
|
192
|
+
reason: "This command writes a file from the shell; confirm it is intended.",
|
|
193
|
+
actions: ["Use the Write/Edit tool instead"]
|
|
194
|
+
};
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/policy/guards/interface-separation.ts
|
|
199
|
+
/** TS/JS component files: top-level `interface`/`type Foo`. */
|
|
200
|
+
const TS_DECL_RE = /^\s*(export\s+)?(interface|type)\s+[A-Z]/m;
|
|
201
|
+
/** Python view models: class subclassing a schema/protocol base. */
|
|
202
|
+
const PY_MODEL_RE = /^\s*class\s+\w+\((BaseModel|TypedDict|Protocol)\)/m;
|
|
203
|
+
/** PHP controllers: top-level `interface` / `abstract class`. */
|
|
204
|
+
const PHP_DECL_RE = /^\s*(interface|abstract class)\b/m;
|
|
205
|
+
/** Swift views: top-level `protocol Foo`. */
|
|
206
|
+
const SWIFT_PROTO_RE = /^\s*protocol\s+[A-Z]/m;
|
|
207
|
+
/**
|
|
208
|
+
* Blocks top-level interface/type/protocol declarations in component, view or
|
|
209
|
+
* controller files (Interface Segregation). Fires only when BOTH the path
|
|
210
|
+
* category AND the content pattern match.
|
|
211
|
+
*/
|
|
212
|
+
function interfaceSeparationGuard(ctx) {
|
|
213
|
+
if (ctx.tool !== "Write" && ctx.tool !== "Edit") return null;
|
|
214
|
+
const path = ctx.filePath;
|
|
215
|
+
const content = ctx.content;
|
|
216
|
+
if (!path || !content) return null;
|
|
217
|
+
const block = {
|
|
218
|
+
kind: "block",
|
|
219
|
+
title: "Separate the interface",
|
|
220
|
+
reason: "Top-level interface/type/protocol declarations belong in their own file, not in a component/view/controller.",
|
|
221
|
+
actions: ["Move the interface/type to its own file (Interface Segregation)"]
|
|
222
|
+
};
|
|
223
|
+
if (/\.(tsx|jsx|vue|svelte)$/.test(path) && TS_DECL_RE.test(content)) return block;
|
|
224
|
+
if (path.includes("views/") && /\.py$/.test(path) && PY_MODEL_RE.test(content)) return block;
|
|
225
|
+
if (path.includes("Controllers/") && /\.php$/.test(path) && PHP_DECL_RE.test(content)) return block;
|
|
226
|
+
if (path.includes("Views/") && /\.swift$/.test(path) && SWIFT_PROTO_RE.test(content)) return block;
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
//#endregion
|
|
230
|
+
//#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
|
+
/** Asks for confirmation before a dependency or system package install. */
|
|
236
|
+
function installGuard(ctx) {
|
|
237
|
+
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 {
|
|
240
|
+
kind: "ask",
|
|
241
|
+
title: "Dependency install",
|
|
242
|
+
reason: `This command installs packages: ${cmd.trim()}`,
|
|
243
|
+
actions: ["Confirm this install is intended"]
|
|
244
|
+
};
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
//#endregion
|
|
248
|
+
//#region src/policy/guards/index.ts
|
|
249
|
+
/** Ordered guard chain: critical/security + protected first, then writes/installs. */
|
|
250
|
+
const GUARDS = [
|
|
251
|
+
securityGuard,
|
|
252
|
+
protectedPathGuard,
|
|
253
|
+
bashWriteGuard,
|
|
254
|
+
interfaceSeparationGuard,
|
|
255
|
+
installGuard
|
|
256
|
+
];
|
|
257
|
+
/** Run the guard chain; the first firing guard's Prompt wins, else null. */
|
|
258
|
+
function runGuards(ctx) {
|
|
259
|
+
for (const guard of GUARDS) {
|
|
260
|
+
const hit = guard(ctx);
|
|
261
|
+
if (hit) return hit;
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/policy/evaluate.ts
|
|
267
|
+
/**
|
|
268
|
+
* Evaluate a single tool-use against the bundled policies, returning a pure
|
|
269
|
+
* decision plus a portable {@link Prompt}. Adapters translate the prompt into
|
|
270
|
+
* their harness's native response (Claude `permissionDecision`, etc.).
|
|
271
|
+
*/
|
|
272
|
+
function evaluate(ctx) {
|
|
273
|
+
const guard = runGuards(ctx);
|
|
274
|
+
if (guard) return {
|
|
275
|
+
decision: "deny",
|
|
276
|
+
message: guard.reason,
|
|
277
|
+
prompt: guard
|
|
278
|
+
};
|
|
279
|
+
if (ctx.command && matchPatterns(ctx.command, GIT_BLOCKED)) {
|
|
280
|
+
const reason = `Destructive git command: ${ctx.command}`;
|
|
281
|
+
return {
|
|
282
|
+
decision: "deny",
|
|
283
|
+
message: reason,
|
|
284
|
+
prompt: {
|
|
285
|
+
kind: "block",
|
|
286
|
+
title: "Destructive git command",
|
|
287
|
+
reason,
|
|
288
|
+
actions: ["Use a non-destructive alternative (e.g. --force-with-lease; avoid --hard / -D)"]
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
if (ctx.filePath && isCodeFile(ctx.filePath) && ctx.content !== void 0) {
|
|
293
|
+
const verdict = evaluateFileSize(countLines(ctx.content), ctx.maxLines);
|
|
294
|
+
if (!verdict.ok) return {
|
|
295
|
+
decision: "deny",
|
|
296
|
+
message: verdict.message,
|
|
297
|
+
prompt: {
|
|
298
|
+
kind: "block",
|
|
299
|
+
title: "SOLID file-size limit",
|
|
300
|
+
reason: verdict.message ?? "",
|
|
301
|
+
actions: [`Split into modules under ${verdict.max} lines (Single Responsibility)`, "Then re-run the write"]
|
|
302
|
+
},
|
|
303
|
+
meta: {
|
|
304
|
+
framework: detectFramework(ctx.filePath, ctx.content),
|
|
305
|
+
lines: verdict.lines,
|
|
306
|
+
max: verdict.max
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
decision: "allow",
|
|
312
|
+
message: null
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
//#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 };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { O as detectFramework, t as evaluate } from "./evaluate-DkTgwHNh.mjs";
|
|
2
|
+
import { i as evaluateApex } from "./apex-BcJSE-VL.mjs";
|
|
3
3
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
4
4
|
import { t as loadRefs } from "./loader-BephwI8n.mjs";
|
|
5
|
-
import { a as recordAgent,
|
|
5
|
+
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
6
|
import { join } from "node:path";
|
|
7
7
|
import { tmpdir } from "node:os";
|
|
8
8
|
//#region src/runtime/activity.ts
|
|
@@ -49,10 +49,12 @@ function activityFor(event) {
|
|
|
49
49
|
const REQUIRED_AGENTS = ["explore-codebase", "research-expert"];
|
|
50
50
|
/** Default freshness window for {@link REQUIRED_AGENTS} (4 min, the APEX TTL). */
|
|
51
51
|
const DEFAULT_WINDOW_MS = 24e4;
|
|
52
|
+
/** Trivial edits allowed within the window before the full APEX gates apply. */
|
|
53
|
+
const TRIVIAL_BUDGET = 4;
|
|
52
54
|
/**
|
|
53
|
-
* Full gate: the stateless guards (file-size, git) first, then
|
|
54
|
-
*
|
|
55
|
-
*
|
|
55
|
+
* Full gate: the stateless guards (file-size, git, security...) first, then a
|
|
56
|
+
* trivial-edit fast path, then the stateful APEX gates fed from the session
|
|
57
|
+
* track. Returns the first blocking prompt, or null to allow.
|
|
56
58
|
*/
|
|
57
59
|
async function gate(input) {
|
|
58
60
|
const quick = evaluate({
|
|
@@ -63,7 +65,13 @@ async function gate(input) {
|
|
|
63
65
|
});
|
|
64
66
|
if (quick.decision !== "allow" && quick.prompt) return quick.prompt;
|
|
65
67
|
if (!input.filePath) return null;
|
|
68
|
+
const window = input.windowMs ?? 24e4;
|
|
66
69
|
const track = await loadTrack(input.trackFile);
|
|
70
|
+
const lineCount = input.content === void 0 ? Number.POSITIVE_INFINITY : input.content.split("\n").length;
|
|
71
|
+
if (!input.isReplaceAll && lineCount < 5 && trivialCount(track, window, input.now) < 4) {
|
|
72
|
+
await saveTrack(input.trackFile, recordTrivialEdit(track, input.now, window, input.now));
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
67
75
|
return evaluateApex({
|
|
68
76
|
sessionId: input.sessionId,
|
|
69
77
|
framework: input.framework,
|
|
@@ -72,7 +80,9 @@ async function gate(input) {
|
|
|
72
80
|
authorizations: track.authorizations,
|
|
73
81
|
refs: input.refs,
|
|
74
82
|
refsRead: track.refsRead,
|
|
75
|
-
agentsFresh: agentsFresh(track, [...REQUIRED_AGENTS],
|
|
83
|
+
agentsFresh: agentsFresh(track, [...REQUIRED_AGENTS], window, input.now),
|
|
84
|
+
brainstormRequired: track.brainstormRequired,
|
|
85
|
+
brainstormFresh: agentsFresh(track, ["brainstorming"], window, input.now)
|
|
76
86
|
});
|
|
77
87
|
}
|
|
78
88
|
//#endregion
|
|
@@ -211,6 +221,7 @@ async function handleHook(id, payload, opts) {
|
|
|
211
221
|
content: event.content,
|
|
212
222
|
command: event.command,
|
|
213
223
|
refs: opts.refsDir ? await loadRefs(opts.refsDir) : void 0,
|
|
224
|
+
isReplaceAll: event.input.replace_all === true,
|
|
214
225
|
now: opts.now,
|
|
215
226
|
trackFile: file
|
|
216
227
|
});
|
|
@@ -223,4 +234,4 @@ async function handleHook(id, payload, opts) {
|
|
|
223
234
|
};
|
|
224
235
|
}
|
|
225
236
|
//#endregion
|
|
226
|
-
export { trackFile as a, REQUIRED_AGENTS as c, recordActivity as i,
|
|
237
|
+
export { trackFile as a, REQUIRED_AGENTS as c, activityFor as d, recordActivity as i, TRIVIAL_BUDGET as l, harnessTrackDir as n, normalizeEvent as o, respond as r, DEFAULT_WINDOW_MS as s, handleHook as t, gate as u };
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { t as Prompt } from "./types-D56jSgD9.mjs";
|
|
2
|
+
import { t as AuthEntry } from "./doc-helpers-CG1nuf-c.mjs";
|
|
3
|
+
import { t as RefMeta } from "./types-CY5qT2X1.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/policy/detect-project.d.ts
|
|
6
|
+
/** Project types detected from filesystem indicators. */
|
|
7
|
+
type ProjectType = "nextjs" | "nuxt" | "angular" | "svelte" | "vue" | "react" | "tailwind" | "laravel" | "rails" | "django" | "python" | "go" | "rust" | "swift" | "java" | "scala" | "elixir" | "ruby" | "generic";
|
|
8
|
+
/** Keywords that signal a development task (APEX trigger). */
|
|
9
|
+
declare const DEV_KEYWORDS: RegExp;
|
|
10
|
+
/** True when the prompt invokes the /apex command. */
|
|
11
|
+
declare function isApexCommand(prompt: string): boolean;
|
|
12
|
+
/** Detect the project type by scanning config files in `dir`. */
|
|
13
|
+
declare function detectProjectType(dir: string): ProjectType;
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/policy/detect-framework.d.ts
|
|
16
|
+
/**
|
|
17
|
+
* Detect the framework from a file path extension + content patterns.
|
|
18
|
+
* Aligned with the fusengine require-solid-read detection (distinct from
|
|
19
|
+
* {@link detectProjectType}, which scans config files on disk).
|
|
20
|
+
*/
|
|
21
|
+
declare function detectFramework(filePath: string, content: string): string;
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/policy/file-size.d.ts
|
|
24
|
+
/** Verdict from {@link evaluateFileSize}. */
|
|
25
|
+
interface FileSizeVerdict {
|
|
26
|
+
ok: boolean;
|
|
27
|
+
lines: number;
|
|
28
|
+
max: number;
|
|
29
|
+
message: string | null;
|
|
30
|
+
}
|
|
31
|
+
/** Count lines in file content (empty string = 0). */
|
|
32
|
+
declare function countLines(content: string): number;
|
|
33
|
+
/**
|
|
34
|
+
* Evaluate a file's line count against the SOLID limit.
|
|
35
|
+
* @param lines - the file's line count
|
|
36
|
+
* @param max - the limit (defaults to `resolveMaxLines()`)
|
|
37
|
+
*/
|
|
38
|
+
declare function evaluateFileSize(lines: number, max?: number): FileSizeVerdict;
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/policy/patterns.d.ts
|
|
41
|
+
/**
|
|
42
|
+
* Guard pattern data, ported verbatim from the fusengine git/install guards.
|
|
43
|
+
* Note (faithful): `git push.*--force` also matches `--force-with-lease` —
|
|
44
|
+
* preserved from the source guard.
|
|
45
|
+
*/
|
|
46
|
+
/** Destructive git operations to block outright. */
|
|
47
|
+
declare const GIT_BLOCKED: ReadonlyArray<RegExp>;
|
|
48
|
+
/** Git operations that warrant a confirmation prompt. */
|
|
49
|
+
declare const GIT_ASK: ReadonlyArray<RegExp>;
|
|
50
|
+
/** System-level package installs (need confirmation). */
|
|
51
|
+
declare const SYSTEM_INSTALL: ReadonlyArray<RegExp>;
|
|
52
|
+
/** Project-level package installs. */
|
|
53
|
+
declare const PROJECT_INSTALL: ReadonlyArray<RegExp>;
|
|
54
|
+
/** True when `cmd` matches any pattern in `patterns`. */
|
|
55
|
+
declare function matchPatterns(cmd: string, patterns: ReadonlyArray<RegExp>): boolean;
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/policy/evaluate.d.ts
|
|
58
|
+
/** Harness-agnostic input to {@link evaluate}. */
|
|
59
|
+
interface PolicyContext {
|
|
60
|
+
/** Tool name (e.g. "Write", "Edit", "Bash"). */
|
|
61
|
+
tool: string;
|
|
62
|
+
filePath?: string;
|
|
63
|
+
content?: string;
|
|
64
|
+
command?: string;
|
|
65
|
+
/** Optional override for the SOLID max-lines limit. */
|
|
66
|
+
maxLines?: number;
|
|
67
|
+
}
|
|
68
|
+
/** Harness-agnostic policy decision (+ a portable prompt for adapters to render). */
|
|
69
|
+
interface PolicyResult {
|
|
70
|
+
decision: "allow" | "deny" | "warn";
|
|
71
|
+
message: string | null;
|
|
72
|
+
prompt?: Prompt;
|
|
73
|
+
meta?: Record<string, unknown>;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Evaluate a single tool-use against the bundled policies, returning a pure
|
|
77
|
+
* decision plus a portable {@link Prompt}. Adapters translate the prompt into
|
|
78
|
+
* their harness's native response (Claude `permissionDecision`, etc.).
|
|
79
|
+
*/
|
|
80
|
+
declare function evaluate(ctx: PolicyContext): PolicyResult;
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/policy/apex.d.ts
|
|
83
|
+
/**
|
|
84
|
+
* Session context for the stateful APEX gates. The harness adapter supplies this
|
|
85
|
+
* (the package owns the gate LOGIC; recording the session activity is the
|
|
86
|
+
* adapter's tracking layer).
|
|
87
|
+
*/
|
|
88
|
+
interface ApexContext {
|
|
89
|
+
sessionId: string;
|
|
90
|
+
framework: string;
|
|
91
|
+
filePath: string;
|
|
92
|
+
content: string;
|
|
93
|
+
/** Doc-consultation authorizations from session state (Context7/Exa). */
|
|
94
|
+
authorizations?: Record<string, AuthEntry>;
|
|
95
|
+
/** Available SOLID references for the framework's skill. */
|
|
96
|
+
refs?: RefMeta[];
|
|
97
|
+
/** Absolute paths of SOLID refs already read this session. */
|
|
98
|
+
refsRead?: string[];
|
|
99
|
+
/** Whether the required prior agents (explore + research) ran within the freshness window. */
|
|
100
|
+
agentsFresh?: boolean;
|
|
101
|
+
/** Whether brainstorming is required for this edit (creation intent on a new file). */
|
|
102
|
+
brainstormRequired?: boolean;
|
|
103
|
+
/** Whether the brainstorming agent ran within the window. */
|
|
104
|
+
brainstormFresh?: boolean;
|
|
105
|
+
}
|
|
106
|
+
/** A single APEX gate: returns a blocking {@link Prompt}, or null to pass. */
|
|
107
|
+
type ApexGate = (ctx: ApexContext) => Prompt | null;
|
|
108
|
+
/** Gate: Context7 + Exa must have been consulted this session. */
|
|
109
|
+
declare const docConsultedGate: ApexGate;
|
|
110
|
+
/** Gate: the routed SOLID references for this edit must have been read. */
|
|
111
|
+
declare const solidReadGate: ApexGate;
|
|
112
|
+
/** Gate: the required prior agents (explore + research) must have run within the window. */
|
|
113
|
+
declare const freshnessGate: ApexGate;
|
|
114
|
+
/** Gate: brainstorming must precede creating new files when flagged. */
|
|
115
|
+
declare const brainstormGate: ApexGate;
|
|
116
|
+
/** Default APEX gate chain (brainstorm, freshness, docs, SOLID refs). */
|
|
117
|
+
declare const APEX_GATES: ReadonlyArray<ApexGate>;
|
|
118
|
+
/**
|
|
119
|
+
* Run the APEX gates (chain-of-responsibility): the first failing gate's prompt
|
|
120
|
+
* wins; null means every gate passed (allow).
|
|
121
|
+
*/
|
|
122
|
+
declare function evaluateApex(ctx: ApexContext, gates?: ReadonlyArray<ApexGate>): Prompt | null;
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/policy/guards/context.d.ts
|
|
125
|
+
/** Context handed to every guard in the chain. */
|
|
126
|
+
interface GuardContext {
|
|
127
|
+
tool: string;
|
|
128
|
+
filePath?: string;
|
|
129
|
+
content?: string;
|
|
130
|
+
command?: string;
|
|
131
|
+
}
|
|
132
|
+
/** A single guard: returns a blocking/asking Prompt, or null to continue. */
|
|
133
|
+
type Guard = (ctx: GuardContext) => Prompt | null;
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region src/policy/guards/security.d.ts
|
|
136
|
+
/** Critical patterns that must always be blocked. */
|
|
137
|
+
declare const CRITICAL_PATTERNS: RegExp[];
|
|
138
|
+
/** Patterns that warrant explicit confirmation before running. */
|
|
139
|
+
declare const ASK_PATTERNS: RegExp[];
|
|
140
|
+
/** Guards against dangerous Bash commands (critical → block, sensitive → ask). */
|
|
141
|
+
declare function securityGuard(ctx: GuardContext): Prompt | null;
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/policy/guards/protected-path.d.ts
|
|
144
|
+
/** Path fragments that mark a location as internal/generated state (off-limits to Write/Edit). */
|
|
145
|
+
declare const PROTECTED_FRAGMENTS: readonly string[];
|
|
146
|
+
/** Blocks direct edits to internal/generated state directories. */
|
|
147
|
+
declare function protectedPathGuard(ctx: GuardContext): Prompt | null;
|
|
148
|
+
//#endregion
|
|
149
|
+
//#region src/policy/guards/bash-write.d.ts
|
|
150
|
+
/** Redirect (`>`/`>>`) targeting a code-file extension. */
|
|
151
|
+
declare const CODE_REDIRECT: RegExp;
|
|
152
|
+
/** Interpreters / tools that mutate source in place, plus heredoc-into-file. */
|
|
153
|
+
declare const CODE_MUTATORS: RegExp;
|
|
154
|
+
/** Redirect to a non-code file, or other ambiguous file writers (ASK). */
|
|
155
|
+
declare const ASK_WRITERS: RegExp;
|
|
156
|
+
/**
|
|
157
|
+
* Blocks shell commands that mutate code files in place (and heredocs/redirects
|
|
158
|
+
* to source files); asks before other file-writing shell commands. Forces use
|
|
159
|
+
* of the Write/Edit tool so APEX/SOLID checks are not bypassed.
|
|
160
|
+
*/
|
|
161
|
+
declare function bashWriteGuard(ctx: GuardContext): Prompt | null;
|
|
162
|
+
//#endregion
|
|
163
|
+
//#region src/policy/guards/interface-separation.d.ts
|
|
164
|
+
/** TS/JS component files: top-level `interface`/`type Foo`. */
|
|
165
|
+
declare const TS_DECL_RE: RegExp;
|
|
166
|
+
/** Python view models: class subclassing a schema/protocol base. */
|
|
167
|
+
declare const PY_MODEL_RE: RegExp;
|
|
168
|
+
/** PHP controllers: top-level `interface` / `abstract class`. */
|
|
169
|
+
declare const PHP_DECL_RE: RegExp;
|
|
170
|
+
/** Swift views: top-level `protocol Foo`. */
|
|
171
|
+
declare const SWIFT_PROTO_RE: RegExp;
|
|
172
|
+
/**
|
|
173
|
+
* Blocks top-level interface/type/protocol declarations in component, view or
|
|
174
|
+
* controller files (Interface Segregation). Fires only when BOTH the path
|
|
175
|
+
* category AND the content pattern match.
|
|
176
|
+
*/
|
|
177
|
+
declare function interfaceSeparationGuard(ctx: GuardContext): Prompt | null;
|
|
178
|
+
//#endregion
|
|
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
|
+
/** Asks for confirmation before a dependency or system package install. */
|
|
185
|
+
declare function installGuard(ctx: GuardContext): Prompt | null;
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/policy/guards/index.d.ts
|
|
188
|
+
/** Ordered guard chain: critical/security + protected first, then writes/installs. */
|
|
189
|
+
declare const GUARDS: ReadonlyArray<Guard>;
|
|
190
|
+
/** Run the guard chain; the first firing guard's Prompt wins, else null. */
|
|
191
|
+
declare function runGuards(ctx: GuardContext): Prompt | null;
|
|
192
|
+
//#endregion
|
|
193
|
+
//#region src/policy/creation-intent.d.ts
|
|
194
|
+
/**
|
|
195
|
+
* True when a prompt expresses creation intent (a new feature/component) and is
|
|
196
|
+
* not a fix/refactor — the signal that brainstorming should precede creation.
|
|
197
|
+
* The harness calls this on UserPromptSubmit, then `recordBrainstormRequired`.
|
|
198
|
+
*/
|
|
199
|
+
declare function detectCreationIntent(prompt: string): boolean;
|
|
200
|
+
//#endregion
|
|
201
|
+
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 _, 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, PolicyContext as j, freshnessGate as k, 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, detectCreationIntent as t, TS_DECL_RE as u, ASK_PATTERNS as v, ApexContext as w, Guard as x, CRITICAL_PATTERNS as y, FileSizeVerdict as z };
|
package/dist/index.d.mts
CHANGED
|
@@ -5,10 +5,10 @@ import { a as detectHarness, i as HarnessVia, n as HarnessInfo, o as detectMode,
|
|
|
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 { C as isApexCommand, S as
|
|
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 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, j as PolicyContext, k as freshnessGate, 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 detectCreationIntent, u as TS_DECL_RE, v as ASK_PATTERNS, w as ApexContext, x as Guard, y as CRITICAL_PATTERNS, z as FileSizeVerdict } from "./index-CGFPvQ69.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, ApexContext, ApexGate, ApexState, ApexTask, ApexTaskFile, AuthEntry, COLOR_THRESHOLDS, ColorFn, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, DocSatisfactionStatus, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, GRADIENT_BLOCKS, HarnessId, HarnessInfo, HarnessMode, HarnessVia, IndexSummary, MAX_LINES_ENV_KEY, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INSTALL, Palette, PolicyContext, PolicyResult, ProgressBarOptions, ProjectType, Prompt, PromptKind, RefMeta, ReminderState, RouteResult, SYSTEM_INSTALL, ScoredRef, TIME_INTERVALS, TTL_ENV_KEY, acquireLock, addRoot, apexStateDir, colors, compactJson, compactMarkdown, countLines, detectFramework, detectHarness, detectMode, detectProjectType, docConsultedGate, ensureMemoryGitignore, ensureStateDir, evaluate, evaluateApex, evaluateFileSize, extractText, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, isApexCommand, isCodeFile, isDocConsulted, jaccardSimilar, loadIndex, loadRefs, loadState, matchPatterns, modeFor, nowStamp, parseEnvInt, parseFrontmatter, progressiveColor, projectRoot, projectRootOrNull, queryHash, readRoots, readState, registryFile, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, saveState, scoreReferences, 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_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 };
|
package/dist/index.mjs
CHANGED
|
@@ -3,11 +3,11 @@ import { i as ttlLabel, n as TTL_ENV_KEY, r as resolveTtlSec, t as DEFAULT_TTL_S
|
|
|
3
3
|
import { t as compactJson } from "./compact-json-DK2nX-MK.mjs";
|
|
4
4
|
import { n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./project-root-C4ks_q1G.mjs";
|
|
5
5
|
import { n as detectMode, r as modeFor, t as detectHarness } from "./harness-C8Nxxyn_.mjs";
|
|
6
|
-
import { n as
|
|
7
|
-
import { a as
|
|
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";
|
|
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 freshnessGate, i as evaluateApex, n as brainstormGate, o as solidReadGate, r as docConsultedGate, t as APEX_GATES } from "./apex-BcJSE-VL.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
13
|
import { a as jaccardSimilar, i as compactMarkdown, n as loadIndex, o as queryHash, r as summarizeIndex, t as extractText } from "./cache-DbPSJ9bC.mjs";
|
|
@@ -15,4 +15,4 @@ import { t as incrementTrivialEditCounter } from "./freshness-BK9Xg6oG.mjs";
|
|
|
15
15
|
import { n as toRefMeta, t as loadRefs } from "./loader-BephwI8n.mjs";
|
|
16
16
|
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
17
|
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, COLOR_THRESHOLDS, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, GIT_ASK, GIT_BLOCKED, GRADIENT_BLOCKS, MAX_LINES_ENV_KEY, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INSTALL, SYSTEM_INSTALL, TIME_INTERVALS, TTL_ENV_KEY, acquireLock, addRoot, apexStateDir, colors, compactJson, compactMarkdown, countLines, detectFramework, detectHarness, detectMode, detectProjectType, docConsultedGate, ensureMemoryGitignore, ensureStateDir, evaluate, evaluateApex, evaluateFileSize, extractText, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, isApexCommand, isCodeFile, isDocConsulted, jaccardSimilar, loadIndex, loadRefs, loadState, matchPatterns, modeFor, nowStamp, parseEnvInt, parseFrontmatter, progressiveColor, projectRoot, projectRootOrNull, queryHash, readRoots, readState, registryFile, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, saveState, scoreReferences, setStateField, solidReadGate, splitTarget, stateFileFor, stateFilePath, summarizeIndex, taskComplete, taskCreate, taskStart, throttleMs, toRefMeta, ttlLabel };
|
|
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 };
|
package/dist/policy/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as isApexCommand, S as
|
|
2
|
-
export { APEX_GATES, ApexContext, ApexGate, DEV_KEYWORDS, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, PROJECT_INSTALL, PolicyContext, PolicyResult, ProjectType, SYSTEM_INSTALL, countLines, detectFramework, detectProjectType, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, freshnessGate, isApexCommand, matchPatterns, 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 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, j as PolicyContext, k as freshnessGate, 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 detectCreationIntent, u as TS_DECL_RE, v as ASK_PATTERNS, w as ApexContext, x as Guard, y as CRITICAL_PATTERNS, z as FileSizeVerdict } from "../index-CGFPvQ69.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, 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 };
|
package/dist/policy/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import { a as
|
|
3
|
-
import { a as
|
|
4
|
-
export { APEX_GATES, DEV_KEYWORDS, GIT_ASK, GIT_BLOCKED, PROJECT_INSTALL, SYSTEM_INSTALL, countLines, detectFramework, detectProjectType, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, freshnessGate, isApexCommand, matchPatterns, solidReadGate };
|
|
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";
|
|
3
|
+
import { a as freshnessGate, i as evaluateApex, n as brainstormGate, o as solidReadGate, r as docConsultedGate, t as APEX_GATES } from "../apex-BcJSE-VL.mjs";
|
|
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 };
|
|
@@ -30,4 +30,16 @@ function detectProjectType(dir) {
|
|
|
30
30
|
return "generic";
|
|
31
31
|
}
|
|
32
32
|
//#endregion
|
|
33
|
-
|
|
33
|
+
//#region src/policy/creation-intent.ts
|
|
34
|
+
const CREATE_RE = /\b(?:create|implement|add|build|new|feature|component|generate|make|develop|scaffold)\b/i;
|
|
35
|
+
const SKIP_RE = /\b(?:fix|bug|debug|update|refactor|rename|move|delete|remove|commit|push|edit|modify|change)\b/i;
|
|
36
|
+
/**
|
|
37
|
+
* True when a prompt expresses creation intent (a new feature/component) and is
|
|
38
|
+
* not a fix/refactor — the signal that brainstorming should precede creation.
|
|
39
|
+
* The harness calls this on UserPromptSubmit, then `recordBrainstormRequired`.
|
|
40
|
+
*/
|
|
41
|
+
function detectCreationIntent(prompt) {
|
|
42
|
+
return CREATE_RE.test(prompt) && !SKIP_RE.test(prompt);
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { isApexCommand as i, DEV_KEYWORDS as n, detectProjectType as r, detectCreationIntent as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as isCodeFile } from "./project-root-C4ks_q1G.mjs";
|
|
2
|
-
import { t as evaluate } from "./evaluate-
|
|
2
|
+
import { t as evaluate } from "./evaluate-DkTgwHNh.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
|
package/dist/runtime/index.d.mts
CHANGED
|
@@ -56,6 +56,8 @@ declare function activityFor(event: ToolEvent): Activity | null;
|
|
|
56
56
|
declare const REQUIRED_AGENTS: ReadonlyArray<string>;
|
|
57
57
|
/** Default freshness window for {@link REQUIRED_AGENTS} (4 min, the APEX TTL). */
|
|
58
58
|
declare const DEFAULT_WINDOW_MS = 24e4;
|
|
59
|
+
/** Trivial edits allowed within the window before the full APEX gates apply. */
|
|
60
|
+
declare const TRIVIAL_BUDGET = 4;
|
|
59
61
|
/** A tool-use to gate, plus the session pointers needed for the stateful gates. */
|
|
60
62
|
interface GateInput {
|
|
61
63
|
sessionId: string;
|
|
@@ -68,11 +70,12 @@ interface GateInput {
|
|
|
68
70
|
now: number;
|
|
69
71
|
trackFile: string;
|
|
70
72
|
windowMs?: number;
|
|
73
|
+
isReplaceAll?: boolean;
|
|
71
74
|
}
|
|
72
75
|
/**
|
|
73
|
-
* Full gate: the stateless guards (file-size, git) first, then
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
+
* Full gate: the stateless guards (file-size, git, security...) first, then a
|
|
77
|
+
* trivial-edit fast path, then the stateful APEX gates fed from the session
|
|
78
|
+
* track. Returns the first blocking prompt, or null to allow.
|
|
76
79
|
*/
|
|
77
80
|
declare function gate(input: GateInput): Promise<Prompt | null>;
|
|
78
81
|
//#endregion
|
|
@@ -123,4 +126,4 @@ interface HandleOutcome {
|
|
|
123
126
|
*/
|
|
124
127
|
declare function handleHook(id: string, payload: Record<string, unknown>, opts: HandleOptions): Promise<HandleOutcome>;
|
|
125
128
|
//#endregion
|
|
126
|
-
export { Activity, DEFAULT_WINDOW_MS, GateInput, HandleOptions, HandleOutcome, NormalizedEvent, REQUIRED_AGENTS, ToolEvent, activityFor, gate, handleHook, harnessTrackDir, normalizeEvent, recordActivity, respond, trackFile };
|
|
129
|
+
export { Activity, DEFAULT_WINDOW_MS, GateInput, HandleOptions, HandleOutcome, NormalizedEvent, REQUIRED_AGENTS, TRIVIAL_BUDGET, ToolEvent, activityFor, gate, handleHook, harnessTrackDir, normalizeEvent, recordActivity, respond, trackFile };
|
package/dist/runtime/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as trackFile, c as REQUIRED_AGENTS, i as recordActivity, l as
|
|
2
|
-
export { DEFAULT_WINDOW_MS, REQUIRED_AGENTS, activityFor, gate, handleHook, harnessTrackDir, normalizeEvent, recordActivity, respond, trackFile };
|
|
1
|
+
import { a as trackFile, c as REQUIRED_AGENTS, d as activityFor, i as recordActivity, l as TRIVIAL_BUDGET, n as harnessTrackDir, o as normalizeEvent, r as respond, s as DEFAULT_WINDOW_MS, t as handleHook, u as gate } from "../handle-Cr0YQYz-.mjs";
|
|
2
|
+
export { DEFAULT_WINDOW_MS, REQUIRED_AGENTS, TRIVIAL_BUDGET, activityFor, gate, handleHook, harnessTrackDir, normalizeEvent, recordActivity, respond, trackFile };
|
|
@@ -5,7 +5,8 @@ function emptyTrack() {
|
|
|
5
5
|
return {
|
|
6
6
|
authorizations: {},
|
|
7
7
|
refsRead: [],
|
|
8
|
-
agents: []
|
|
8
|
+
agents: [],
|
|
9
|
+
trivialEdits: []
|
|
9
10
|
};
|
|
10
11
|
}
|
|
11
12
|
/** Record a doc consultation (Context7/Exa) for a framework in this session. Immutable. */
|
|
@@ -34,20 +35,45 @@ function recordRefRead(track, path) {
|
|
|
34
35
|
refsRead: [...track.refsRead, path]
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
|
-
/** Record an agent/tool call with a timestamp. Immutable. */
|
|
38
|
-
function recordAgent(track, name, ts) {
|
|
38
|
+
/** Record an agent/tool call with a timestamp + optional quality. Immutable. */
|
|
39
|
+
function recordAgent(track, name, ts, quality) {
|
|
40
|
+
const entry = quality ? {
|
|
41
|
+
name,
|
|
42
|
+
ts,
|
|
43
|
+
quality
|
|
44
|
+
} : {
|
|
45
|
+
name,
|
|
46
|
+
ts
|
|
47
|
+
};
|
|
39
48
|
return {
|
|
40
49
|
...track,
|
|
41
|
-
agents: [...track.agents,
|
|
42
|
-
name,
|
|
43
|
-
ts
|
|
44
|
-
}]
|
|
50
|
+
agents: [...track.agents, entry]
|
|
45
51
|
};
|
|
46
52
|
}
|
|
47
|
-
/** True when ALL of `names`
|
|
53
|
+
/** True when ALL of `names` ran within `windowMs` with non-insufficient quality. */
|
|
48
54
|
function agentsFresh(track, names, windowMs, now) {
|
|
49
55
|
const cutoff = now - windowMs;
|
|
50
|
-
return names.every((n) => track.agents.some((a) => a.name === n && a.ts > cutoff));
|
|
56
|
+
return names.every((n) => track.agents.some((a) => a.name === n && a.ts > cutoff && a.quality !== "insufficient"));
|
|
57
|
+
}
|
|
58
|
+
/** Record a trivial edit timestamp (sliding window; old evicted). Immutable. */
|
|
59
|
+
function recordTrivialEdit(track, ts, windowMs, now) {
|
|
60
|
+
const cutoff = now - windowMs;
|
|
61
|
+
return {
|
|
62
|
+
...track,
|
|
63
|
+
trivialEdits: [...(track.trivialEdits ?? []).filter((t) => t > cutoff), ts]
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Count trivial edits within the sliding window. */
|
|
67
|
+
function trivialCount(track, windowMs, now) {
|
|
68
|
+
const cutoff = now - windowMs;
|
|
69
|
+
return (track.trivialEdits ?? []).filter((t) => t > cutoff).length;
|
|
70
|
+
}
|
|
71
|
+
/** Set the brainstorm-required flag (from creation-intent detection). Immutable. */
|
|
72
|
+
function recordBrainstormRequired(track, required) {
|
|
73
|
+
return {
|
|
74
|
+
...track,
|
|
75
|
+
brainstormRequired: required
|
|
76
|
+
};
|
|
51
77
|
}
|
|
52
78
|
//#endregion
|
|
53
79
|
//#region src/tracking/store.ts
|
|
@@ -60,4 +86,4 @@ async function saveTrack(file, track) {
|
|
|
60
86
|
await writeJsonFile(file, track);
|
|
61
87
|
}
|
|
62
88
|
//#endregion
|
|
63
|
-
export { recordAgent as a, emptyTrack as i, saveTrack as n,
|
|
89
|
+
export { recordAgent as a, recordRefRead as c, emptyTrack as i, recordTrivialEdit as l, saveTrack as n, recordBrainstormRequired as o, agentsFresh as r, recordDoc as s, loadTrack as t, trivialCount as u };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { t as AuthEntry } from "../doc-helpers-CG1nuf-c.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/tracking/session-state.d.ts
|
|
4
|
+
/** Quality of a recorded agent call (the freshness gate ignores insufficient ones). */
|
|
5
|
+
type AgentQuality = "sufficient" | "insufficient";
|
|
4
6
|
/** Recorded session activity that feeds the APEX gates. */
|
|
5
7
|
interface SessionTrack {
|
|
6
8
|
authorizations: Record<string, AuthEntry>;
|
|
@@ -8,7 +10,10 @@ interface SessionTrack {
|
|
|
8
10
|
agents: {
|
|
9
11
|
name: string;
|
|
10
12
|
ts: number;
|
|
13
|
+
quality?: AgentQuality;
|
|
11
14
|
}[];
|
|
15
|
+
trivialEdits: number[];
|
|
16
|
+
brainstormRequired?: boolean;
|
|
12
17
|
}
|
|
13
18
|
/** A fresh, empty track. */
|
|
14
19
|
declare function emptyTrack(): SessionTrack;
|
|
@@ -16,10 +21,16 @@ declare function emptyTrack(): SessionTrack;
|
|
|
16
21
|
declare function recordDoc(track: SessionTrack, framework: string, sessionId: string, source: string): SessionTrack;
|
|
17
22
|
/** Record that a SOLID reference file was read (deduped). Immutable. */
|
|
18
23
|
declare function recordRefRead(track: SessionTrack, path: string): SessionTrack;
|
|
19
|
-
/** Record an agent/tool call with a timestamp. Immutable. */
|
|
20
|
-
declare function recordAgent(track: SessionTrack, name: string, ts: number): SessionTrack;
|
|
21
|
-
/** True when ALL of `names`
|
|
24
|
+
/** Record an agent/tool call with a timestamp + optional quality. Immutable. */
|
|
25
|
+
declare function recordAgent(track: SessionTrack, name: string, ts: number, quality?: AgentQuality): SessionTrack;
|
|
26
|
+
/** True when ALL of `names` ran within `windowMs` with non-insufficient quality. */
|
|
22
27
|
declare function agentsFresh(track: SessionTrack, names: string[], windowMs: number, now: number): boolean;
|
|
28
|
+
/** Record a trivial edit timestamp (sliding window; old evicted). Immutable. */
|
|
29
|
+
declare function recordTrivialEdit(track: SessionTrack, ts: number, windowMs: number, now: number): SessionTrack;
|
|
30
|
+
/** Count trivial edits within the sliding window. */
|
|
31
|
+
declare function trivialCount(track: SessionTrack, windowMs: number, now: number): number;
|
|
32
|
+
/** Set the brainstorm-required flag (from creation-intent detection). Immutable. */
|
|
33
|
+
declare function recordBrainstormRequired(track: SessionTrack, required: boolean): SessionTrack;
|
|
23
34
|
//#endregion
|
|
24
35
|
//#region src/tracking/store.d.ts
|
|
25
36
|
/** Load a session track from a file (an empty track if absent/corrupt). */
|
|
@@ -27,4 +38,4 @@ declare function loadTrack(file: string): Promise<SessionTrack>;
|
|
|
27
38
|
/** Persist a session track. */
|
|
28
39
|
declare function saveTrack(file: string, track: SessionTrack): Promise<void>;
|
|
29
40
|
//#endregion
|
|
30
|
-
export { SessionTrack, agentsFresh, emptyTrack, loadTrack, recordAgent, recordDoc, recordRefRead, saveTrack };
|
|
41
|
+
export { AgentQuality, SessionTrack, agentsFresh, emptyTrack, loadTrack, recordAgent, recordBrainstormRequired, recordDoc, recordRefRead, recordTrivialEdit, saveTrack, trivialCount };
|
package/dist/tracking/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as recordAgent, i as emptyTrack, n as saveTrack, o as
|
|
2
|
-
export { agentsFresh, emptyTrack, loadTrack, recordAgent, recordDoc, recordRefRead, saveTrack };
|
|
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";
|
|
2
|
+
export { agentsFresh, emptyTrack, loadTrack, recordAgent, recordBrainstormRequired, recordDoc, recordRefRead, recordTrivialEdit, saveTrack, trivialCount };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fusengine/harness",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
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,152 +0,0 @@
|
|
|
1
|
-
import { i as splitTarget, r as resolveMaxLines } from "./limits-CHn8AIL1.mjs";
|
|
2
|
-
import { t as isCodeFile } from "./project-root-C4ks_q1G.mjs";
|
|
3
|
-
//#region src/policy/detect-framework.ts
|
|
4
|
-
/**
|
|
5
|
-
* Detect the framework from a file path extension + content patterns.
|
|
6
|
-
* Aligned with the fusengine require-solid-read detection (distinct from
|
|
7
|
-
* {@link detectProjectType}, which scans config files on disk).
|
|
8
|
-
*/
|
|
9
|
-
function detectFramework(filePath, content) {
|
|
10
|
-
if (/\.(tsx?|jsx?|vue|svelte)$/.test(filePath) || /from ['"]react|useState|className=/.test(content)) {
|
|
11
|
-
if (/(page|layout|loading|error|route)\.(ts|tsx)$/.test(filePath) || /use client|use server/.test(content)) return "nextjs";
|
|
12
|
-
return "react";
|
|
13
|
-
}
|
|
14
|
-
if (/\.swift$/.test(filePath)) return "swift";
|
|
15
|
-
if (/\.php$/.test(filePath)) return "laravel";
|
|
16
|
-
if (/\.java$/.test(filePath)) return "java";
|
|
17
|
-
if (/\.go$/.test(filePath)) return "go";
|
|
18
|
-
if (/\.rb$/.test(filePath)) return "ruby";
|
|
19
|
-
if (/\.rs$/.test(filePath)) return "rust";
|
|
20
|
-
if (/\.css$/.test(filePath) || /@tailwind|@apply/.test(content)) return "tailwind";
|
|
21
|
-
return "generic";
|
|
22
|
-
}
|
|
23
|
-
//#endregion
|
|
24
|
-
//#region src/policy/file-size.ts
|
|
25
|
-
/** Count lines in file content (empty string = 0). */
|
|
26
|
-
function countLines(content) {
|
|
27
|
-
return content === "" ? 0 : content.split("\n").length;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Evaluate a file's line count against the SOLID limit.
|
|
31
|
-
* @param lines - the file's line count
|
|
32
|
-
* @param max - the limit (defaults to `resolveMaxLines()`)
|
|
33
|
-
*/
|
|
34
|
-
function evaluateFileSize(lines, max = resolveMaxLines()) {
|
|
35
|
-
if (lines <= max) return {
|
|
36
|
-
ok: true,
|
|
37
|
-
lines,
|
|
38
|
-
max,
|
|
39
|
-
message: null
|
|
40
|
-
};
|
|
41
|
-
return {
|
|
42
|
-
ok: false,
|
|
43
|
-
lines,
|
|
44
|
-
max,
|
|
45
|
-
message: `File has ${lines} lines (max: ${max}). Split into modules under ${splitTarget(max)} lines (Single Responsibility).`
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
//#endregion
|
|
49
|
-
//#region src/policy/patterns.ts
|
|
50
|
-
/**
|
|
51
|
-
* Guard pattern data, ported verbatim from the fusengine git/install guards.
|
|
52
|
-
* Note (faithful): `git push.*--force` also matches `--force-with-lease` —
|
|
53
|
-
* preserved from the source guard.
|
|
54
|
-
*/
|
|
55
|
-
/** Destructive git operations to block outright. */
|
|
56
|
-
const GIT_BLOCKED = [
|
|
57
|
-
/git push.*--force/,
|
|
58
|
-
/git push.*-f/,
|
|
59
|
-
/git reset.*--hard/,
|
|
60
|
-
/git clean.*-fd/,
|
|
61
|
-
/git branch.*-D/,
|
|
62
|
-
/git rebase.*--force/
|
|
63
|
-
];
|
|
64
|
-
/** Git operations that warrant a confirmation prompt. */
|
|
65
|
-
const GIT_ASK = [
|
|
66
|
-
/git push/,
|
|
67
|
-
/git checkout/,
|
|
68
|
-
/git reset/,
|
|
69
|
-
/git rebase/,
|
|
70
|
-
/git merge/,
|
|
71
|
-
/git stash/,
|
|
72
|
-
/git clean/,
|
|
73
|
-
/git rm/,
|
|
74
|
-
/git mv/,
|
|
75
|
-
/git restore/,
|
|
76
|
-
/git revert/,
|
|
77
|
-
/git cherry-pick/
|
|
78
|
-
];
|
|
79
|
-
/** System-level package installs (need confirmation). */
|
|
80
|
-
const SYSTEM_INSTALL = [
|
|
81
|
-
/brew install/,
|
|
82
|
-
/brew upgrade/,
|
|
83
|
-
/brew cask/,
|
|
84
|
-
/apt install/,
|
|
85
|
-
/apt-get install/
|
|
86
|
-
];
|
|
87
|
-
/** Project-level package installs. */
|
|
88
|
-
const PROJECT_INSTALL = [
|
|
89
|
-
/npm install/,
|
|
90
|
-
/npm i /,
|
|
91
|
-
/yarn add/,
|
|
92
|
-
/pnpm add/,
|
|
93
|
-
/pip install/,
|
|
94
|
-
/pip3 install/,
|
|
95
|
-
/composer require/,
|
|
96
|
-
/bun add/,
|
|
97
|
-
/bun install/,
|
|
98
|
-
/cargo install/,
|
|
99
|
-
/go install/,
|
|
100
|
-
/gem install/,
|
|
101
|
-
/pipx install/
|
|
102
|
-
];
|
|
103
|
-
/** True when `cmd` matches any pattern in `patterns`. */
|
|
104
|
-
function matchPatterns(cmd, patterns) {
|
|
105
|
-
return patterns.some((re) => re.test(cmd));
|
|
106
|
-
}
|
|
107
|
-
//#endregion
|
|
108
|
-
//#region src/policy/evaluate.ts
|
|
109
|
-
/**
|
|
110
|
-
* Evaluate a single tool-use against the bundled policies, returning a pure
|
|
111
|
-
* decision plus a portable {@link Prompt}. Adapters translate the prompt into
|
|
112
|
-
* their harness's native response (Claude `permissionDecision`, etc.).
|
|
113
|
-
*/
|
|
114
|
-
function evaluate(ctx) {
|
|
115
|
-
if (ctx.command && matchPatterns(ctx.command, GIT_BLOCKED)) {
|
|
116
|
-
const reason = `Destructive git command: ${ctx.command}`;
|
|
117
|
-
return {
|
|
118
|
-
decision: "deny",
|
|
119
|
-
message: reason,
|
|
120
|
-
prompt: {
|
|
121
|
-
kind: "block",
|
|
122
|
-
title: "Destructive git command",
|
|
123
|
-
reason,
|
|
124
|
-
actions: ["Use a non-destructive alternative (e.g. --force-with-lease; avoid --hard / -D)"]
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
if (ctx.filePath && isCodeFile(ctx.filePath) && ctx.content !== void 0) {
|
|
129
|
-
const verdict = evaluateFileSize(countLines(ctx.content), ctx.maxLines);
|
|
130
|
-
if (!verdict.ok) return {
|
|
131
|
-
decision: "deny",
|
|
132
|
-
message: verdict.message,
|
|
133
|
-
prompt: {
|
|
134
|
-
kind: "block",
|
|
135
|
-
title: "SOLID file-size limit",
|
|
136
|
-
reason: verdict.message ?? "",
|
|
137
|
-
actions: [`Split into modules under ${verdict.max} lines (Single Responsibility)`, "Then re-run the write"]
|
|
138
|
-
},
|
|
139
|
-
meta: {
|
|
140
|
-
framework: detectFramework(ctx.filePath, ctx.content),
|
|
141
|
-
lines: verdict.lines,
|
|
142
|
-
max: verdict.max
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
return {
|
|
147
|
-
decision: "allow",
|
|
148
|
-
message: null
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
//#endregion
|
|
152
|
-
export { SYSTEM_INSTALL as a, evaluateFileSize as c, PROJECT_INSTALL as i, detectFramework as l, GIT_ASK as n, matchPatterns as o, GIT_BLOCKED as r, countLines as s, evaluate as t };
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { t as Prompt } from "./types-D56jSgD9.mjs";
|
|
2
|
-
import { t as AuthEntry } from "./doc-helpers-CG1nuf-c.mjs";
|
|
3
|
-
import { t as RefMeta } from "./types-CY5qT2X1.mjs";
|
|
4
|
-
|
|
5
|
-
//#region src/policy/detect-project.d.ts
|
|
6
|
-
/** Project types detected from filesystem indicators. */
|
|
7
|
-
type ProjectType = "nextjs" | "nuxt" | "angular" | "svelte" | "vue" | "react" | "tailwind" | "laravel" | "rails" | "django" | "python" | "go" | "rust" | "swift" | "java" | "scala" | "elixir" | "ruby" | "generic";
|
|
8
|
-
/** Keywords that signal a development task (APEX trigger). */
|
|
9
|
-
declare const DEV_KEYWORDS: RegExp;
|
|
10
|
-
/** True when the prompt invokes the /apex command. */
|
|
11
|
-
declare function isApexCommand(prompt: string): boolean;
|
|
12
|
-
/** Detect the project type by scanning config files in `dir`. */
|
|
13
|
-
declare function detectProjectType(dir: string): ProjectType;
|
|
14
|
-
//#endregion
|
|
15
|
-
//#region src/policy/detect-framework.d.ts
|
|
16
|
-
/**
|
|
17
|
-
* Detect the framework from a file path extension + content patterns.
|
|
18
|
-
* Aligned with the fusengine require-solid-read detection (distinct from
|
|
19
|
-
* {@link detectProjectType}, which scans config files on disk).
|
|
20
|
-
*/
|
|
21
|
-
declare function detectFramework(filePath: string, content: string): string;
|
|
22
|
-
//#endregion
|
|
23
|
-
//#region src/policy/file-size.d.ts
|
|
24
|
-
/** Verdict from {@link evaluateFileSize}. */
|
|
25
|
-
interface FileSizeVerdict {
|
|
26
|
-
ok: boolean;
|
|
27
|
-
lines: number;
|
|
28
|
-
max: number;
|
|
29
|
-
message: string | null;
|
|
30
|
-
}
|
|
31
|
-
/** Count lines in file content (empty string = 0). */
|
|
32
|
-
declare function countLines(content: string): number;
|
|
33
|
-
/**
|
|
34
|
-
* Evaluate a file's line count against the SOLID limit.
|
|
35
|
-
* @param lines - the file's line count
|
|
36
|
-
* @param max - the limit (defaults to `resolveMaxLines()`)
|
|
37
|
-
*/
|
|
38
|
-
declare function evaluateFileSize(lines: number, max?: number): FileSizeVerdict;
|
|
39
|
-
//#endregion
|
|
40
|
-
//#region src/policy/patterns.d.ts
|
|
41
|
-
/**
|
|
42
|
-
* Guard pattern data, ported verbatim from the fusengine git/install guards.
|
|
43
|
-
* Note (faithful): `git push.*--force` also matches `--force-with-lease` —
|
|
44
|
-
* preserved from the source guard.
|
|
45
|
-
*/
|
|
46
|
-
/** Destructive git operations to block outright. */
|
|
47
|
-
declare const GIT_BLOCKED: ReadonlyArray<RegExp>;
|
|
48
|
-
/** Git operations that warrant a confirmation prompt. */
|
|
49
|
-
declare const GIT_ASK: ReadonlyArray<RegExp>;
|
|
50
|
-
/** System-level package installs (need confirmation). */
|
|
51
|
-
declare const SYSTEM_INSTALL: ReadonlyArray<RegExp>;
|
|
52
|
-
/** Project-level package installs. */
|
|
53
|
-
declare const PROJECT_INSTALL: ReadonlyArray<RegExp>;
|
|
54
|
-
/** True when `cmd` matches any pattern in `patterns`. */
|
|
55
|
-
declare function matchPatterns(cmd: string, patterns: ReadonlyArray<RegExp>): boolean;
|
|
56
|
-
//#endregion
|
|
57
|
-
//#region src/policy/evaluate.d.ts
|
|
58
|
-
/** Harness-agnostic input to {@link evaluate}. */
|
|
59
|
-
interface PolicyContext {
|
|
60
|
-
/** Tool name (e.g. "Write", "Edit", "Bash"). */
|
|
61
|
-
tool: string;
|
|
62
|
-
filePath?: string;
|
|
63
|
-
content?: string;
|
|
64
|
-
command?: string;
|
|
65
|
-
/** Optional override for the SOLID max-lines limit. */
|
|
66
|
-
maxLines?: number;
|
|
67
|
-
}
|
|
68
|
-
/** Harness-agnostic policy decision (+ a portable prompt for adapters to render). */
|
|
69
|
-
interface PolicyResult {
|
|
70
|
-
decision: "allow" | "deny" | "warn";
|
|
71
|
-
message: string | null;
|
|
72
|
-
prompt?: Prompt;
|
|
73
|
-
meta?: Record<string, unknown>;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Evaluate a single tool-use against the bundled policies, returning a pure
|
|
77
|
-
* decision plus a portable {@link Prompt}. Adapters translate the prompt into
|
|
78
|
-
* their harness's native response (Claude `permissionDecision`, etc.).
|
|
79
|
-
*/
|
|
80
|
-
declare function evaluate(ctx: PolicyContext): PolicyResult;
|
|
81
|
-
//#endregion
|
|
82
|
-
//#region src/policy/apex.d.ts
|
|
83
|
-
/**
|
|
84
|
-
* Session context for the stateful APEX gates. The harness adapter supplies this
|
|
85
|
-
* (the package owns the gate LOGIC; recording the session activity is the
|
|
86
|
-
* adapter's tracking layer).
|
|
87
|
-
*/
|
|
88
|
-
interface ApexContext {
|
|
89
|
-
sessionId: string;
|
|
90
|
-
framework: string;
|
|
91
|
-
filePath: string;
|
|
92
|
-
content: string;
|
|
93
|
-
/** Doc-consultation authorizations from session state (Context7/Exa). */
|
|
94
|
-
authorizations?: Record<string, AuthEntry>;
|
|
95
|
-
/** Available SOLID references for the framework's skill. */
|
|
96
|
-
refs?: RefMeta[];
|
|
97
|
-
/** Absolute paths of SOLID refs already read this session. */
|
|
98
|
-
refsRead?: string[];
|
|
99
|
-
/** Whether the required prior agents (explore + research) ran within the freshness window. */
|
|
100
|
-
agentsFresh?: boolean;
|
|
101
|
-
}
|
|
102
|
-
/** A single APEX gate: returns a blocking {@link Prompt}, or null to pass. */
|
|
103
|
-
type ApexGate = (ctx: ApexContext) => Prompt | null;
|
|
104
|
-
/** Gate: Context7 + Exa must have been consulted this session. */
|
|
105
|
-
declare const docConsultedGate: ApexGate;
|
|
106
|
-
/** Gate: the routed SOLID references for this edit must have been read. */
|
|
107
|
-
declare const solidReadGate: ApexGate;
|
|
108
|
-
/** Gate: the required prior agents (explore + research) must have run within the window. */
|
|
109
|
-
declare const freshnessGate: ApexGate;
|
|
110
|
-
/** Default APEX gate chain (freshness, then docs, then SOLID refs). */
|
|
111
|
-
declare const APEX_GATES: ReadonlyArray<ApexGate>;
|
|
112
|
-
/**
|
|
113
|
-
* Run the APEX gates (chain-of-responsibility): the first failing gate's prompt
|
|
114
|
-
* wins; null means every gate passed (allow).
|
|
115
|
-
*/
|
|
116
|
-
declare function evaluateApex(ctx: ApexContext, gates?: ReadonlyArray<ApexGate>): Prompt | null;
|
|
117
|
-
//#endregion
|
|
118
|
-
export { isApexCommand as C, detectProjectType as S, countLines as _, evaluateApex as a, DEV_KEYWORDS as b, PolicyContext as c, GIT_ASK as d, GIT_BLOCKED as f, FileSizeVerdict as g, matchPatterns as h, docConsultedGate as i, PolicyResult as l, SYSTEM_INSTALL as m, ApexContext as n, freshnessGate as o, PROJECT_INSTALL as p, ApexGate as r, solidReadGate as s, APEX_GATES as t, evaluate as u, evaluateFileSize as v, ProjectType as x, detectFramework as y };
|