@fusengine/harness 0.1.43 → 0.1.45
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.d.mts +2 -2
- package/dist/adapters/claude/index.mjs +2 -2
- package/dist/adapters/cline/index.mjs +1 -1
- package/dist/adapters/codex/index.d.mts +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/cache/index.d.mts +2 -2
- package/dist/cache/index.mjs +2 -2
- package/dist/{claude-CeRYMOaG.mjs → claude-DLh0fHWM.mjs} +6 -2
- package/dist/cli/bin.mjs +227 -7
- package/dist/cli/index.mjs +1 -1
- package/dist/config/index.d.mts +1 -1
- package/dist/config/index.mjs +1 -2
- package/dist/{doc-helpers-D14nkD5D.d.mts → doc-helpers-BNfYWvYv.d.mts} +9 -1
- package/dist/{doc-helpers-BhzDmJ18.mjs → doc-helpers-CWZegVdR.mjs} +14 -5
- package/dist/{dotenv-DGyLln7U.mjs → dotenv-B9nM4cuQ.mjs} +26 -1
- package/dist/evaluate-d7Pp8XJH.mjs +784 -0
- package/dist/freshness/index.d.mts +1 -1
- package/dist/freshness/index.mjs +1 -1
- package/dist/{handle-BTHcKWQ5.mjs → handle-DJaaD2BT.mjs} +849 -395
- package/dist/home-state-mKZxP4oZ.mjs +52 -0
- package/dist/{index-D7GpOmkl.d.mts → index-BA-SqNR7.d.mts} +1 -1
- package/dist/{index-BX-xcvhY.d.mts → index-BUwEmIK-.d.mts} +10 -1
- package/dist/{index-DXQfL1u8.d.mts → index-BXPySPxE.d.mts} +7 -1
- package/dist/{index-CwOdFBOr.d.mts → index-BxjzFraL.d.mts} +3 -1
- package/dist/{index-DN4cZDbU.d.mts → index-CVhw7eA0.d.mts} +111 -23
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +8 -9
- package/dist/{loader-Bn-DbZmt.mjs → loader-AGz4nK7d.mjs} +1 -1
- package/dist/{mcp-store-CnsW9oFj.mjs → mcp-store-BkBDmuxN.mjs} +22 -5
- package/dist/policy/index.d.mts +2 -2
- package/dist/policy/index.mjs +3 -3
- package/dist/refs/index.mjs +2 -2
- package/dist/{router-BfX0hJg8.mjs → router-PKVNBHge.mjs} +11 -1
- package/dist/{run-jgivVDv6.mjs → run-DLXtA5DH.mjs} +1 -1
- package/dist/runtime/index.d.mts +71 -14
- package/dist/runtime/index.mjs +3 -3
- package/dist/{session-state-Dzq6yrw7.d.mts → session-state-D4F_Dub6.d.mts} +1 -1
- package/dist/state/index.d.mts +1 -1
- package/dist/{store-CdWOQ9zD.mjs → store-CNjFenWe.mjs} +3 -50
- package/dist/tracking/index.d.mts +1 -1
- package/dist/tracking/index.mjs +1 -1
- package/dist/{validate-xi-zc-22.mjs → validate-DcQaKzau.mjs} +538 -84
- package/package.json +1 -1
- package/dist/evaluate-CeivW6G0.mjs +0 -477
- package/dist/ttl-BG55s6HZ.mjs +0 -20
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as fileSizeGuard, c as
|
|
2
|
-
export { ClaudeHookInput, blockResponse, contextResponse, denyResponse, fileSizeGuard, guard, readClaudeInput, toClaudeResponse };
|
|
1
|
+
import { a as fileSizeGuard, c as systemMessage, i as denyResponse, l as toClaudeResponse, n as blockResponse, o as guard, r as contextResponse, s as readClaudeInput, t as ClaudeHookInput } from "../../index-BxjzFraL.mjs";
|
|
2
|
+
export { ClaudeHookInput, blockResponse, contextResponse, denyResponse, fileSizeGuard, guard, readClaudeInput, systemMessage, toClaudeResponse };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as guard, i as fileSizeGuard, n as contextResponse, o as readClaudeInput, r as denyResponse, s as
|
|
2
|
-
export { blockResponse, contextResponse, denyResponse, fileSizeGuard, guard, readClaudeInput, toClaudeResponse };
|
|
1
|
+
import { a as guard, c as toClaudeResponse, i as fileSizeGuard, n as contextResponse, o as readClaudeInput, r as denyResponse, s as systemMessage, t as blockResponse } from "../../claude-DLh0fHWM.mjs";
|
|
2
|
+
export { blockResponse, contextResponse, denyResponse, fileSizeGuard, guard, readClaudeInput, systemMessage, toClaudeResponse };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as denyResponse, o as guard, r as contextResponse, s as readClaudeInput, t as ClaudeHookInput } from "../../index-
|
|
1
|
+
import { i as denyResponse, o as guard, r as contextResponse, s as readClaudeInput, t as ClaudeHookInput } from "../../index-BxjzFraL.mjs";
|
|
2
2
|
export { type ClaudeHookInput as CodexHookInput, contextResponse, denyResponse, guard, readClaudeInput as readCodexInput };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as guard, n as contextResponse, o as readClaudeInput, r as denyResponse } from "../../claude-
|
|
1
|
+
import { a as guard, n as contextResponse, o as readClaudeInput, r as denyResponse } from "../../claude-DLh0fHWM.mjs";
|
|
2
2
|
export { contextResponse, denyResponse, guard, readClaudeInput as readCodexInput };
|
package/dist/cache/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { IndexSummary, cacheLookup, cacheLookupSubstring, cachePath, cacheStore, compactMarkdown, extractText, jaccardSimilar, loadIndex, mcpCacheKey, mcpCacheWrite, queryHash, summarizeIndex, webfetchCacheWrite };
|
|
1
|
+
import { _ as queryHash, a as cacheLookupSubstringMeta, c as mcpCacheKey, d as extractText, f as IndexSummary, g as jaccardSimilar, h as compactMarkdown, i as cacheLookupSubstring, l as mcpCacheWrite, m as summarizeIndex, n as cacheLookup, o as cachePath, p as loadIndex, r as cacheLookupMeta, s as cacheStore, t as CacheHit, u as webfetchCacheWrite } from "../index-BUwEmIK-.mjs";
|
|
2
|
+
export { CacheHit, IndexSummary, cacheLookup, cacheLookupMeta, cacheLookupSubstring, cacheLookupSubstringMeta, cachePath, cacheStore, compactMarkdown, extractText, jaccardSimilar, loadIndex, mcpCacheKey, mcpCacheWrite, queryHash, summarizeIndex, webfetchCacheWrite };
|
package/dist/cache/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as cacheLookupSubstring, c as cacheStore, d as loadIndex, f as summarizeIndex, h as queryHash, i as cacheLookupMeta, l as mcpCacheKey, m as jaccardSimilar, n as webfetchCacheWrite, o as cacheLookupSubstringMeta, p as compactMarkdown, r as cacheLookup, s as cachePath, t as mcpCacheWrite, u as extractText } from "../mcp-store-BkBDmuxN.mjs";
|
|
2
2
|
import "../cache-la_KkjCS.mjs";
|
|
3
|
-
export { cacheLookup, cacheLookupSubstring, cachePath, cacheStore, compactMarkdown, extractText, jaccardSimilar, loadIndex, mcpCacheKey, mcpCacheWrite, queryHash, summarizeIndex, webfetchCacheWrite };
|
|
3
|
+
export { cacheLookup, cacheLookupMeta, cacheLookupSubstring, cacheLookupSubstringMeta, cachePath, cacheStore, compactMarkdown, extractText, jaccardSimilar, loadIndex, mcpCacheKey, mcpCacheWrite, queryHash, summarizeIndex, webfetchCacheWrite };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as evaluate } from "./evaluate-
|
|
1
|
+
import { t as evaluate } from "./evaluate-d7Pp8XJH.mjs";
|
|
2
2
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
@@ -116,6 +116,10 @@ function contextResponse(event, text) {
|
|
|
116
116
|
additionalContext: text
|
|
117
117
|
} });
|
|
118
118
|
}
|
|
119
|
+
/** A raw `systemMessage` notice — shown to the user without blocking the tool. Mirrors the shared Python `hook_output.allow_pass`/`post_pass` convention. */
|
|
120
|
+
function systemMessage(text) {
|
|
121
|
+
return JSON.stringify({ systemMessage: text });
|
|
122
|
+
}
|
|
119
123
|
/**
|
|
120
124
|
* Render a portable {@link Prompt} as a Claude Code hook response:
|
|
121
125
|
* `block` → `permissionDecision: deny`, `ask` → `permissionDecision: ask`
|
|
@@ -148,4 +152,4 @@ function guard(input) {
|
|
|
148
152
|
/** @deprecated use {@link guard}. Kept for back-compat. */
|
|
149
153
|
const fileSizeGuard = guard;
|
|
150
154
|
//#endregion
|
|
151
|
-
export { guard as a,
|
|
155
|
+
export { guard as a, toClaudeResponse as c, readText as d, sleep as f, fileSizeGuard as i, collectFiles as l, writeText as m, contextResponse as n, readClaudeInput as o, spawnCapture as p, denyResponse as r, systemMessage as s, blockResponse as t, pathExists as u };
|
package/dist/cli/bin.mjs
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { r as resolveTtlSec } from "../
|
|
3
|
-
import { r as loadDotenv } from "../dotenv-DGyLln7U.mjs";
|
|
2
|
+
import { r as loadDotenv, s as resolveTtlSec } from "../dotenv-B9nM4cuQ.mjs";
|
|
4
3
|
import { t as detectHarness } from "../harness-C8Nxxyn_.mjs";
|
|
5
|
-
import {
|
|
4
|
+
import { t as claudeHome } from "../home-state-mKZxP4oZ.mjs";
|
|
5
|
+
import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-DLXtA5DH.mjs";
|
|
6
6
|
import { n as writeInitFile, t as initFor } from "../run-Do2JltgU.mjs";
|
|
7
|
-
import {
|
|
8
|
-
import { Ot as todayUtc, t as handleHook } from "../handle-BTHcKWQ5.mjs";
|
|
7
|
+
import { Mt as todayUtc, t as handleHook } from "../handle-DJaaD2BT.mjs";
|
|
9
8
|
import { delimiter, dirname, join } from "node:path";
|
|
10
9
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
11
10
|
import { homedir } from "node:os";
|
|
@@ -78,6 +77,222 @@ async function scanChangelog(now = Date.now(), home = homedir()) {
|
|
|
78
77
|
};
|
|
79
78
|
}
|
|
80
79
|
//#endregion
|
|
80
|
+
//#region src/runtime/lifecycle/security/scan-patterns.ts
|
|
81
|
+
/** OWASP scan patterns keyed by detected language (ported 1:1 from Python). */
|
|
82
|
+
const PATTERNS = {
|
|
83
|
+
javascript: [
|
|
84
|
+
{
|
|
85
|
+
severity: "HIGH",
|
|
86
|
+
category: "XSS",
|
|
87
|
+
regex: /innerHTML\s*=/,
|
|
88
|
+
glob: "*.js"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
severity: "HIGH",
|
|
92
|
+
category: "XSS",
|
|
93
|
+
regex: /dangerouslySetInnerHTML/,
|
|
94
|
+
glob: "*.js"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
severity: "CRITICAL",
|
|
98
|
+
category: "CODE_EXEC",
|
|
99
|
+
regex: /eval\(/,
|
|
100
|
+
glob: "*.js"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
severity: "CRITICAL",
|
|
104
|
+
category: "CODE_EXEC",
|
|
105
|
+
regex: /new Function\(/,
|
|
106
|
+
glob: "*.js"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
severity: "CRITICAL",
|
|
110
|
+
category: "CMD_INJECTION",
|
|
111
|
+
regex: /child_process/,
|
|
112
|
+
glob: "*.js"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
severity: "HIGH",
|
|
116
|
+
category: "CMD_INJECTION",
|
|
117
|
+
regex: /shell:\s*true/,
|
|
118
|
+
glob: "*.js"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
severity: "MEDIUM",
|
|
122
|
+
category: "WEAK_CRYPTO",
|
|
123
|
+
regex: /Math\.random\(\)/,
|
|
124
|
+
glob: "*.js"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
severity: "CRITICAL",
|
|
128
|
+
category: "SECRETS",
|
|
129
|
+
regex: /AKIA[0-9A-Z]{16}/,
|
|
130
|
+
glob: "*.js"
|
|
131
|
+
}
|
|
132
|
+
],
|
|
133
|
+
php: [
|
|
134
|
+
{
|
|
135
|
+
severity: "CRITICAL",
|
|
136
|
+
category: "RCE",
|
|
137
|
+
regex: /shell_exec|system\(|passthru\(/,
|
|
138
|
+
glob: "*.php"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
severity: "CRITICAL",
|
|
142
|
+
category: "CODE_EXEC",
|
|
143
|
+
regex: /eval\(|assert\(/,
|
|
144
|
+
glob: "*.php"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
severity: "HIGH",
|
|
148
|
+
category: "SQL_INJECTION",
|
|
149
|
+
regex: /mysql_query\(/,
|
|
150
|
+
glob: "*.php"
|
|
151
|
+
}
|
|
152
|
+
],
|
|
153
|
+
python: [
|
|
154
|
+
{
|
|
155
|
+
severity: "CRITICAL",
|
|
156
|
+
category: "CODE_EXEC",
|
|
157
|
+
regex: /eval\(|exec\(/,
|
|
158
|
+
glob: "*.py"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
severity: "CRITICAL",
|
|
162
|
+
category: "CMD_INJECTION",
|
|
163
|
+
regex: /os\.system\(|subprocess.*shell=True/,
|
|
164
|
+
glob: "*.py"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
severity: "HIGH",
|
|
168
|
+
category: "DESERIALIZATION",
|
|
169
|
+
regex: /pickle\.loads\(/,
|
|
170
|
+
glob: "*.py"
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
severity: "HIGH",
|
|
174
|
+
category: "TLS",
|
|
175
|
+
regex: /verify=False|ssl\.CERT_NONE/,
|
|
176
|
+
glob: "*.py"
|
|
177
|
+
}
|
|
178
|
+
],
|
|
179
|
+
swift: [
|
|
180
|
+
{
|
|
181
|
+
severity: "HIGH",
|
|
182
|
+
category: "INSECURE_STORAGE",
|
|
183
|
+
regex: /UserDefaults.*password|token|secret/,
|
|
184
|
+
glob: "*.swift"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
severity: "MEDIUM",
|
|
188
|
+
category: "INSECURE_HTTP",
|
|
189
|
+
regex: /"http:\/\//,
|
|
190
|
+
glob: "*.swift"
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
severity: "HIGH",
|
|
194
|
+
category: "WEAK_KEYCHAIN",
|
|
195
|
+
regex: /kSecAttrAccessibleAlways/,
|
|
196
|
+
glob: "*.swift"
|
|
197
|
+
}
|
|
198
|
+
]
|
|
199
|
+
};
|
|
200
|
+
/** Return the scan patterns for a language ("" / go / rust / unknown → none). */
|
|
201
|
+
function getPatterns(lang) {
|
|
202
|
+
return PATTERNS[lang] ?? [];
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region src/runtime/lifecycle/security/scan.ts
|
|
206
|
+
/**
|
|
207
|
+
* @module security/scan
|
|
208
|
+
* OWASP vulnerability scanner. Ports the Python `security-scan.py`: detect the
|
|
209
|
+
* project language, walk source files (excluding vendored/build dirs), match the
|
|
210
|
+
* per-language {@link getPatterns} rules, and aggregate a JSON report. Invoked by
|
|
211
|
+
* the `harness scan` CLI command (skill `/fuse-security:scan`), NOT a hook.
|
|
212
|
+
* @packageDocumentation
|
|
213
|
+
*/
|
|
214
|
+
const EXCLUDE_RE = /(^|\/)(node_modules|vendor|\.git|dist|build)(\/|$)/;
|
|
215
|
+
const LANG_FILES = [
|
|
216
|
+
["package.json", "javascript"],
|
|
217
|
+
["composer.json", "php"],
|
|
218
|
+
["requirements.txt", "python"],
|
|
219
|
+
["pyproject.toml", "python"],
|
|
220
|
+
["Package.swift", "swift"],
|
|
221
|
+
["go.mod", "go"],
|
|
222
|
+
["Cargo.toml", "rust"]
|
|
223
|
+
];
|
|
224
|
+
/** Detect the project language from its config files (first match wins). */
|
|
225
|
+
function detectLanguage(dir) {
|
|
226
|
+
for (const [file, lang] of LANG_FILES) if (existsSync(join(dir, file))) return lang;
|
|
227
|
+
return "unknown";
|
|
228
|
+
}
|
|
229
|
+
/** Recursively collect files whose name ends with `ext`, skipping vendored dirs. */
|
|
230
|
+
function collectFiles(dir, ext, out = []) {
|
|
231
|
+
let entries;
|
|
232
|
+
try {
|
|
233
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
234
|
+
} catch {
|
|
235
|
+
return out;
|
|
236
|
+
}
|
|
237
|
+
for (const e of entries) {
|
|
238
|
+
const full = join(dir, e.name);
|
|
239
|
+
if (EXCLUDE_RE.test(full)) continue;
|
|
240
|
+
if (e.isDirectory()) collectFiles(full, ext, out);
|
|
241
|
+
else if (e.name.endsWith(ext)) out.push(full);
|
|
242
|
+
}
|
|
243
|
+
return out;
|
|
244
|
+
}
|
|
245
|
+
/** Apply one pattern to every matching file and collect line-level findings. */
|
|
246
|
+
function scanPattern(dir, p) {
|
|
247
|
+
const ext = p.glob.replace(/^\*/, "");
|
|
248
|
+
const findings = [];
|
|
249
|
+
for (const file of collectFiles(dir, ext)) {
|
|
250
|
+
let text;
|
|
251
|
+
try {
|
|
252
|
+
text = readFileSync(file, "utf8");
|
|
253
|
+
} catch {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
text.split("\n").forEach((line, i) => {
|
|
257
|
+
if (p.regex.test(line)) findings.push({
|
|
258
|
+
severity: p.severity,
|
|
259
|
+
category: p.category,
|
|
260
|
+
pattern: p.regex.source,
|
|
261
|
+
file,
|
|
262
|
+
line: i + 1
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
return findings;
|
|
267
|
+
}
|
|
268
|
+
/** Run the OWASP scan over `dir` and return the aggregated {@link ScanReport}. */
|
|
269
|
+
function runSecurityScan(dir) {
|
|
270
|
+
const language = detectLanguage(dir);
|
|
271
|
+
const counts = {
|
|
272
|
+
critical: 0,
|
|
273
|
+
high: 0,
|
|
274
|
+
medium: 0,
|
|
275
|
+
low: 0
|
|
276
|
+
};
|
|
277
|
+
const findings = [];
|
|
278
|
+
for (const p of getPatterns(language)) {
|
|
279
|
+
const hits = scanPattern(dir, p);
|
|
280
|
+
findings.push(...hits);
|
|
281
|
+
const key = p.severity.toLowerCase();
|
|
282
|
+
counts[key] = (counts[key] ?? 0) + hits.length;
|
|
283
|
+
}
|
|
284
|
+
const total = Object.values(counts).reduce((a, b) => a + b, 0);
|
|
285
|
+
return {
|
|
286
|
+
language,
|
|
287
|
+
directory: dir,
|
|
288
|
+
summary: {
|
|
289
|
+
...counts,
|
|
290
|
+
total
|
|
291
|
+
},
|
|
292
|
+
findings
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
//#endregion
|
|
81
296
|
//#region src/refs/discover.ts
|
|
82
297
|
/** Immediate subdirectory names of `p`, or `[]` when it is missing/inaccessible. */
|
|
83
298
|
function subdirs(p) {
|
|
@@ -264,7 +479,8 @@ if (cmd === "--version" || cmd === "-v") {
|
|
|
264
479
|
"aipilot",
|
|
265
480
|
"lessons",
|
|
266
481
|
"seo",
|
|
267
|
-
"memory"
|
|
482
|
+
"memory",
|
|
483
|
+
"tailwindcss"
|
|
268
484
|
])).has(scopeArg) ? scopeArg : "core";
|
|
269
485
|
const marketplaces = (process.env.FUSE_HARNESS_MARKETPLACES ?? "fusengine-plugins").split(",").map((s) => s.trim()).filter(Boolean);
|
|
270
486
|
const refsDir = process.env.FUSE_HARNESS_REFS || discoverRefs(homedir(), process.cwd(), marketplaces) || void 0;
|
|
@@ -297,7 +513,11 @@ if (cmd === "--version" || cmd === "-v") {
|
|
|
297
513
|
}) + "\n");
|
|
298
514
|
process.exit(1);
|
|
299
515
|
}
|
|
300
|
-
else {
|
|
516
|
+
else if (cmd === "scan") {
|
|
517
|
+
const dir = process.argv[3] ?? process.cwd();
|
|
518
|
+
process.stdout.write(JSON.stringify(runSecurityScan(dir), null, 2) + "\n");
|
|
519
|
+
process.exit(0);
|
|
520
|
+
} else {
|
|
301
521
|
const files = stagedFiles();
|
|
302
522
|
if (files.length === 0) process.exit(0);
|
|
303
523
|
const violations = checkStaged(files, stagedContent);
|
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-DLXtA5DH.mjs";
|
|
2
2
|
export { checkStaged, stagedContent, stagedFiles };
|
package/dist/config/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as parseEnvInt, a as ProjectLayout, c as projectLayout, d as resolveMaxLines, f as splitTarget, g as ttlLabel, h as resolveTtlSec, i as parseEnvFile, l as DEFAULT_MAX_LINES, m as TTL_ENV_KEY, n as envCandidates, o as STATE_GITIGNORE, p as DEFAULT_TTL_SEC, r as loadDotenv, s as STATE_ROOT, t as HOME_DIR, u as MAX_LINES_ENV_KEY } from "../index-
|
|
1
|
+
import { _ as parseEnvInt, a as ProjectLayout, c as projectLayout, d as resolveMaxLines, f as splitTarget, g as ttlLabel, h as resolveTtlSec, i as parseEnvFile, l as DEFAULT_MAX_LINES, m as TTL_ENV_KEY, n as envCandidates, o as STATE_GITIGNORE, p as DEFAULT_TTL_SEC, r as loadDotenv, s as STATE_ROOT, t as HOME_DIR, u as MAX_LINES_ENV_KEY } from "../index-BXPySPxE.mjs";
|
|
2
2
|
export { DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, HOME_DIR, MAX_LINES_ENV_KEY, ProjectLayout, STATE_GITIGNORE, STATE_ROOT, TTL_ENV_KEY, envCandidates, loadDotenv, parseEnvFile, parseEnvInt, projectLayout, resolveMaxLines, resolveTtlSec, splitTarget, ttlLabel };
|
package/dist/config/index.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { a as parseEnvInt, i as splitTarget, n as MAX_LINES_ENV_KEY, r as resolveMaxLines, t as DEFAULT_MAX_LINES } from "../limits-CHn8AIL1.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { a as DEFAULT_TTL_SEC, c as ttlLabel, i as parseEnvFile, n as envCandidates, o as TTL_ENV_KEY, r as loadDotenv, s as resolveTtlSec, t as HOME_DIR } from "../dotenv-B9nM4cuQ.mjs";
|
|
3
3
|
import { n as STATE_ROOT, r as projectLayout, t as STATE_GITIGNORE } from "../layout-C0jaaCQC.mjs";
|
|
4
|
-
import { i as parseEnvFile, n as envCandidates, r as loadDotenv, t as HOME_DIR } from "../dotenv-DGyLln7U.mjs";
|
|
5
4
|
import "../config-la_KkjCS.mjs";
|
|
6
5
|
export { DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, HOME_DIR, MAX_LINES_ENV_KEY, STATE_GITIGNORE, STATE_ROOT, TTL_ENV_KEY, envCandidates, loadDotenv, parseEnvFile, parseEnvInt, projectLayout, resolveMaxLines, resolveTtlSec, splitTarget, ttlLabel };
|
|
@@ -22,7 +22,15 @@ interface DocSatisfactionStatus {
|
|
|
22
22
|
web: boolean;
|
|
23
23
|
viaCache: boolean;
|
|
24
24
|
}
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* True when documentation research is satisfied for the session. Primary path
|
|
27
|
+
* is parity with the Python `mcp_research_done`
|
|
28
|
+
* (`_shared/scripts/check_skill_common.py`): `"context7:" in content and
|
|
29
|
+
* "exa:" in content`, a strict AND (the Python source has no web-alone path).
|
|
30
|
+
* `web` (WebSearch/WebFetch/fuse-browser) is a deliberate TS-side addition —
|
|
31
|
+
* it alone satisfies the gate, since fuse-browser is this harness's preferred
|
|
32
|
+
* doc-research fast-path (see CLAUDE.md) and Context7/Exa can be unavailable.
|
|
33
|
+
*/
|
|
26
34
|
declare function isDocConsulted(authorizations: Record<string, AuthEntry> | undefined, sessionId: string): boolean;
|
|
27
35
|
/** Report how each doc source was satisfied for a session. */
|
|
28
36
|
declare function formatDocSatisfactionStatus(authorizations: Record<string, AuthEntry> | undefined, sessionId: string): DocSatisfactionStatus;
|
|
@@ -23,10 +23,18 @@ function evaluateDoc(auths) {
|
|
|
23
23
|
viaCache: !liveC7 && cacheC7 || !liveExa && cacheExa
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* True when documentation research is satisfied for the session. Primary path
|
|
28
|
+
* is parity with the Python `mcp_research_done`
|
|
29
|
+
* (`_shared/scripts/check_skill_common.py`): `"context7:" in content and
|
|
30
|
+
* "exa:" in content`, a strict AND (the Python source has no web-alone path).
|
|
31
|
+
* `web` (WebSearch/WebFetch/fuse-browser) is a deliberate TS-side addition —
|
|
32
|
+
* it alone satisfies the gate, since fuse-browser is this harness's preferred
|
|
33
|
+
* doc-research fast-path (see CLAUDE.md) and Context7/Exa can be unavailable.
|
|
34
|
+
*/
|
|
27
35
|
function isDocConsulted(authorizations, sessionId) {
|
|
28
36
|
const s = evaluateDoc(sessionAuthsFor(authorizations, sessionId));
|
|
29
|
-
return s.context7
|
|
37
|
+
return s.context7 && s.exa || s.web;
|
|
30
38
|
}
|
|
31
39
|
/** Report how each doc source was satisfied for a session. */
|
|
32
40
|
function formatDocSatisfactionStatus(authorizations, sessionId) {
|
|
@@ -35,9 +43,10 @@ function formatDocSatisfactionStatus(authorizations, sessionId) {
|
|
|
35
43
|
/** Deny message when online documentation has not been consulted. */
|
|
36
44
|
function formatDocDeny(framework) {
|
|
37
45
|
return [
|
|
38
|
-
`
|
|
39
|
-
"
|
|
40
|
-
"
|
|
46
|
+
`BLOCKED: No MCP research done for ${framework}. Use BOTH:`,
|
|
47
|
+
"1) mcp__context7__query-docs AND 2) mcp__exa__web_search_exa",
|
|
48
|
+
"— OR a web fallback alone: mcp__fuse-browser__browser_fetch / browser_crawl / browser_serp_batch, WebSearch, or WebFetch.",
|
|
49
|
+
"This check is once per session — after consulting the required source(s), Write/Edit will be allowed."
|
|
41
50
|
].join("\n");
|
|
42
51
|
}
|
|
43
52
|
//#endregion
|
|
@@ -1,6 +1,31 @@
|
|
|
1
|
+
import { a as parseEnvInt } from "./limits-CHn8AIL1.mjs";
|
|
1
2
|
import { join } from "node:path";
|
|
2
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
4
|
import { homedir } from "node:os";
|
|
5
|
+
//#region src/config/ttl.ts
|
|
6
|
+
/**
|
|
7
|
+
* Default enforcement-freshness window, in seconds (2 minutes). Matches the
|
|
8
|
+
* plugin's original `FUSE_ENFORCE_TTL_SEC` default. This is the constant that
|
|
9
|
+
* actually reaches production (via `resolveTtlSec` -> `bin.ts`); `gate.ts`'s
|
|
10
|
+
* `DEFAULT_WINDOW_MS` is only a fallback for direct programmatic callers that
|
|
11
|
+
* omit `windowMs` (e.g. tests) and never applies on the real CLI path.
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_TTL_SEC = 120;
|
|
14
|
+
/** Default env var name carrying the TTL override. */
|
|
15
|
+
const TTL_ENV_KEY = "FUSE_ENFORCE_TTL_SEC";
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the enforcement TTL (seconds) from an env map.
|
|
18
|
+
* @param env - environment map (defaults to `process.env`)
|
|
19
|
+
* @param key - env var name (defaults to `FUSE_ENFORCE_TTL_SEC`)
|
|
20
|
+
*/
|
|
21
|
+
function resolveTtlSec(env = process.env, key = TTL_ENV_KEY) {
|
|
22
|
+
return parseEnvInt(env[key], 120);
|
|
23
|
+
}
|
|
24
|
+
/** Human label for a TTL: 120 -> "2min", 240 -> "4min", 90 -> "90s". */
|
|
25
|
+
function ttlLabel(sec) {
|
|
26
|
+
return sec % 60 === 0 ? `${sec / 60}min` : `${sec}s`;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
4
29
|
//#region src/config/dotenv.ts
|
|
5
30
|
/**
|
|
6
31
|
* Native .env loader. Ports the claude-plugins `services/env-file.ts`
|
|
@@ -52,4 +77,4 @@ function loadDotenv(id, env = process.env, home = homedir(), cwd = process.cwd()
|
|
|
52
77
|
for (const path of envCandidates(id, home, cwd)) for (const [k, v] of Object.entries(parseEnvFile(path))) if (env[k] === void 0) env[k] = v;
|
|
53
78
|
}
|
|
54
79
|
//#endregion
|
|
55
|
-
export { parseEnvFile as i, envCandidates as n, loadDotenv as r, HOME_DIR as t };
|
|
80
|
+
export { DEFAULT_TTL_SEC as a, ttlLabel as c, parseEnvFile as i, envCandidates as n, TTL_ENV_KEY as o, loadDotenv as r, resolveTtlSec as s, HOME_DIR as t };
|