@fusengine/harness 0.1.76 → 0.1.78
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 +1 -1
- package/dist/adapters/claude/index.mjs +1 -1
- package/dist/adapters/codex/index.d.mts +1 -1
- package/dist/adapters/codex/index.mjs +1 -1
- package/dist/adapters/hermes/index.mjs +1 -1
- package/dist/{claude-BxQwhdZB.mjs → claude-51RSspJ1.mjs} +6 -13
- package/dist/cli/bin.mjs +2 -2
- package/dist/{handle-BNWlWpKX.mjs → handle-Dthtlp7Z.mjs} +122 -99
- package/dist/{hermes-OVoZtUzT.mjs → hermes-rTmVdhUL.mjs} +1 -1
- package/dist/{index-DJw1OGwE.d.mts → index-BIGVNQB8.d.mts} +19 -11
- package/dist/{index-a6kvHEPa.d.mts → index-DGxNjXW7.d.mts} +3 -11
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/policy/index.d.mts +1 -1
- package/dist/policy/index.mjs +1 -1
- package/dist/runtime/index.d.mts +34 -17
- package/dist/runtime/index.mjs +1 -1
- package/dist/{validate-DnLU4IHy.mjs → validate-DhOX5hDK.mjs} +76 -25
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as denyResponse, c as informResponse, d as toClaudeResponse, i as contextResponse, l as readClaudeInput, n as attachSystemMessage, o as fileSizeGuard, r as blockResponse, s as guard, t as ClaudeHookInput, u as systemMessage } from "../../index-
|
|
1
|
+
import { a as denyResponse, c as informResponse, d as toClaudeResponse, i as contextResponse, l as readClaudeInput, n as attachSystemMessage, o as fileSizeGuard, r as blockResponse, s as guard, t as ClaudeHookInput, u as systemMessage } from "../../index-DGxNjXW7.mjs";
|
|
2
2
|
export { ClaudeHookInput, attachSystemMessage, blockResponse, contextResponse, denyResponse, fileSizeGuard, guard, informResponse, readClaudeInput, systemMessage, toClaudeResponse };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as fileSizeGuard, c as readClaudeInput, i as denyResponse, l as systemMessage, n as blockResponse, o as guard, r as contextResponse, s as informResponse, t as attachSystemMessage, u as toClaudeResponse } from "../../claude-
|
|
1
|
+
import { a as fileSizeGuard, c as readClaudeInput, i as denyResponse, l as systemMessage, n as blockResponse, o as guard, r as contextResponse, s as informResponse, t as attachSystemMessage, u as toClaudeResponse } from "../../claude-51RSspJ1.mjs";
|
|
2
2
|
export { attachSystemMessage, blockResponse, contextResponse, denyResponse, fileSizeGuard, guard, informResponse, readClaudeInput, systemMessage, toClaudeResponse };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as Prompt } from "../../types-DVbIl9md.mjs";
|
|
2
|
-
import { a as denyResponse, c as informResponse, i as contextResponse, l as readClaudeInput, t as ClaudeHookInput } from "../../index-
|
|
2
|
+
import { a as denyResponse, c as informResponse, i as contextResponse, l as readClaudeInput, t as ClaudeHookInput } from "../../index-DGxNjXW7.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/adapters/codex/index.d.ts
|
|
5
5
|
/**
|
|
@@ -2,7 +2,7 @@ import { t as evaluate } from "../../evaluate-ClfbCDiY.mjs";
|
|
|
2
2
|
import { f as countLines } from "../../home-state-BXf38Zi1.mjs";
|
|
3
3
|
import { t as formatPrompt } from "../../types-ernB1Dy3.mjs";
|
|
4
4
|
import { n as parseApplyPatch, t as isBypassPermissions } from "../../permission-mode-BN3MNgbm.mjs";
|
|
5
|
-
import { c as readClaudeInput, i as denyResponse, r as contextResponse, s as informResponse, v as commandToString } from "../../claude-
|
|
5
|
+
import { c as readClaudeInput, i as denyResponse, r as contextResponse, s as informResponse, v as commandToString } from "../../claude-51RSspJ1.mjs";
|
|
6
6
|
//#region src/adapters/codex/index.ts
|
|
7
7
|
/**
|
|
8
8
|
* OpenAI Codex CLI adapter (hook-mode). Codex's `PreToolUse` hook (since 2026)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as readHermesInput, r as toHermesResponse, t as guard } from "../../hermes-
|
|
1
|
+
import { n as readHermesInput, r as toHermesResponse, t as guard } from "../../hermes-rTmVdhUL.mjs";
|
|
2
2
|
export { guard, readHermesInput, toHermesResponse };
|
|
@@ -178,23 +178,19 @@ function informResponse(event, notice, context) {
|
|
|
178
178
|
}
|
|
179
179
|
});
|
|
180
180
|
}
|
|
181
|
-
/** Attach a
|
|
181
|
+
/** Attach a `systemMessage`, MERGING it with any already present (`\n`-joined) instead of overwriting — several producers may each call this on the same stdout in one hook invocation. */
|
|
182
182
|
function attachSystemMessage(stdout, notice) {
|
|
183
183
|
try {
|
|
184
|
+
const parsed = JSON.parse(stdout), prev = typeof parsed.systemMessage === "string" ? parsed.systemMessage : "";
|
|
184
185
|
return JSON.stringify({
|
|
185
|
-
...
|
|
186
|
-
systemMessage: notice
|
|
186
|
+
...parsed,
|
|
187
|
+
systemMessage: prev ? `${prev}\n${notice}` : notice
|
|
187
188
|
});
|
|
188
189
|
} catch {
|
|
189
190
|
return systemMessage(notice);
|
|
190
191
|
}
|
|
191
192
|
}
|
|
192
|
-
/**
|
|
193
|
-
* Render a portable {@link Prompt} as a Claude Code hook response:
|
|
194
|
-
* `block` → `permissionDecision: deny`, `ask` → `permissionDecision: ask`
|
|
195
|
-
* (interactive confirm), `inform` → `additionalContext` — plus the user-visible
|
|
196
|
-
* `systemMessage` channel when the prompt carries a `userMessage`.
|
|
197
|
-
*/
|
|
193
|
+
/** Render a portable {@link Prompt} as a Claude Code hook response: `block` → deny, `ask` → interactive `permissionDecision`, `inform` → `additionalContext` (+ `systemMessage` when `userMessage` is set). */
|
|
198
194
|
function toClaudeResponse(event, prompt) {
|
|
199
195
|
const reason = formatPrompt(prompt);
|
|
200
196
|
if (prompt.kind === "block") return denyResponse(event, reason);
|
|
@@ -206,10 +202,7 @@ function toClaudeResponse(event, prompt) {
|
|
|
206
202
|
if (prompt.userMessage) return informResponse(event, prompt.userMessage, prompt.reason ? reason : "");
|
|
207
203
|
return contextResponse(event, reason);
|
|
208
204
|
}
|
|
209
|
-
/**
|
|
210
|
-
* Run the bundled policy over a Claude payload and return the native response
|
|
211
|
-
* string (deny/ask/additionalContext), or null to allow.
|
|
212
|
-
*/
|
|
205
|
+
/** Run the bundled policy over a Claude payload and return the native response string (deny/ask/additionalContext), or null to allow. */
|
|
213
206
|
function guard(input) {
|
|
214
207
|
const result = evaluate({
|
|
215
208
|
tool: input.tool_name ?? "Write",
|
package/dist/cli/bin.mjs
CHANGED
|
@@ -4,8 +4,8 @@ import { t as detectHarness } from "../harness-Cb9xR8dC.mjs";
|
|
|
4
4
|
import { t as claudeHome } from "../home-state-BXf38Zi1.mjs";
|
|
5
5
|
import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-3tyTW9Ud.mjs";
|
|
6
6
|
import { n as writeInitFile, t as initFor } from "../run-Do2JltgU.mjs";
|
|
7
|
-
import { $ as runningVersion, Q as runDoctor, Z as notify, et as versionBanner, t as handleHook, zt as todayUtc } from "../handle-
|
|
8
|
-
import { p as readStdin$1 } from "../claude-
|
|
7
|
+
import { $ as runningVersion, Q as runDoctor, Z as notify, et as versionBanner, t as handleHook, zt as todayUtc } from "../handle-Dthtlp7Z.mjs";
|
|
8
|
+
import { p as readStdin$1 } from "../claude-51RSspJ1.mjs";
|
|
9
9
|
import { delimiter, join } from "node:path";
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
11
11
|
import { homedir } from "node:os";
|
|
@@ -6,7 +6,7 @@ import { B as detectProjectType$1, H as requiredArchSkill, L as detectFramework,
|
|
|
6
6
|
import { a as sanitizeSessionId, c as sessionsDir, d as countFrameworkCodeLines, f as countLines, i as loadSessionState, l as PLUGINS_DIR, n as fuseHarnessHome, o as saveSessionState, r as fusengineCache, t as claudeHome } from "./home-state-BXf38Zi1.mjs";
|
|
7
7
|
import { a as writeJsonFile, i as readJsonFile, r as hashText, t as atomicWrite } from "./json-io-DisYd2fb.mjs";
|
|
8
8
|
import { r as isDocConsulted } from "./doc-helpers-CWZegVdR.mjs";
|
|
9
|
-
import { A as
|
|
9
|
+
import { A as capVerbosity, C as usesTailwindUtilities, D as isExcludedSwiftPath, E as isExcludedJsPath, I as docConsultedGate, L as evaluateApex, N as POST_AUTH_GATES, P as PRE_AUTH_GATES, S as skillTriggerGate, T as frameworkSolidGate, _ as harnessHomeSegment, b as parseField, c as EXCLUDE_DIRS$1, d as buildApexTaskInjection, h as buildClaudeMdContext, j as detectCreationIntent, l as PROJECT_INDICATORS, n as missingSeoElements, o as parseEnrichment, r as descFromText, s as parseEntry, t as isHtmlLike, v as scanPlugin } from "./validate-DhOX5hDK.mjs";
|
|
10
10
|
import { n as findMarketplacePlugins, r as readPluginMeta, t as resolveSkillPath } from "./skill-path-BO2N0XvB.mjs";
|
|
11
11
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
12
12
|
import { a as nowStamp, l as throttleMs, n as readRoots, o as readState, s as setStateField, t as addRoot } from "./registry-CymilZiZ.mjs";
|
|
@@ -14,8 +14,8 @@ import { d as loadIndex, i as cacheLookupMeta, n as webfetchCacheWrite, o as cac
|
|
|
14
14
|
import { t as loadRefs } from "./loader-AGz4nK7d.mjs";
|
|
15
15
|
import { a as writeLastNonce, c as recordAgent, d as recordRefRead, f as recordTarget, h as apexAuthorizationGate, i as verifyTrack, l as recordBrainstormRequired, m as trivialCount, n as saveTrack, o as agentsFresh, p as recordTrivialEdit, r as signTrack, s as emptyTrack, t as loadTrack, u as recordDoc } from "./store-Bcn08y9m.mjs";
|
|
16
16
|
import { n as parseApplyPatch, t as isBypassPermissions } from "./permission-mode-BN3MNgbm.mjs";
|
|
17
|
-
import { _ as writeText, d as collectFiles, f as pathExists, g as spawnCapture, h as sleep, i as denyResponse, l as systemMessage, m as readText, n as blockResponse, r as contextResponse, s as informResponse, t as attachSystemMessage, v as commandToString } from "./claude-
|
|
18
|
-
import { r as toHermesResponse } from "./hermes-
|
|
17
|
+
import { _ as writeText, d as collectFiles, f as pathExists, g as spawnCapture, h as sleep, i as denyResponse, l as systemMessage, m as readText, n as blockResponse, r as contextResponse, s as informResponse, t as attachSystemMessage, v as commandToString } from "./claude-51RSspJ1.mjs";
|
|
18
|
+
import { r as toHermesResponse } from "./hermes-rTmVdhUL.mjs";
|
|
19
19
|
import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
20
20
|
import { appendFileSync, copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, renameSync, rmSync, rmdirSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
|
|
21
21
|
import { homedir } from "node:os";
|
|
@@ -591,13 +591,14 @@ function claudeMdKey(prompt, ctx) {
|
|
|
591
591
|
* is emitted on EVERY message" is thus preserved.
|
|
592
592
|
* @param prompt - The raw user prompt.
|
|
593
593
|
* @param cwd - Project root (for project-type detection).
|
|
594
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
594
595
|
* @returns The native hook stdout (possibly empty).
|
|
595
596
|
*/
|
|
596
|
-
function promptSubmitContext(prompt, cwd) {
|
|
597
|
-
const ctx = buildClaudeMdContext(prompt, cwd);
|
|
597
|
+
function promptSubmitContext(prompt, cwd, id = "claude-code") {
|
|
598
|
+
const ctx = buildClaudeMdContext(prompt, cwd, id);
|
|
598
599
|
if (!ctx) return "";
|
|
599
600
|
if (!oncePerWindow(claudeMdKey(prompt, ctx), 3e3)) return "";
|
|
600
|
-
return contextResponse("UserPromptSubmit", ctx);
|
|
601
|
+
return attachSystemMessage(contextResponse("UserPromptSubmit", ctx), "CLAUDE.md injected");
|
|
601
602
|
}
|
|
602
603
|
/**
|
|
603
604
|
* PreToolUse Task context injection: render the APEX sub-agent context as a
|
|
@@ -605,10 +606,11 @@ function promptSubmitContext(prompt, cwd) {
|
|
|
605
606
|
* Harness-produced (not owner CLAUDE.md content), so it is subject to the
|
|
606
607
|
* per-fragment {@link capFragment} budget — unlike {@link promptSubmitContext}.
|
|
607
608
|
* @param cwd - Fallback project root when `CLAUDE_PROJECT_DIR` is unset.
|
|
609
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
608
610
|
* @returns The native hook stdout (possibly empty).
|
|
609
611
|
*/
|
|
610
|
-
function taskContext(cwd) {
|
|
611
|
-
const ctx = buildApexTaskInjection(process.env.CLAUDE_PROJECT_DIR ?? cwd);
|
|
612
|
+
function taskContext(cwd, id = "claude-code") {
|
|
613
|
+
const ctx = buildApexTaskInjection(process.env.CLAUDE_PROJECT_DIR ?? cwd, id);
|
|
612
614
|
return ctx ? contextResponse("PreToolUse", capFragment("apex-task", ctx)) : "";
|
|
613
615
|
}
|
|
614
616
|
//#endregion
|
|
@@ -770,7 +772,7 @@ function sessionStartCore(cwd, home = homedir(), now = Date.now()) {
|
|
|
770
772
|
const dev = devContext(cwd);
|
|
771
773
|
runSessionStartCleanups(home, now);
|
|
772
774
|
const ctx = [md, dev].filter(Boolean).join("\n");
|
|
773
|
-
return ctx ? contextResponse("SessionStart", ctx) : "";
|
|
775
|
+
return ctx ? attachSystemMessage(contextResponse("SessionStart", ctx), "CLAUDE.md injected") : "";
|
|
774
776
|
}
|
|
775
777
|
//#endregion
|
|
776
778
|
//#region src/runtime/lifecycle/inject-rules.ts
|
|
@@ -801,7 +803,7 @@ function readRules(rulesDir) {
|
|
|
801
803
|
*/
|
|
802
804
|
function injectRules(pluginRoot, event) {
|
|
803
805
|
const content = readRules(join(pluginRoot, "rules"));
|
|
804
|
-
return content ? contextResponse(event, content) : "";
|
|
806
|
+
return content ? attachSystemMessage(contextResponse(event, content), "rules 00-08 injected") : "";
|
|
805
807
|
}
|
|
806
808
|
//#endregion
|
|
807
809
|
//#region src/runtime/lifecycle/solid-detect.ts
|
|
@@ -1393,18 +1395,20 @@ function trackAgentMemory(data, home = homedir(), now = Date.now()) {
|
|
|
1393
1395
|
//#region src/runtime/lifecycle/teammate-idle.ts
|
|
1394
1396
|
/**
|
|
1395
1397
|
* Handle TeammateIdle: when the teammate's session-changes file shows code was
|
|
1396
|
-
* modified, suggest sniper validation as
|
|
1397
|
-
* `
|
|
1398
|
+
* modified, suggest sniper validation as plain text — TeammateIdle has no
|
|
1399
|
+
* `additionalContext` channel (rejects `hookSpecificOutput`), so the caller
|
|
1400
|
+
* ({@link module:teammate-idle-check.teammateIdleContext}) rides this text on
|
|
1401
|
+
* the `systemMessage` channel instead. Ports `teammate-idle/validate-teammate-output.py`.
|
|
1398
1402
|
* @param data - The raw hook payload.
|
|
1399
1403
|
* @param home - Home dir (defaults to `~`).
|
|
1400
|
-
* @returns The
|
|
1404
|
+
* @returns The plain notice text, or "" when no code file was modified.
|
|
1401
1405
|
*/
|
|
1402
1406
|
function validateTeammateOutput(data, home = homedir()) {
|
|
1403
1407
|
const teammate = String(data.teammate_name ?? "unknown");
|
|
1404
1408
|
const changes = loadSessionState(sanitizeSessionId(data.session_id) ?? "unknown", home).changes;
|
|
1405
1409
|
const count = changes?.cumulativeCodeFiles ?? 0;
|
|
1406
|
-
if (count
|
|
1407
|
-
return ""
|
|
1410
|
+
if (count === 0) return "";
|
|
1411
|
+
return `Teammate '${teammate}' going idle after modifying ${count} code file(s): ${(changes?.modifiedFiles ?? []).slice(0, 5).join(", ")}. Consider running sniper validation.`;
|
|
1408
1412
|
}
|
|
1409
1413
|
//#endregion
|
|
1410
1414
|
//#region src/runtime/lifecycle/tool-failure.ts
|
|
@@ -1440,14 +1444,17 @@ function stamp$1(now) {
|
|
|
1440
1444
|
return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
|
|
1441
1445
|
}
|
|
1442
1446
|
/**
|
|
1443
|
-
* Handle PreCompact: back up
|
|
1444
|
-
* the 5 newest, and emit a
|
|
1447
|
+
* Handle PreCompact: back up the target apex `task.json` (`.claude/apex/`,
|
|
1448
|
+
* `.codex/apex/`, ...) to `backups/`, keep only the 5 newest, and emit a
|
|
1449
|
+
* confirmation. Ports `pre-compact/save-apex-state.py`.
|
|
1445
1450
|
* @param cwd - Project root.
|
|
1446
1451
|
* @param now - Clock (defaults to `Date.now()`).
|
|
1452
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
1447
1453
|
* @returns The native hook stdout (possibly empty when no task.json).
|
|
1448
1454
|
*/
|
|
1449
|
-
function saveApexState(cwd, now = Date.now()) {
|
|
1450
|
-
const
|
|
1455
|
+
function saveApexState(cwd, now = Date.now(), id = "claude-code") {
|
|
1456
|
+
const seg = harnessHomeSegment(id);
|
|
1457
|
+
const apexDir = join(cwd, seg, "apex");
|
|
1451
1458
|
const stateFile = join(apexDir, "task.json");
|
|
1452
1459
|
if (!existsSync(stateFile)) return "";
|
|
1453
1460
|
const backupDir = join(apexDir, "backups");
|
|
@@ -1457,7 +1464,7 @@ function saveApexState(cwd, now = Date.now()) {
|
|
|
1457
1464
|
for (const old of backups.slice(5)) try {
|
|
1458
1465
|
rmSync(join(backupDir, old), { force: true });
|
|
1459
1466
|
} catch {}
|
|
1460
|
-
return JSON.stringify({ additionalContext:
|
|
1467
|
+
return JSON.stringify({ additionalContext: `APEX state saved before compaction. Previous task state preserved in ${seg}/apex/backups/` });
|
|
1461
1468
|
}
|
|
1462
1469
|
//#endregion
|
|
1463
1470
|
//#region src/runtime/lifecycle/session-end.ts
|
|
@@ -2210,19 +2217,12 @@ const IDLE_DEDUP_MS = 3e4;
|
|
|
2210
2217
|
function missingDeliverables(sessionId, home) {
|
|
2211
2218
|
return (loadSessionState(sessionId, home).changes?.modifiedFiles ?? []).filter((f) => typeof f === "string" && f !== "" && !existsSync(f));
|
|
2212
2219
|
}
|
|
2213
|
-
/** Pull the `additionalContext` body out of a `contextResponse` stdout ("" when empty/unparseable). */
|
|
2214
|
-
function bodyOf(stdout) {
|
|
2215
|
-
if (!stdout) return "";
|
|
2216
|
-
try {
|
|
2217
|
-
return JSON.parse(stdout).hookSpecificOutput?.additionalContext ?? "";
|
|
2218
|
-
} catch {
|
|
2219
|
-
return "";
|
|
2220
|
-
}
|
|
2221
|
-
}
|
|
2222
2220
|
/**
|
|
2223
2221
|
* Handle TeammateIdle: merge the existing sniper suggestion with a missing-
|
|
2224
|
-
* deliverable warning (deduped) into one `
|
|
2225
|
-
* neither fires.
|
|
2222
|
+
* deliverable warning (deduped) into one advisory `systemMessage`, or "" when
|
|
2223
|
+
* neither fires. TeammateIdle rejects `hookSpecificOutput` (no `additionalContext`
|
|
2224
|
+
* channel) — `systemMessage` is the only user-visible, non-blocking channel it
|
|
2225
|
+
* accepts.
|
|
2226
2226
|
* @param data - The raw TeammateIdle payload (`teammate_name`, `session_id`).
|
|
2227
2227
|
* @param cwd - Project root (state dir for the dedup sidecar).
|
|
2228
2228
|
* @param home - Home dir (defaults to `~`).
|
|
@@ -2230,7 +2230,7 @@ function bodyOf(stdout) {
|
|
|
2230
2230
|
* @returns The native hook stdout, or "".
|
|
2231
2231
|
*/
|
|
2232
2232
|
function teammateIdleContext(data, cwd, home = homedir(), now = Date.now()) {
|
|
2233
|
-
const sniper =
|
|
2233
|
+
const sniper = validateTeammateOutput(data, home);
|
|
2234
2234
|
const sessionId = sanitizeSessionId(data.session_id);
|
|
2235
2235
|
const teammate = String(data.teammate_name ?? data.team_name ?? "unknown");
|
|
2236
2236
|
let notice = "";
|
|
@@ -2244,7 +2244,7 @@ function teammateIdleContext(data, cwd, home = homedir(), now = Date.now()) {
|
|
|
2244
2244
|
const merged = [sniper, notice].filter(Boolean).join("\n\n");
|
|
2245
2245
|
if (!merged) return "";
|
|
2246
2246
|
notify("human");
|
|
2247
|
-
return
|
|
2247
|
+
return systemMessage(merged);
|
|
2248
2248
|
}
|
|
2249
2249
|
//#endregion
|
|
2250
2250
|
//#region src/policy/lessons/trigger-index.ts
|
|
@@ -3148,17 +3148,10 @@ function collectViolations(files, max) {
|
|
|
3148
3148
|
return violations;
|
|
3149
3149
|
}
|
|
3150
3150
|
/**
|
|
3151
|
-
* Handle TaskCompleted (ports `task-completed/validate-task-solid.py
|
|
3152
|
-
* receipt gate)
|
|
3153
|
-
*
|
|
3154
|
-
* that has no fresh passing tsc/test receipt. Also reused verbatim from `Stop`
|
|
3155
|
-
* for Codex's core scope ({@link stopCore}): Codex never emits `TaskCompleted`
|
|
3156
|
-
* (codex-plugins/docs/reference/hooks.md) so this is its only completion check;
|
|
3157
|
-
* `event` lets that caller stamp the real hook name instead of a false one.
|
|
3151
|
+
* Handle TaskCompleted (ports `task-completed/validate-task-solid.py` + the
|
|
3152
|
+
* receipt gate); also reused verbatim for Codex's `Stop` via {@link stopCore}
|
|
3153
|
+
* (Codex never emits `TaskCompleted`) — `event` stamps the real hook name.
|
|
3158
3154
|
* @param payload - The TaskCompleted/Stop payload (`task_id`, `task_subject`, `session_id`).
|
|
3159
|
-
* @param home - Home dir (defaults to `~`).
|
|
3160
|
-
* @param now - Clock (defaults to `Date.now()`).
|
|
3161
|
-
* @param stateDir - Track base dir (defaults to the cwd-derived state dir; matches `handleHook`).
|
|
3162
3155
|
* @param event - The hook event name to stamp on the SOLID-violation response (default `TaskCompleted`).
|
|
3163
3156
|
* @returns The native hook stdout, or `""` when the session is clean.
|
|
3164
3157
|
*/
|
|
@@ -3171,7 +3164,8 @@ function validateTaskSolid(payload, home = homedir(), now = Date.now(), stateDir
|
|
|
3171
3164
|
const violations = collectViolations(files, max);
|
|
3172
3165
|
if (violations.length === 0) return receiptGate(sid, files, now, stateDir) ?? "";
|
|
3173
3166
|
const taskId = String(payload.task_id ?? "");
|
|
3174
|
-
|
|
3167
|
+
const msg = `SOLID VIOLATION in task '${String(payload.task_subject ?? "")}' (${taskId}): ${violations.length} file(s) exceed ${max} lines: ` + violations.slice(0, 5).join("; ");
|
|
3168
|
+
return event === "TaskCompleted" ? systemMessage(msg) : contextResponse(event, msg);
|
|
3175
3169
|
}
|
|
3176
3170
|
//#endregion
|
|
3177
3171
|
//#region src/runtime/lifecycle/cartographer/fs-util.ts
|
|
@@ -4003,13 +3997,13 @@ function injectMemory(cwd, event, now) {
|
|
|
4003
3997
|
if (!content) return "";
|
|
4004
3998
|
const { content: curated, archive, report } = curateLessons(content, now, root);
|
|
4005
3999
|
if (curated !== content) content = persistCuration(file, root, curated, archive, content);
|
|
4006
|
-
const
|
|
4007
|
-
return report ? attachSystemMessage(
|
|
4000
|
+
const withNotice = attachSystemMessage(contextResponse(event, `Project lessons — never reproduce these:\n${capFragment("lessons", compressInjection(content))}\nYou may append OR refine/merge/dedupe bullets in MEMORY/LESSON.md — keep it terse.`), "lessons injected");
|
|
4001
|
+
return report ? attachSystemMessage(withNotice, `LESSON.md curation:\n${report}`) : withNotice;
|
|
4008
4002
|
}
|
|
4009
4003
|
/**
|
|
4010
4004
|
* Route a fuse-lessons event to its handler. Returns the native stdout for
|
|
4011
|
-
* context-injecting events (SessionStart/SubagentStart/
|
|
4012
|
-
* side-effect-only PostToolUse mark.
|
|
4005
|
+
* context-injecting events (SessionStart/SubagentStart/UserPromptSubmit inject
|
|
4006
|
+
* MEMORY/LESSON.md, Stop reminds) or "" for the side-effect-only PostToolUse mark.
|
|
4013
4007
|
* @param event - The raw hook event name.
|
|
4014
4008
|
* @param payload - The raw hook payload.
|
|
4015
4009
|
* @param cwd - Project root for memory injection.
|
|
@@ -4019,7 +4013,8 @@ function injectMemory(cwd, event, now) {
|
|
|
4019
4013
|
function dispatchLessons(event, payload, cwd, now) {
|
|
4020
4014
|
switch (event) {
|
|
4021
4015
|
case "SessionStart":
|
|
4022
|
-
case "SubagentStart":
|
|
4016
|
+
case "SubagentStart":
|
|
4017
|
+
case "UserPromptSubmit": return injectMemory(cwd, event, now);
|
|
4023
4018
|
case "Stop": return remindWrite(payload, now);
|
|
4024
4019
|
case "PostToolUse":
|
|
4025
4020
|
markWrite(payload, now);
|
|
@@ -4084,18 +4079,25 @@ function cartographerContext() {
|
|
|
4084
4079
|
return `\n### 7. Cartographer Maps\nNavigate branches (index.md) -> leaves link to real files:\n- Plugin skills: ${pluginsMap}\n- Project files: .cartographer/project/index.md`;
|
|
4085
4080
|
}
|
|
4086
4081
|
/**
|
|
4087
|
-
* Build the APEX sub-agent injection for SubagentStart, or "" when the project
|
|
4088
|
-
* has no `.claude/apex/` dir. Reads AGENTS.md (first 4KB) + task.json.
|
|
4082
|
+
* Build the APEX sub-agent injection for SubagentStart, or "" when the project has no target apex dir (`.claude/apex/`, `.codex/apex/`, ...). Reads AGENTS.md (first 4KB) + task.json.
|
|
4089
4083
|
* @param cwd - Fallback project root when `CLAUDE_PROJECT_DIR` is unset.
|
|
4090
4084
|
* @param home - Home dir (unused placeholder; kept for symmetry/testing).
|
|
4085
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
4091
4086
|
* @returns The native hook stdout (possibly empty).
|
|
4092
4087
|
*/
|
|
4093
|
-
async function injectApexSubagentContext(cwd, home = homedir()) {
|
|
4094
|
-
const
|
|
4088
|
+
async function injectApexSubagentContext(cwd, home = homedir(), id = "claude-code") {
|
|
4089
|
+
const projectRoot = process.env.CLAUDE_PROJECT_DIR ?? cwd;
|
|
4090
|
+
const seg = harnessHomeSegment(id);
|
|
4091
|
+
const apexDir = join(projectRoot, seg, "apex");
|
|
4095
4092
|
if (!existsSync(apexDir)) return "";
|
|
4096
4093
|
const agentsPath = join(apexDir, "AGENTS.md");
|
|
4097
4094
|
const agents = existsSync(agentsPath) ? readText(agentsPath).slice(0, 4e3) : "";
|
|
4098
4095
|
const taskData = await readJsonFile(join(apexDir, "task.json"));
|
|
4096
|
+
const completed = taskData ? completedTasks(taskData.tasks) : "none";
|
|
4097
|
+
const pending = taskData ? pendingTasks(taskData.tasks) : "none";
|
|
4098
|
+
const isCodex = id === "codex";
|
|
4099
|
+
const beforeStart = isCodex ? "Use update_plan → mark the active step in_progress before starting" : "Use TaskUpdate(taskId, status: in_progress) before starting";
|
|
4100
|
+
const whenDone = isCodex ? "update_plan → mark the step completed when done" : "TaskUpdate(taskId, status: completed) triggers auto-commit";
|
|
4099
4101
|
return contextResponse("SubagentStart", capFragment("apex-subagent", `## APEX Sub-Agent Instructions
|
|
4100
4102
|
|
|
4101
4103
|
You are a sub-agent in APEX workflow. Follow these rules:
|
|
@@ -4104,24 +4106,24 @@ You are a sub-agent in APEX workflow. Follow these rules:
|
|
|
4104
4106
|
${agents}
|
|
4105
4107
|
|
|
4106
4108
|
### 2. Task Context
|
|
4107
|
-
- Last completed: ${
|
|
4108
|
-
- Pending: ${
|
|
4109
|
+
- Last completed: ${completed}
|
|
4110
|
+
- Pending: ${pending}
|
|
4109
4111
|
|
|
4110
4112
|
### 3. Before Starting Work
|
|
4111
|
-
-
|
|
4113
|
+
- ${beforeStart}
|
|
4112
4114
|
|
|
4113
4115
|
### 4. SOLID Rules
|
|
4114
4116
|
- Files < ${resolveMaxLines()} lines | Interfaces in src/interfaces/ | JSDoc/PHPDoc required
|
|
4115
4117
|
|
|
4116
4118
|
### 5. Research Before Code
|
|
4117
|
-
- Use Context7/Exa for docs | Write notes to
|
|
4119
|
+
- Use Context7/Exa for docs | Write notes to ${seg}/apex/docs/
|
|
4118
4120
|
|
|
4119
4121
|
### 6. Before Done (NEVER skip)
|
|
4120
4122
|
- eLicit: self-review with a NAMED elicitation technique; fix findings first
|
|
4121
4123
|
- Verify: run/functional-check your changes (references⇔declarations)
|
|
4122
4124
|
|
|
4123
4125
|
### 7. When Done
|
|
4124
|
-
-
|
|
4126
|
+
- ${whenDone}${cartographerContext()}`));
|
|
4125
4127
|
}
|
|
4126
4128
|
/** 16-char hex SHA-256 of `text` (project hash / doc topic key). */
|
|
4127
4129
|
function hashText16(text) {
|
|
@@ -5057,18 +5059,22 @@ async function onComplete(taskFile, taskId, projectRoot) {
|
|
|
5057
5059
|
return contextResponse("PostToolUse", `Task #${taskId} completed: ${(await readJsonFile(taskFile))?.tasks[taskId]?.subject ?? "Task"}\n\nChanges detected. MANDATORY: Run /fuse-commit-pro:commit to commit with smart detection.`);
|
|
5058
5060
|
}
|
|
5059
5061
|
/**
|
|
5060
|
-
* PostToolUse TaskCreate/TaskUpdate handler.
|
|
5062
|
+
* PostToolUse TaskCreate/TaskUpdate handler. Claude-only by design: Codex never
|
|
5063
|
+
* emits these native task tools, so the `toolName` matcher below intentionally
|
|
5064
|
+
* stays as-is — only the target apex-dir segment is harness-aware.
|
|
5061
5065
|
* @param payload - The raw hook payload (`tool_name`, `tool_input`, `tool_response`).
|
|
5062
5066
|
* @param cwd - Fallback project root (uses `CLAUDE_PROJECT_DIR` first).
|
|
5067
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
5063
5068
|
* @returns The native hook stdout (possibly empty).
|
|
5064
5069
|
*/
|
|
5065
|
-
async function syncTaskTracking(payload, cwd) {
|
|
5070
|
+
async function syncTaskTracking(payload, cwd, id = "claude-code") {
|
|
5066
5071
|
const toolName = String(payload.tool_name ?? "");
|
|
5067
5072
|
if (toolName !== "TaskCreate" && toolName !== "TaskUpdate") return "";
|
|
5068
5073
|
const projectRoot = process.env.CLAUDE_PROJECT_DIR ?? cwd;
|
|
5069
|
-
const
|
|
5074
|
+
const seg = harnessHomeSegment(id);
|
|
5075
|
+
const taskFile = join(projectRoot, seg, "apex", "task.json");
|
|
5070
5076
|
if (!existsSync(taskFile)) return "";
|
|
5071
|
-
const unlock = await acquireLock(join(projectRoot,
|
|
5077
|
+
const unlock = await acquireLock(join(projectRoot, seg, "apex", ".task.lock"), 1e4);
|
|
5072
5078
|
if (!unlock) return "";
|
|
5073
5079
|
try {
|
|
5074
5080
|
const ti = payload.tool_input ?? {};
|
|
@@ -5277,9 +5283,9 @@ async function typeSpecificCache(agent, cwd, now, home) {
|
|
|
5277
5283
|
* entries (APEX context + lessons) fire for EVERY sub-agent, then the type-specific
|
|
5278
5284
|
* cache is concatenated on top — sniper too, hence no early return.
|
|
5279
5285
|
*/
|
|
5280
|
-
async function onSubagentStart(payload, cwd, now, home) {
|
|
5286
|
+
async function onSubagentStart(payload, cwd, now, home, id) {
|
|
5281
5287
|
const agent = agentTypeOf(payload);
|
|
5282
|
-
return combineContext(await injectApexSubagentContext(cwd, home), await injectLessonsCache(cwd, home, now), await typeSpecificCache(agent, cwd, now, home));
|
|
5288
|
+
return combineContext(await injectApexSubagentContext(cwd, home, id), await injectLessonsCache(cwd, home, now), await typeSpecificCache(agent, cwd, now, home));
|
|
5283
5289
|
}
|
|
5284
5290
|
/** SubagentStop routing: transcript-driven cache writers, then the universal SOLID check. */
|
|
5285
5291
|
async function onSubagentStop(payload, cwd, home) {
|
|
@@ -5295,10 +5301,10 @@ async function onSubagentStop(payload, cwd, home) {
|
|
|
5295
5301
|
/**
|
|
5296
5302
|
* Dispatch an ai-pilot-scope lifecycle event. Returns the native stdout, or
|
|
5297
5303
|
* `null` when unhandled (caller falls through to the default pipeline).
|
|
5298
|
-
* @param home - Home dir for cache resolution (defaults to `~`; injectable for test isolation).
|
|
5304
|
+
* @param home - Home dir for cache resolution (defaults to `~`; injectable for test isolation); `id` selects the harness target (defaults to "claude-code").
|
|
5299
5305
|
*/
|
|
5300
|
-
async function dispatchAipilot(event, payload, cwd, now, home = homedir()) {
|
|
5301
|
-
if (event === "SubagentStart") return onSubagentStart(payload, cwd, now, home);
|
|
5306
|
+
async function dispatchAipilot(event, payload, cwd, now, home = homedir(), id = "claude-code") {
|
|
5307
|
+
if (event === "SubagentStart") return onSubagentStart(payload, cwd, now, home, id);
|
|
5302
5308
|
if (event === "SubagentStop") return onSubagentStop(payload, cwd, home);
|
|
5303
5309
|
if (event === "SessionEnd" || event === "Stop") {
|
|
5304
5310
|
await cacheAnalyticsSave(home, now);
|
|
@@ -5307,9 +5313,9 @@ async function dispatchAipilot(event, payload, cwd, now, home = homedir()) {
|
|
|
5307
5313
|
if (event === "PreToolUse") return docCacheGate(payload, cwd, now, home);
|
|
5308
5314
|
return null;
|
|
5309
5315
|
}
|
|
5310
|
-
/** PostToolUse (Write/Edit SOLID check, else TaskCreate/TaskUpdate sync) for the ai-pilot scope. */
|
|
5311
|
-
async function aipilotPostToolUse(payload, cwd) {
|
|
5312
|
-
return checkSolidCompliance(payload) || await syncTaskTracking(payload, cwd);
|
|
5316
|
+
/** PostToolUse (Write/Edit SOLID check, else TaskCreate/TaskUpdate sync) for the ai-pilot scope; `id` selects the harness target (defaults to "claude-code"). */
|
|
5317
|
+
async function aipilotPostToolUse(payload, cwd, id = "claude-code") {
|
|
5318
|
+
return checkSolidCompliance(payload) || await syncTaskTracking(payload, cwd, id);
|
|
5313
5319
|
}
|
|
5314
5320
|
//#endregion
|
|
5315
5321
|
//#region src/runtime/lifecycle/dispatch.ts
|
|
@@ -5332,7 +5338,10 @@ function sessionStart(input) {
|
|
|
5332
5338
|
function dispatchLifecycle(input) {
|
|
5333
5339
|
switch (input.event) {
|
|
5334
5340
|
case "SessionStart": return sessionStart(input);
|
|
5335
|
-
case "UserPromptSubmit":
|
|
5341
|
+
case "UserPromptSubmit":
|
|
5342
|
+
if (input.scope === "rules") return injectRules(process.env.CLAUDE_PLUGIN_ROOT ?? input.cwd, input.event);
|
|
5343
|
+
if (input.scope === "lessons") return dispatchLessons("UserPromptSubmit", input.payload, input.cwd, input.now);
|
|
5344
|
+
return null;
|
|
5336
5345
|
case "SubagentStart":
|
|
5337
5346
|
if (input.scope === "rules") return injectRules(process.env.CLAUDE_PLUGIN_ROOT ?? input.cwd, input.event);
|
|
5338
5347
|
if (input.scope === "aipilot") return "";
|
|
@@ -5348,7 +5357,7 @@ function dispatchLifecycle(input) {
|
|
|
5348
5357
|
case "TeammateIdle": return teammateIdleContext(input.payload, input.cwd, void 0, input.now);
|
|
5349
5358
|
case "PostToolUseFailure": return failureLessonContext(input.payload, input.cwd, void 0, input.now);
|
|
5350
5359
|
case "PostCompact": return input.scope === "core" ? postCompactContext(input.payload, input.cwd, import.meta.url, input.now) : "";
|
|
5351
|
-
case "PreCompact": return saveApexState(input.cwd, input.now);
|
|
5360
|
+
case "PreCompact": return saveApexState(input.cwd, input.now, input.id ?? "claude-code");
|
|
5352
5361
|
case "SessionEnd":
|
|
5353
5362
|
if (input.scope !== "aipilot") cleanupSession(void 0, input.now);
|
|
5354
5363
|
return "";
|
|
@@ -5890,19 +5899,22 @@ function docTypeOf(filePath) {
|
|
|
5890
5899
|
}
|
|
5891
5900
|
/**
|
|
5892
5901
|
* Auto-document a Read of a SKILL.md/README/CLAUDE.md/docs/references file
|
|
5893
|
-
* into `.claude/apex/docs/
|
|
5894
|
-
*
|
|
5902
|
+
* into the target apex docs dir (`.claude/apex/docs/`, `.codex/apex/docs/`,
|
|
5903
|
+
* ...) as `task-<current>-<framework>.md` (skipped when the file is already
|
|
5904
|
+
* logged, or no project root is found from `filePath`).
|
|
5895
5905
|
* @param filePath - The path passed to the Read tool.
|
|
5896
5906
|
* @param now - Clock (defaults to `Date.now()`).
|
|
5907
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
5897
5908
|
* @returns The native `systemMessage` stdout, or "" when nothing was logged.
|
|
5898
5909
|
*/
|
|
5899
|
-
async function autoDocumentRead(filePath, now = Date.now()) {
|
|
5910
|
+
async function autoDocumentRead(filePath, now = Date.now(), id = "claude-code") {
|
|
5900
5911
|
if (!filePath || !DOC_PATTERNS.some((p) => p.test(filePath))) return "";
|
|
5901
5912
|
const root = projectRootOrNull(dirname(filePath));
|
|
5902
5913
|
if (!root) return "";
|
|
5914
|
+
const seg = harnessHomeSegment(id);
|
|
5903
5915
|
const framework = detectProjectType$1(root);
|
|
5904
|
-
const current = (await readJsonFile(join(root,
|
|
5905
|
-
const docDir = join(root,
|
|
5916
|
+
const current = (await readJsonFile(join(root, seg, "apex", "task.json")))?.current_task ?? "1";
|
|
5917
|
+
const docDir = join(root, seg, "apex", "docs");
|
|
5906
5918
|
const docFile = join(docDir, `task-${current}-${framework}.md`);
|
|
5907
5919
|
const ts = new Date(now).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
5908
5920
|
const fname = basename(filePath);
|
|
@@ -5941,15 +5953,17 @@ function additionalContextOf(stdout) {
|
|
|
5941
5953
|
* @param cwd - Project root.
|
|
5942
5954
|
* @param scope - The invoking plugin scope (defaults to `core`).
|
|
5943
5955
|
* @param now - Clock.
|
|
5956
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
5944
5957
|
* @returns The native stdout, or `null` when unhandled.
|
|
5945
5958
|
*/
|
|
5946
|
-
function lifecycleStdout(payload, cwd, scope, now) {
|
|
5959
|
+
function lifecycleStdout(payload, cwd, scope, now, id = "claude-code") {
|
|
5947
5960
|
return dispatchLifecycle({
|
|
5948
5961
|
event: rawEvent(payload),
|
|
5949
5962
|
payload,
|
|
5950
5963
|
cwd,
|
|
5951
5964
|
scope,
|
|
5952
|
-
now
|
|
5965
|
+
now,
|
|
5966
|
+
id
|
|
5953
5967
|
});
|
|
5954
5968
|
}
|
|
5955
5969
|
/**
|
|
@@ -5962,11 +5976,12 @@ function lifecycleStdout(payload, cwd, scope, now) {
|
|
|
5962
5976
|
* @param scope - The invoking plugin scope.
|
|
5963
5977
|
* @param event - The normalized event.
|
|
5964
5978
|
* @param now - Clock.
|
|
5979
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
5965
5980
|
* @returns The extra stdout (possibly empty).
|
|
5966
5981
|
*/
|
|
5967
|
-
async function postEditContext(scope, event, now) {
|
|
5982
|
+
async function postEditContext(scope, event, now, id = "claude-code") {
|
|
5968
5983
|
if (scope !== "core" || !event.filePath) return "";
|
|
5969
|
-
if (event.tool === "Read") return autoDocumentRead(event.filePath, now);
|
|
5984
|
+
if (event.tool === "Read") return autoDocumentRead(event.filePath, now, id);
|
|
5970
5985
|
if (event.tool !== "Write" && event.tool !== "Edit") return "";
|
|
5971
5986
|
const sniper = trackSessionChanges(event.sessionId, event.filePath, void 0, now);
|
|
5972
5987
|
const lint = postEditTypescript(event.filePath);
|
|
@@ -6026,7 +6041,7 @@ const EXEMPT_PATTERNS = [
|
|
|
6026
6041
|
/\.claude-plugin\//,
|
|
6027
6042
|
/CHANGELOG\.md$/,
|
|
6028
6043
|
/marketplace\.json$/,
|
|
6029
|
-
/\/\.claude\/(apex|memory|logs|fusengine-cache)\//,
|
|
6044
|
+
/\/\.(claude|codex)\/(apex|memory|logs|fusengine-cache)\//,
|
|
6030
6045
|
/\/\.fuse-harness\//
|
|
6031
6046
|
];
|
|
6032
6047
|
/**
|
|
@@ -7192,18 +7207,25 @@ async function recordActivity(file, activity) {
|
|
|
7192
7207
|
* - cline: NO user-visible channel (`cancel`/`contextModification`/
|
|
7193
7208
|
* `errorMessage`-on-cancel only) — the notice is dropped silently (a pure
|
|
7194
7209
|
* notice returns "" = plain allow).
|
|
7195
|
-
|
|
7196
|
-
|
|
7210
|
+
* @param id - Harness id.
|
|
7211
|
+
* @param prompt - The portable prompt to render.
|
|
7212
|
+
* @param event - The firing hook event name for the claude-code/codex branch
|
|
7213
|
+
* (defaults to `"PreToolUse"`, the only phase every existing caller but
|
|
7214
|
+
* `handle-post.ts` renders) — a POST-phase caller MUST pass `"PostToolUse"`
|
|
7215
|
+
* (or its real raw event name) so `hookEventName` matches the event that
|
|
7216
|
+
* actually fired instead of a hardcoded, wrong `"PreToolUse"`.
|
|
7217
|
+
*/
|
|
7218
|
+
function respond(id, prompt, event = "PreToolUse") {
|
|
7197
7219
|
const message = formatPrompt(prompt);
|
|
7198
7220
|
const { kind, userMessage, reason } = prompt;
|
|
7199
7221
|
switch (id) {
|
|
7200
7222
|
case "claude-code":
|
|
7201
7223
|
case "codex":
|
|
7202
|
-
if (kind === "block") return denyResponse(
|
|
7203
|
-
if (kind === "inform") return userMessage ? informResponse(
|
|
7204
|
-
if (id === "codex") return denyResponse(
|
|
7224
|
+
if (kind === "block") return denyResponse(event, message);
|
|
7225
|
+
if (kind === "inform") return userMessage ? informResponse(event, userMessage, reason ? message : "") : contextResponse(event, message);
|
|
7226
|
+
if (id === "codex") return denyResponse(event, `[downgraded from ask — Codex has no interactive approval]\n${message}`);
|
|
7205
7227
|
return JSON.stringify({ hookSpecificOutput: {
|
|
7206
|
-
hookEventName:
|
|
7228
|
+
hookEventName: event,
|
|
7207
7229
|
permissionDecision: "ask",
|
|
7208
7230
|
permissionDecisionReason: message
|
|
7209
7231
|
} });
|
|
@@ -7873,7 +7895,7 @@ async function handlePre(ctx) {
|
|
|
7873
7895
|
exit: 0
|
|
7874
7896
|
};
|
|
7875
7897
|
if (event.tool === "Task") {
|
|
7876
|
-
const taskCtx = taskContext(opts.cwd);
|
|
7898
|
+
const taskCtx = taskContext(opts.cwd, id);
|
|
7877
7899
|
if (taskCtx) return {
|
|
7878
7900
|
stdout: taskCtx,
|
|
7879
7901
|
exit: 0
|
|
@@ -8440,7 +8462,7 @@ async function handlePost(ctx) {
|
|
|
8440
8462
|
};
|
|
8441
8463
|
}
|
|
8442
8464
|
if (opts.scope === "aipilot" && (event.tool === "TaskCreate" || event.tool === "TaskUpdate" || event.tool === "Write" || event.tool === "Edit")) {
|
|
8443
|
-
const out = await aipilotPostToolUse(payload, opts.cwd);
|
|
8465
|
+
const out = await aipilotPostToolUse(payload, opts.cwd, id);
|
|
8444
8466
|
if (out) return {
|
|
8445
8467
|
stdout: out,
|
|
8446
8468
|
exit: 0
|
|
@@ -8448,7 +8470,7 @@ async function handlePost(ctx) {
|
|
|
8448
8470
|
}
|
|
8449
8471
|
let extra = "";
|
|
8450
8472
|
for (const f of files) {
|
|
8451
|
-
extra = await postEditContext(opts.scope ?? "core", f, opts.now);
|
|
8473
|
+
extra = await postEditContext(opts.scope ?? "core", f, opts.now, id);
|
|
8452
8474
|
if (extra) break;
|
|
8453
8475
|
}
|
|
8454
8476
|
const notice = designPassNotice({
|
|
@@ -8465,7 +8487,7 @@ async function handlePost(ctx) {
|
|
|
8465
8487
|
stdout: respond(id, userMessage ? {
|
|
8466
8488
|
...designWarn,
|
|
8467
8489
|
userMessage
|
|
8468
|
-
} : designWarn),
|
|
8490
|
+
} : designWarn, "PostToolUse"),
|
|
8469
8491
|
exit: 0
|
|
8470
8492
|
};
|
|
8471
8493
|
if (!userMessage) return {
|
|
@@ -8482,7 +8504,7 @@ async function handlePost(ctx) {
|
|
|
8482
8504
|
userMessage
|
|
8483
8505
|
};
|
|
8484
8506
|
if (!extra) return {
|
|
8485
|
-
stdout: respond(id, withUserMessage),
|
|
8507
|
+
stdout: respond(id, withUserMessage, "PostToolUse"),
|
|
8486
8508
|
exit: 0
|
|
8487
8509
|
};
|
|
8488
8510
|
if (id === "claude-code" || id === "codex") return {
|
|
@@ -8490,7 +8512,7 @@ async function handlePost(ctx) {
|
|
|
8490
8512
|
exit: 0
|
|
8491
8513
|
};
|
|
8492
8514
|
return {
|
|
8493
|
-
stdout: respond(id, withUserMessage) || extra,
|
|
8515
|
+
stdout: respond(id, withUserMessage, "PostToolUse") || extra,
|
|
8494
8516
|
exit: 0
|
|
8495
8517
|
};
|
|
8496
8518
|
}
|
|
@@ -8508,10 +8530,11 @@ async function handlePost(ctx) {
|
|
|
8508
8530
|
* @param payload - The raw hook payload.
|
|
8509
8531
|
* @param cwd - Project root.
|
|
8510
8532
|
* @param now - Clock.
|
|
8533
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
8511
8534
|
* @returns The native stdout when intercepted, or `null` to fall through.
|
|
8512
8535
|
*/
|
|
8513
|
-
async function asyncScopeStdout(scope, event, payload, cwd, now) {
|
|
8514
|
-
if (scope === "aipilot") return dispatchAipilot(event, payload, cwd, now);
|
|
8536
|
+
async function asyncScopeStdout(scope, event, payload, cwd, now, id = "claude-code") {
|
|
8537
|
+
if (scope === "aipilot") return dispatchAipilot(event, payload, cwd, now, void 0, id);
|
|
8515
8538
|
if (scope === "memory") return dispatchMemory(event, payload, cwd, now);
|
|
8516
8539
|
return null;
|
|
8517
8540
|
}
|
|
@@ -8925,12 +8948,12 @@ async function handleHook(id, payload, opts) {
|
|
|
8925
8948
|
exit: 0
|
|
8926
8949
|
};
|
|
8927
8950
|
if (id === "codex" && rawEventName(payload) === "SessionStart") resyncCodexAgents();
|
|
8928
|
-
const asyncOut = await asyncScopeStdout(opts.scope, rawEventName(payload), payload, opts.cwd, opts.now);
|
|
8951
|
+
const asyncOut = await asyncScopeStdout(opts.scope, rawEventName(payload), payload, opts.cwd, opts.now, id);
|
|
8929
8952
|
if (asyncOut !== null) return {
|
|
8930
8953
|
stdout: asyncOut,
|
|
8931
8954
|
exit: 0
|
|
8932
8955
|
};
|
|
8933
|
-
const life = lifecycleStdout(payload, opts.cwd, opts.scope ?? "core", opts.now);
|
|
8956
|
+
const life = lifecycleStdout(payload, opts.cwd, opts.scope ?? "core", opts.now, id);
|
|
8934
8957
|
if (life !== null) return {
|
|
8935
8958
|
stdout: id === "claude-code" ? attachBudgetRecap(life, rawEventName(payload), event.sessionId, opts.cwd, opts.now) : life,
|
|
8936
8959
|
exit: 0
|
|
@@ -8939,7 +8962,7 @@ async function handleHook(id, payload, opts) {
|
|
|
8939
8962
|
if (userPrompt !== void 0) {
|
|
8940
8963
|
await saveTrack(file, recordBrainstormRequired(await loadTrack(file), detectCreationIntent(userPrompt)));
|
|
8941
8964
|
return {
|
|
8942
|
-
stdout: promptSubmitContext(userPrompt, opts.cwd),
|
|
8965
|
+
stdout: promptSubmitContext(userPrompt, opts.cwd, id),
|
|
8943
8966
|
exit: 0
|
|
8944
8967
|
};
|
|
8945
8968
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as evaluate } from "./evaluate-ClfbCDiY.mjs";
|
|
2
2
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
3
|
-
import { c as readClaudeInput, v as commandToString } from "./claude-
|
|
3
|
+
import { c as readClaudeInput, v as commandToString } from "./claude-51RSspJ1.mjs";
|
|
4
4
|
//#region src/adapters/hermes/index.ts
|
|
5
5
|
/**
|
|
6
6
|
* Hermes Agent adapter (hook-mode). Nous Research's hermes-agent (2026) pipes a
|
|
@@ -412,9 +412,7 @@ declare function usesTailwindUtilities(filePath: string, content: string): boole
|
|
|
412
412
|
*/
|
|
413
413
|
declare function skillTriggerGate(framework: string, content: string, refsRead: readonly string[], forcedSkill?: string | null, cwd?: string, filePath?: string): Prompt | null;
|
|
414
414
|
//#endregion
|
|
415
|
-
//#region src/policy/claude-md-
|
|
416
|
-
/** Dev-verb regex (FR/EN) that triggers the APEX preamble (case-insensitive). */
|
|
417
|
-
declare const DEV_VERBS: RegExp;
|
|
415
|
+
//#region src/policy/detect-claude-md-project-type.d.ts
|
|
418
416
|
/**
|
|
419
417
|
* Detect the project type from the cwd, reproducing the legacy Python logic:
|
|
420
418
|
* package.json containing "next" → nextjs, else "react" → react; else
|
|
@@ -424,22 +422,29 @@ declare const DEV_VERBS: RegExp;
|
|
|
424
422
|
* @returns The detected project type label.
|
|
425
423
|
*/
|
|
426
424
|
declare function detectClaudeMdProjectType(cwd: string): ProjectType;
|
|
425
|
+
//#endregion
|
|
426
|
+
//#region src/policy/claude-md-context.d.ts
|
|
427
|
+
/** Dev-verb regex (FR/EN) that triggers the APEX preamble (case-insensitive). */
|
|
428
|
+
declare const DEV_VERBS: RegExp;
|
|
427
429
|
/**
|
|
428
430
|
* Build the APEX instruction preamble for a development task.
|
|
429
431
|
* @param projectType - Detected project type label.
|
|
430
432
|
* @param maxLines - SOLID per-file line ceiling.
|
|
433
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
431
434
|
* @returns The APEX instruction text.
|
|
432
435
|
*/
|
|
433
|
-
declare function buildApexInstruction(projectType: ProjectType, maxLines: number): string;
|
|
436
|
+
declare function buildApexInstruction(projectType: ProjectType, maxLines: number, id?: string): string;
|
|
434
437
|
/**
|
|
435
|
-
* Build the UserPromptSubmit injection text: read
|
|
438
|
+
* Build the UserPromptSubmit injection text: read the target's root
|
|
439
|
+
* instructions doc (`~/.claude/CLAUDE.md`, `~/.codex/AGENTS.md`, ...) and,
|
|
436
440
|
* when the prompt matches a dev verb, prepend the APEX instruction. Returns
|
|
437
|
-
* `null` when
|
|
441
|
+
* `null` when the doc is absent/unreadable (the hook then emits nothing).
|
|
438
442
|
* @param prompt - The raw user prompt.
|
|
439
443
|
* @param cwd - Project root (for project-type detection).
|
|
444
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
440
445
|
* @returns The injection text, or `null` to emit nothing.
|
|
441
446
|
*/
|
|
442
|
-
declare function buildClaudeMdContext(prompt: string, cwd: string): string | null;
|
|
447
|
+
declare function buildClaudeMdContext(prompt: string, cwd: string, id?: string): string | null;
|
|
443
448
|
//#endregion
|
|
444
449
|
//#region src/policy/apex-task-context.d.ts
|
|
445
450
|
/** Parsed task state injected into a Task sub-agent prompt. */
|
|
@@ -464,16 +469,19 @@ declare function loadApexTaskState(taskFile: string): ApexTaskState;
|
|
|
464
469
|
* Build the APEX context string injected into a Task sub-agent prompt.
|
|
465
470
|
* @param state - The parsed task state.
|
|
466
471
|
* @param maxLines - SOLID per-file line ceiling.
|
|
472
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
467
473
|
* @returns The injection text.
|
|
468
474
|
*/
|
|
469
|
-
declare function buildApexTaskContext(state: ApexTaskState, maxLines: number): string;
|
|
475
|
+
declare function buildApexTaskContext(state: ApexTaskState, maxLines: number, id?: string): string;
|
|
470
476
|
/**
|
|
471
|
-
* Build the PreToolUse Task injection, gated on the existence of the
|
|
472
|
-
* `.claude/apex
|
|
477
|
+
* Build the PreToolUse Task injection, gated on the existence of the
|
|
478
|
+
* project's target apex dir (`.claude/apex/`, `.codex/apex/`, ...). Returns
|
|
479
|
+
* `null` when APEX is not active (no dir).
|
|
473
480
|
* @param projectRoot - `CLAUDE_PROJECT_DIR` or cwd.
|
|
481
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
474
482
|
* @returns The injection text, or `null` to emit nothing.
|
|
475
483
|
*/
|
|
476
|
-
declare function buildApexTaskInjection(projectRoot: string): string | null;
|
|
484
|
+
declare function buildApexTaskInjection(projectRoot: string, id?: string): string | null;
|
|
477
485
|
//#endregion
|
|
478
486
|
//#region src/policy/cartographer/indicators.d.ts
|
|
479
487
|
/**
|
|
@@ -31,19 +31,11 @@ declare function contextResponse(event: string, text: string): string;
|
|
|
31
31
|
declare function systemMessage(text: string): string;
|
|
32
32
|
/** An `inform` response with a user-visible notice (Python `allow_pass`/`post_pass` parity): a pure pass reuses {@link systemMessage} alone; with agent-facing `context` both channels merge in one JSON. */
|
|
33
33
|
declare function informResponse(event: string, notice: string, context: string): string;
|
|
34
|
-
/** Attach a
|
|
34
|
+
/** Attach a `systemMessage`, MERGING it with any already present (`\n`-joined) instead of overwriting — several producers may each call this on the same stdout in one hook invocation. */
|
|
35
35
|
declare function attachSystemMessage(stdout: string, notice: string): string;
|
|
36
|
-
/**
|
|
37
|
-
* Render a portable {@link Prompt} as a Claude Code hook response:
|
|
38
|
-
* `block` → `permissionDecision: deny`, `ask` → `permissionDecision: ask`
|
|
39
|
-
* (interactive confirm), `inform` → `additionalContext` — plus the user-visible
|
|
40
|
-
* `systemMessage` channel when the prompt carries a `userMessage`.
|
|
41
|
-
*/
|
|
36
|
+
/** Render a portable {@link Prompt} as a Claude Code hook response: `block` → deny, `ask` → interactive `permissionDecision`, `inform` → `additionalContext` (+ `systemMessage` when `userMessage` is set). */
|
|
42
37
|
declare function toClaudeResponse(event: string, prompt: Prompt): string;
|
|
43
|
-
/**
|
|
44
|
-
* Run the bundled policy over a Claude payload and return the native response
|
|
45
|
-
* string (deny/ask/additionalContext), or null to allow.
|
|
46
|
-
*/
|
|
38
|
+
/** Run the bundled policy over a Claude payload and return the native response string (deny/ask/additionalContext), or null to allow. */
|
|
47
39
|
declare function guard(input: ClaudeHookInput): string | null;
|
|
48
40
|
/** @deprecated use {@link guard}. Kept for back-compat. */
|
|
49
41
|
declare const fileSizeGuard: typeof guard;
|
package/dist/index.d.mts
CHANGED
|
@@ -5,7 +5,7 @@ import { a as HarnessInfo, i as HarnessId, n as detectMode, o as HarnessMode, r
|
|
|
5
5
|
import { a as isDocConsulted, i as formatDocSatisfactionStatus, n as DocSatisfactionStatus, o as resolveSessions, r as formatDocDeny, t as AuthEntry } from "./doc-helpers-CEKzGg2u.mjs";
|
|
6
6
|
import { t as incrementTrivialEditCounter } from "./index-BOBXQ91y.mjs";
|
|
7
7
|
import { a as compactJson, i as walkUpFor, n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./index-BEMumjOw.mjs";
|
|
8
|
-
import { $ as protectedPathGuard, A as FAIL_CLOSED, B as RUST_DECL_RE, C as usesTailwindUtilities, Ct as DEV_KEYWORDS, D as MAX_TOKENS, Dt as detectProjectType, E as MAX_EXA_RESULTS, Et as detectModularArchitecture, F as installGuard, G as ASK_WRITERS, H as TS_DECL_RE, I as GO_DECL_RE, J as FILE_REDIRECT, K as CODE_MUTATORS, L as JAVA_DECL_RE, M as clearUserGuards, N as registerGuard, O as capVerbosity, Ot as isApexCommand, P as runGuards, Q as PROTECTED_GIT_RE, R as PHP_DECL_RE, S as skillTriggerGate, St as detectFramework, T as frameworkSolidGate, Tt as ProjectType, U as interfaceSeparationGuard, V as SWIFT_PROTO_RE, W as bashWriteGuard, X as SESSION_STATE_FRAGMENT, Y as SAFE_PREFIXES, Z as PROTECTED_FRAGMENTS, _ as DEV_VERBS, _t as PLUGINS_DIR, a as firstHeading, at as GuardContext, b as detectClaudeMdProjectType, bt as countLines, c as parseEntry, ct as PolicyResult, d as EXCLUDE_DIRS, dt as PROJECT_INSTALL, et as ASK_PATTERNS, f as PROJECT_INDICATORS, ft as RALPH_SAFE, g as loadApexTaskState, gt as FileSizeVerdict, h as buildApexTaskInjection, ht as matchPatterns, i as firstComment, it as Guard, j as GUARDS, k as detectCreationIntent, kt as requiredArchSkill, l as parseBodyDesc, lt as GIT_ASK, m as buildApexTaskContext, mt as isRalphMode, n as missingSeoElements, nt as LabeledPattern, o as TreeEntry, ot as evaluate, p as ApexTaskState, pt as SYSTEM_INSTALL, q as CODE_REDIRECT, r as descFromText, rt as securityGuard, s as parseEnrichment, st as PolicyContext, t as isHtmlLike, tt as CRITICAL_PATTERNS, u as parseField, ut as GIT_BLOCKED, v as buildApexInstruction, vt as SOLID_REF, w as SKILL_TRIGGERS, wt as ModularArchitecture, x as detectRequiredSkills, xt as evaluateFileSize, y as buildClaudeMdContext, yt as countFrameworkCodeLines, z as PY_MODEL_RE } from "./index-
|
|
8
|
+
import { $ as protectedPathGuard, A as FAIL_CLOSED, B as RUST_DECL_RE, C as usesTailwindUtilities, Ct as DEV_KEYWORDS, D as MAX_TOKENS, Dt as detectProjectType, E as MAX_EXA_RESULTS, Et as detectModularArchitecture, F as installGuard, G as ASK_WRITERS, H as TS_DECL_RE, I as GO_DECL_RE, J as FILE_REDIRECT, K as CODE_MUTATORS, L as JAVA_DECL_RE, M as clearUserGuards, N as registerGuard, O as capVerbosity, Ot as isApexCommand, P as runGuards, Q as PROTECTED_GIT_RE, R as PHP_DECL_RE, S as skillTriggerGate, St as detectFramework, T as frameworkSolidGate, Tt as ProjectType, U as interfaceSeparationGuard, V as SWIFT_PROTO_RE, W as bashWriteGuard, X as SESSION_STATE_FRAGMENT, Y as SAFE_PREFIXES, Z as PROTECTED_FRAGMENTS, _ as DEV_VERBS, _t as PLUGINS_DIR, a as firstHeading, at as GuardContext, b as detectClaudeMdProjectType, bt as countLines, c as parseEntry, ct as PolicyResult, d as EXCLUDE_DIRS, dt as PROJECT_INSTALL, et as ASK_PATTERNS, f as PROJECT_INDICATORS, ft as RALPH_SAFE, g as loadApexTaskState, gt as FileSizeVerdict, h as buildApexTaskInjection, ht as matchPatterns, i as firstComment, it as Guard, j as GUARDS, k as detectCreationIntent, kt as requiredArchSkill, l as parseBodyDesc, lt as GIT_ASK, m as buildApexTaskContext, mt as isRalphMode, n as missingSeoElements, nt as LabeledPattern, o as TreeEntry, ot as evaluate, p as ApexTaskState, pt as SYSTEM_INSTALL, q as CODE_REDIRECT, r as descFromText, rt as securityGuard, s as parseEnrichment, st as PolicyContext, t as isHtmlLike, tt as CRITICAL_PATTERNS, u as parseField, ut as GIT_BLOCKED, v as buildApexInstruction, vt as SOLID_REF, w as SKILL_TRIGGERS, wt as ModularArchitecture, x as detectRequiredSkills, xt as evaluateFileSize, y as buildClaudeMdContext, yt as countFrameworkCodeLines, z as PY_MODEL_RE } from "./index-BIGVNQB8.mjs";
|
|
9
9
|
import { n as RouteResult, r as ScoredRef, t as RefMeta } from "./types-CY5qT2X1.mjs";
|
|
10
10
|
import { a as PRE_AUTH_GATES, c as evaluateApex, i as POST_AUTH_GATES, l as freshnessGate, n as ApexContext, o as brainstormGate, r as ApexGate, s as docConsultedGate, t as APEX_GATES, u as solidReadGate } from "./apex-Wdi1nq_w.mjs";
|
|
11
11
|
import { a as ReminderState, c as readState, d as throttleMs, i as registryFile, l as setStateField, n as addRoot, o as lessonsFileFor, r as readRoots, s as nowStamp, t as ensureMemoryGitignore, u as stateFileFor } from "./index-DLYhervv.mjs";
|
package/dist/index.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import { A as GIT_ASK, B as detectProjectType, C as SESSION_STATE_FRAGMENT, D as
|
|
|
8
8
|
import { d as countFrameworkCodeLines, f as countLines, l as PLUGINS_DIR, p as evaluateFileSize, u as SOLID_REF } from "./home-state-BXf38Zi1.mjs";
|
|
9
9
|
import { i as resolveSessions, n as formatDocSatisfactionStatus, r as isDocConsulted, t as formatDocDeny } from "./doc-helpers-CWZegVdR.mjs";
|
|
10
10
|
import { i as parseFrontmatter, n as scoreReferences, r as globToRe, t as routeReferences } from "./router-PKVNBHge.mjs";
|
|
11
|
-
import { A as
|
|
11
|
+
import { A as capVerbosity, C as usesTailwindUtilities, F as brainstormGate, I as docConsultedGate, L as evaluateApex, M as APEX_GATES, N as POST_AUTH_GATES, O as MAX_EXA_RESULTS, P as PRE_AUTH_GATES, R as freshnessGate, S as skillTriggerGate, T as frameworkSolidGate, a as firstHeading, b as parseField, c as EXCLUDE_DIRS, d as buildApexTaskInjection, f as loadApexTaskState, g as detectClaudeMdProjectType, h as buildClaudeMdContext, i as firstComment, j as detectCreationIntent, k as MAX_TOKENS, l as PROJECT_INDICATORS, m as buildApexInstruction, n as missingSeoElements, o as parseEnrichment, p as DEV_VERBS, r as descFromText, s as parseEntry, t as isHtmlLike, u as buildApexTaskContext, w as SKILL_TRIGGERS, x as detectRequiredSkills, y as parseBodyDesc, z as solidReadGate } from "./validate-DhOX5hDK.mjs";
|
|
12
12
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
13
13
|
import { a as nowStamp, c as stateFileFor, i as lessonsFileFor, l as throttleMs, n as readRoots, o as readState, r as registryFile, s as setStateField, t as addRoot, u as ensureMemoryGitignore } from "./registry-CymilZiZ.mjs";
|
|
14
14
|
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";
|
package/dist/policy/index.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { $ as protectedPathGuard, A as FAIL_CLOSED, B as RUST_DECL_RE, C as usesTailwindUtilities, Ct as DEV_KEYWORDS, D as MAX_TOKENS, Dt as detectProjectType, E as MAX_EXA_RESULTS, Et as detectModularArchitecture, F as installGuard, G as ASK_WRITERS, H as TS_DECL_RE, I as GO_DECL_RE, J as FILE_REDIRECT, K as CODE_MUTATORS, L as JAVA_DECL_RE, M as clearUserGuards, N as registerGuard, O as capVerbosity, Ot as isApexCommand, P as runGuards, Q as PROTECTED_GIT_RE, R as PHP_DECL_RE, S as skillTriggerGate, St as detectFramework, T as frameworkSolidGate, Tt as ProjectType, U as interfaceSeparationGuard, V as SWIFT_PROTO_RE, W as bashWriteGuard, X as SESSION_STATE_FRAGMENT, Y as SAFE_PREFIXES, Z as PROTECTED_FRAGMENTS, _ as DEV_VERBS, _t as PLUGINS_DIR, a as firstHeading, at as GuardContext, b as detectClaudeMdProjectType, bt as countLines, c as parseEntry, ct as PolicyResult, d as EXCLUDE_DIRS, dt as PROJECT_INSTALL, et as ASK_PATTERNS, f as PROJECT_INDICATORS, ft as RALPH_SAFE, g as loadApexTaskState, gt as FileSizeVerdict, h as buildApexTaskInjection, ht as matchPatterns, i as firstComment, it as Guard, j as GUARDS, k as detectCreationIntent, kt as requiredArchSkill, l as parseBodyDesc, lt as GIT_ASK, m as buildApexTaskContext, mt as isRalphMode, n as missingSeoElements, nt as LabeledPattern, o as TreeEntry, ot as evaluate, p as ApexTaskState, pt as SYSTEM_INSTALL, q as CODE_REDIRECT, r as descFromText, rt as securityGuard, s as parseEnrichment, st as PolicyContext, t as isHtmlLike, tt as CRITICAL_PATTERNS, u as parseField, ut as GIT_BLOCKED, v as buildApexInstruction, vt as SOLID_REF, w as SKILL_TRIGGERS, wt as ModularArchitecture, x as detectRequiredSkills, xt as evaluateFileSize, y as buildClaudeMdContext, yt as countFrameworkCodeLines, z as PY_MODEL_RE } from "../index-
|
|
1
|
+
import { $ as protectedPathGuard, A as FAIL_CLOSED, B as RUST_DECL_RE, C as usesTailwindUtilities, Ct as DEV_KEYWORDS, D as MAX_TOKENS, Dt as detectProjectType, E as MAX_EXA_RESULTS, Et as detectModularArchitecture, F as installGuard, G as ASK_WRITERS, H as TS_DECL_RE, I as GO_DECL_RE, J as FILE_REDIRECT, K as CODE_MUTATORS, L as JAVA_DECL_RE, M as clearUserGuards, N as registerGuard, O as capVerbosity, Ot as isApexCommand, P as runGuards, Q as PROTECTED_GIT_RE, R as PHP_DECL_RE, S as skillTriggerGate, St as detectFramework, T as frameworkSolidGate, Tt as ProjectType, U as interfaceSeparationGuard, V as SWIFT_PROTO_RE, W as bashWriteGuard, X as SESSION_STATE_FRAGMENT, Y as SAFE_PREFIXES, Z as PROTECTED_FRAGMENTS, _ as DEV_VERBS, _t as PLUGINS_DIR, a as firstHeading, at as GuardContext, b as detectClaudeMdProjectType, bt as countLines, c as parseEntry, ct as PolicyResult, d as EXCLUDE_DIRS, dt as PROJECT_INSTALL, et as ASK_PATTERNS, f as PROJECT_INDICATORS, ft as RALPH_SAFE, g as loadApexTaskState, gt as FileSizeVerdict, h as buildApexTaskInjection, ht as matchPatterns, i as firstComment, it as Guard, j as GUARDS, k as detectCreationIntent, kt as requiredArchSkill, l as parseBodyDesc, lt as GIT_ASK, m as buildApexTaskContext, mt as isRalphMode, n as missingSeoElements, nt as LabeledPattern, o as TreeEntry, ot as evaluate, p as ApexTaskState, pt as SYSTEM_INSTALL, q as CODE_REDIRECT, r as descFromText, rt as securityGuard, s as parseEnrichment, st as PolicyContext, t as isHtmlLike, tt as CRITICAL_PATTERNS, u as parseField, ut as GIT_BLOCKED, v as buildApexInstruction, vt as SOLID_REF, w as SKILL_TRIGGERS, wt as ModularArchitecture, x as detectRequiredSkills, xt as evaluateFileSize, y as buildClaudeMdContext, yt as countFrameworkCodeLines, z as PY_MODEL_RE } from "../index-BIGVNQB8.mjs";
|
|
2
2
|
import { a as PRE_AUTH_GATES, c as evaluateApex, i as POST_AUTH_GATES, l as freshnessGate, n as ApexContext, o as brainstormGate, r as ApexGate, s as docConsultedGate, t as APEX_GATES, u as solidReadGate } from "../apex-Wdi1nq_w.mjs";
|
|
3
3
|
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, ApexContext, ApexGate, ApexTaskState, CODE_MUTATORS, CODE_REDIRECT, CRITICAL_PATTERNS, DEV_KEYWORDS, DEV_VERBS, EXCLUDE_DIRS, FAIL_CLOSED, FILE_REDIRECT, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, GO_DECL_RE, GUARDS, Guard, GuardContext, JAVA_DECL_RE, LabeledPattern, MAX_EXA_RESULTS, MAX_TOKENS, ModularArchitecture, PHP_DECL_RE, PLUGINS_DIR, POST_AUTH_GATES, PRE_AUTH_GATES, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, type PolicyContext, type PolicyResult, ProjectType, RALPH_SAFE, RUST_DECL_RE, SAFE_PREFIXES, SESSION_STATE_FRAGMENT, SKILL_TRIGGERS, SOLID_REF, SWIFT_PROTO_RE, SYSTEM_INSTALL, TS_DECL_RE, TreeEntry, bashWriteGuard, brainstormGate, buildApexInstruction, buildApexTaskContext, buildApexTaskInjection, buildClaudeMdContext, capVerbosity, clearUserGuards, countFrameworkCodeLines, countLines, descFromText, detectClaudeMdProjectType, detectCreationIntent, detectFramework, detectModularArchitecture, detectProjectType, detectRequiredSkills, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, firstComment, firstHeading, frameworkSolidGate, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, isHtmlLike, isRalphMode, loadApexTaskState, matchPatterns, missingSeoElements, parseBodyDesc, parseEnrichment, parseEntry, parseField, protectedPathGuard, registerGuard, requiredArchSkill, runGuards, securityGuard, skillTriggerGate, solidReadGate, usesTailwindUtilities };
|
package/dist/policy/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { A as GIT_ASK, B as detectProjectType, C as SESSION_STATE_FRAGMENT, D as ASK_PATTERNS, E as protectedPathGuard, F as isRalphMode, H as requiredArchSkill, I as matchPatterns, L as detectFramework, M as PROJECT_INSTALL, N as RALPH_SAFE, O as CRITICAL_PATTERNS, P as SYSTEM_INSTALL, R as DEV_KEYWORDS, S as SAFE_PREFIXES, T as PROTECTED_GIT_RE, V as isApexCommand, _ as bashWriteGuard, a as registerGuard, b as CODE_REDIRECT, c as GO_DECL_RE, d as PY_MODEL_RE, f as RUST_DECL_RE, h as interfaceSeparationGuard, i as clearUserGuards, j as GIT_BLOCKED, k as securityGuard, l as JAVA_DECL_RE, m as TS_DECL_RE, n as FAIL_CLOSED, o as runGuards, p as SWIFT_PROTO_RE, r as GUARDS, s as installGuard, t as evaluate, u as PHP_DECL_RE, v as ASK_WRITERS, w as PROTECTED_FRAGMENTS, x as FILE_REDIRECT, y as CODE_MUTATORS, z as detectModularArchitecture } from "../evaluate-ClfbCDiY.mjs";
|
|
2
2
|
import { d as countFrameworkCodeLines, f as countLines, l as PLUGINS_DIR, p as evaluateFileSize, u as SOLID_REF } from "../home-state-BXf38Zi1.mjs";
|
|
3
|
-
import { A as
|
|
3
|
+
import { A as capVerbosity, C as usesTailwindUtilities, F as brainstormGate, I as docConsultedGate, L as evaluateApex, M as APEX_GATES, N as POST_AUTH_GATES, O as MAX_EXA_RESULTS, P as PRE_AUTH_GATES, R as freshnessGate, S as skillTriggerGate, T as frameworkSolidGate, a as firstHeading, b as parseField, c as EXCLUDE_DIRS, d as buildApexTaskInjection, f as loadApexTaskState, g as detectClaudeMdProjectType, h as buildClaudeMdContext, i as firstComment, j as detectCreationIntent, k as MAX_TOKENS, l as PROJECT_INDICATORS, m as buildApexInstruction, n as missingSeoElements, o as parseEnrichment, p as DEV_VERBS, r as descFromText, s as parseEntry, t as isHtmlLike, u as buildApexTaskContext, w as SKILL_TRIGGERS, x as detectRequiredSkills, y as parseBodyDesc, z as solidReadGate } from "../validate-DhOX5hDK.mjs";
|
|
4
4
|
import "../policy-la_KkjCS.mjs";
|
|
5
5
|
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, CRITICAL_PATTERNS, DEV_KEYWORDS, DEV_VERBS, EXCLUDE_DIRS, FAIL_CLOSED, FILE_REDIRECT, GIT_ASK, GIT_BLOCKED, GO_DECL_RE, GUARDS, JAVA_DECL_RE, MAX_EXA_RESULTS, MAX_TOKENS, PHP_DECL_RE, PLUGINS_DIR, POST_AUTH_GATES, PRE_AUTH_GATES, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, RALPH_SAFE, RUST_DECL_RE, SAFE_PREFIXES, SESSION_STATE_FRAGMENT, SKILL_TRIGGERS, SOLID_REF, SWIFT_PROTO_RE, SYSTEM_INSTALL, TS_DECL_RE, bashWriteGuard, brainstormGate, buildApexInstruction, buildApexTaskContext, buildApexTaskInjection, buildClaudeMdContext, capVerbosity, clearUserGuards, countFrameworkCodeLines, countLines, descFromText, detectClaudeMdProjectType, detectCreationIntent, detectFramework, detectModularArchitecture, detectProjectType, detectRequiredSkills, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, firstComment, firstHeading, frameworkSolidGate, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, isHtmlLike, isRalphMode, loadApexTaskState, matchPatterns, missingSeoElements, parseBodyDesc, parseEnrichment, parseEntry, parseField, protectedPathGuard, registerGuard, requiredArchSkill, runGuards, securityGuard, skillTriggerGate, solidReadGate, usesTailwindUtilities };
|
package/dist/runtime/index.d.mts
CHANGED
|
@@ -219,8 +219,15 @@ declare function normalizeEvent(id: string, payload: Record<string, unknown>): N
|
|
|
219
219
|
* - cline: NO user-visible channel (`cancel`/`contextModification`/
|
|
220
220
|
* `errorMessage`-on-cancel only) — the notice is dropped silently (a pure
|
|
221
221
|
* notice returns "" = plain allow).
|
|
222
|
+
* @param id - Harness id.
|
|
223
|
+
* @param prompt - The portable prompt to render.
|
|
224
|
+
* @param event - The firing hook event name for the claude-code/codex branch
|
|
225
|
+
* (defaults to `"PreToolUse"`, the only phase every existing caller but
|
|
226
|
+
* `handle-post.ts` renders) — a POST-phase caller MUST pass `"PostToolUse"`
|
|
227
|
+
* (or its real raw event name) so `hookEventName` matches the event that
|
|
228
|
+
* actually fired instead of a hardcoded, wrong `"PreToolUse"`.
|
|
222
229
|
*/
|
|
223
|
-
declare function respond(id: string, prompt: Prompt): string;
|
|
230
|
+
declare function respond(id: string, prompt: Prompt, event?: string): string;
|
|
224
231
|
//#endregion
|
|
225
232
|
//#region src/runtime/mcp-key.d.ts
|
|
226
233
|
/** Cached-MCP freshness (ms) from `FUSE_MCP_TTL_SEC` (default 48h). */
|
|
@@ -280,18 +287,20 @@ declare function claudeMdKey(prompt: string, ctx: string): string;
|
|
|
280
287
|
* is emitted on EVERY message" is thus preserved.
|
|
281
288
|
* @param prompt - The raw user prompt.
|
|
282
289
|
* @param cwd - Project root (for project-type detection).
|
|
290
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
283
291
|
* @returns The native hook stdout (possibly empty).
|
|
284
292
|
*/
|
|
285
|
-
declare function promptSubmitContext(prompt: string, cwd: string): string;
|
|
293
|
+
declare function promptSubmitContext(prompt: string, cwd: string, id?: string): string;
|
|
286
294
|
/**
|
|
287
295
|
* PreToolUse Task context injection: render the APEX sub-agent context as a
|
|
288
296
|
* Claude `additionalContext` response when `.claude/apex/` exists, else "".
|
|
289
297
|
* Harness-produced (not owner CLAUDE.md content), so it is subject to the
|
|
290
298
|
* per-fragment {@link capFragment} budget — unlike {@link promptSubmitContext}.
|
|
291
299
|
* @param cwd - Fallback project root when `CLAUDE_PROJECT_DIR` is unset.
|
|
300
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
292
301
|
* @returns The native hook stdout (possibly empty).
|
|
293
302
|
*/
|
|
294
|
-
declare function taskContext(cwd: string): string;
|
|
303
|
+
declare function taskContext(cwd: string, id?: string): string;
|
|
295
304
|
//#endregion
|
|
296
305
|
//#region src/runtime/home-state.d.ts
|
|
297
306
|
/** Home `~/.claude` dir — per-harness config (CLAUDE.md, logs, plugins). */
|
|
@@ -442,11 +451,13 @@ declare function trackAgentMemory(data: Record<string, unknown>, home?: string,
|
|
|
442
451
|
//#region src/runtime/lifecycle/teammate-idle.d.ts
|
|
443
452
|
/**
|
|
444
453
|
* Handle TeammateIdle: when the teammate's session-changes file shows code was
|
|
445
|
-
* modified, suggest sniper validation as
|
|
446
|
-
* `
|
|
454
|
+
* modified, suggest sniper validation as plain text — TeammateIdle has no
|
|
455
|
+
* `additionalContext` channel (rejects `hookSpecificOutput`), so the caller
|
|
456
|
+
* ({@link module:teammate-idle-check.teammateIdleContext}) rides this text on
|
|
457
|
+
* the `systemMessage` channel instead. Ports `teammate-idle/validate-teammate-output.py`.
|
|
447
458
|
* @param data - The raw hook payload.
|
|
448
459
|
* @param home - Home dir (defaults to `~`).
|
|
449
|
-
* @returns The
|
|
460
|
+
* @returns The plain notice text, or "" when no code file was modified.
|
|
450
461
|
*/
|
|
451
462
|
declare function validateTeammateOutput(data: Record<string, unknown>, home?: string): string;
|
|
452
463
|
//#endregion
|
|
@@ -463,13 +474,15 @@ declare function logToolFailure(data: Record<string, unknown>, home?: string, no
|
|
|
463
474
|
//#endregion
|
|
464
475
|
//#region src/runtime/lifecycle/pre-compact.d.ts
|
|
465
476
|
/**
|
|
466
|
-
* Handle PreCompact: back up
|
|
467
|
-
* the 5 newest, and emit a
|
|
477
|
+
* Handle PreCompact: back up the target apex `task.json` (`.claude/apex/`,
|
|
478
|
+
* `.codex/apex/`, ...) to `backups/`, keep only the 5 newest, and emit a
|
|
479
|
+
* confirmation. Ports `pre-compact/save-apex-state.py`.
|
|
468
480
|
* @param cwd - Project root.
|
|
469
481
|
* @param now - Clock (defaults to `Date.now()`).
|
|
482
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
470
483
|
* @returns The native hook stdout (possibly empty when no task.json).
|
|
471
484
|
*/
|
|
472
|
-
declare function saveApexState(cwd: string, now?: number): string;
|
|
485
|
+
declare function saveApexState(cwd: string, now?: number, id?: string): string;
|
|
473
486
|
//#endregion
|
|
474
487
|
//#region src/runtime/lifecycle/session-end.d.ts
|
|
475
488
|
/**
|
|
@@ -517,11 +530,11 @@ declare function postEditTypescript(filePath: string): string;
|
|
|
517
530
|
/**
|
|
518
531
|
* Dispatch an ai-pilot-scope lifecycle event. Returns the native stdout, or
|
|
519
532
|
* `null` when unhandled (caller falls through to the default pipeline).
|
|
520
|
-
* @param home - Home dir for cache resolution (defaults to `~`; injectable for test isolation).
|
|
533
|
+
* @param home - Home dir for cache resolution (defaults to `~`; injectable for test isolation); `id` selects the harness target (defaults to "claude-code").
|
|
521
534
|
*/
|
|
522
|
-
declare function dispatchAipilot(event: string, payload: Record<string, unknown>, cwd: string, now: number, home?: string): Promise<string | null>;
|
|
523
|
-
/** PostToolUse (Write/Edit SOLID check, else TaskCreate/TaskUpdate sync) for the ai-pilot scope. */
|
|
524
|
-
declare function aipilotPostToolUse(payload: Record<string, unknown>, cwd: string): Promise<string>;
|
|
535
|
+
declare function dispatchAipilot(event: string, payload: Record<string, unknown>, cwd: string, now: number, home?: string, id?: string): Promise<string | null>;
|
|
536
|
+
/** PostToolUse (Write/Edit SOLID check, else TaskCreate/TaskUpdate sync) for the ai-pilot scope; `id` selects the harness target (defaults to "claude-code"). */
|
|
537
|
+
declare function aipilotPostToolUse(payload: Record<string, unknown>, cwd: string, id?: string): Promise<string>;
|
|
525
538
|
//#endregion
|
|
526
539
|
//#region src/runtime/lifecycle/dispatch.d.ts
|
|
527
540
|
/** Which plugin's hooks.json invoked the harness (selects SessionStart behavior). */
|
|
@@ -533,6 +546,8 @@ interface LifecycleInput {
|
|
|
533
546
|
cwd: string;
|
|
534
547
|
scope: PluginScope;
|
|
535
548
|
now: number;
|
|
549
|
+
/** Harness target id (defaults to "claude-code" — zero-regression default). */
|
|
550
|
+
id?: string;
|
|
536
551
|
}
|
|
537
552
|
/**
|
|
538
553
|
* Route a lifecycle/session/context hook event to its ported handler. Returns
|
|
@@ -719,8 +734,8 @@ declare function saveSecurityState(state: Record<string, unknown>, now?: number,
|
|
|
719
734
|
//#region src/runtime/lifecycle/lessons/dispatch.d.ts
|
|
720
735
|
/**
|
|
721
736
|
* Route a fuse-lessons event to its handler. Returns the native stdout for
|
|
722
|
-
* context-injecting events (SessionStart/SubagentStart/
|
|
723
|
-
* side-effect-only PostToolUse mark.
|
|
737
|
+
* context-injecting events (SessionStart/SubagentStart/UserPromptSubmit inject
|
|
738
|
+
* MEMORY/LESSON.md, Stop reminds) or "" for the side-effect-only PostToolUse mark.
|
|
724
739
|
* @param event - The raw hook event name.
|
|
725
740
|
* @param payload - The raw hook payload.
|
|
726
741
|
* @param cwd - Project root for memory injection.
|
|
@@ -806,9 +821,10 @@ declare function writePluginMap(outputDir: string, pluginName: string, version:
|
|
|
806
821
|
* @param cwd - Project root.
|
|
807
822
|
* @param scope - The invoking plugin scope (defaults to `core`).
|
|
808
823
|
* @param now - Clock.
|
|
824
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
809
825
|
* @returns The native stdout, or `null` when unhandled.
|
|
810
826
|
*/
|
|
811
|
-
declare function lifecycleStdout(payload: Record<string, unknown>, cwd: string, scope: PluginScope, now: number): string | null;
|
|
827
|
+
declare function lifecycleStdout(payload: Record<string, unknown>, cwd: string, scope: PluginScope, now: number, id?: string): string | null;
|
|
812
828
|
/**
|
|
813
829
|
* Post-edit additions for core-scope PostToolUse: auto-document a Read of a
|
|
814
830
|
* SKILL.md/README/docs file; else (Write/Edit) track cumulative session
|
|
@@ -819,9 +835,10 @@ declare function lifecycleStdout(payload: Record<string, unknown>, cwd: string,
|
|
|
819
835
|
* @param scope - The invoking plugin scope.
|
|
820
836
|
* @param event - The normalized event.
|
|
821
837
|
* @param now - Clock.
|
|
838
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
822
839
|
* @returns The extra stdout (possibly empty).
|
|
823
840
|
*/
|
|
824
|
-
declare function postEditContext(scope: PluginScope, event: NormalizedEvent, now: number): Promise<string>;
|
|
841
|
+
declare function postEditContext(scope: PluginScope, event: NormalizedEvent, now: number, id?: string): Promise<string>;
|
|
825
842
|
//#endregion
|
|
826
843
|
//#region src/runtime/handle-types.d.ts
|
|
827
844
|
/** Options for {@link handleHook} (caller supplies the clock + project root). */
|
package/dist/runtime/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { r as projectLayout } from "../layout-C0jaaCQC.mjs";
|
|
2
2
|
import { a as sanitizeSessionId, c as sessionsDir, i as loadSessionState, n as fuseHarnessHome, o as saveSessionState, r as fusengineCache, s as sessionStatePath, t as claudeHome } from "../home-state-BXf38Zi1.mjs";
|
|
3
|
-
import { A as trackMcpResearch, At as defaultStateDir, B as generateProjectMap, C as seoPostToolUse, Ct as trimLogFile, D as securityAdvisoryForPatch, Dt as claudeMdKey, E as securityAdvisory, Et as projectContext, F as dispatchAipilot, Ft as loadSecurityState, G as countFiles, H as writeTree, I as dispatchLessons, It as saveSecurityState, J as lessonsArchiveFileFor, K as getFileDesc, L as cartoSessionStart, Lt as securityStateDir, M as trackEnrichment, Mt as trackFile, N as dispatchLifecycle, Nt as normalizeEvent, O as postTrackingSideEffects, Ot as promptSubmitContext, P as aipilotPostToolUse, Pt as isoUtc, R as generateEcosystemMap, Rt as securityStatePath, S as postEditContext, St as removeOldFiles, T as dispatchMemory, Tt as gitContext, U as loadEnriched, V as isProject, W as mergeLines, X as lessonsStateFileFor, Y as lessonsFileFor, _ as preCommitGate, _t as readRules, a as recordActivity, at as saveApexState, b as extractSymbols, bt as pruneEmptyDirs, c as MCP_TTL_MS, ct as trackAgentMemory, d as isMcpTool, dt as validateSolidGate, f as queryOf, ft as checkFileSize, g as gate, gt as injectRules, h as TRIVIAL_BUDGET, ht as solidDetectStart, i as respond, it as cleanupSession, j as trackSkillRead, jt as projectHash, k as trackWatchResearch, kt as taskContext, l as WEBFETCH_TTL_MS, lt as subagentCacheContext, m as REQUIRED_AGENTS, mt as detectSolidProfile, n as activityFor, nt as trackSessionChanges, o as mcpPostStore, ot as logToolFailure, p as DEFAULT_WINDOW_MS, pt as countLoc, q as listChildren, r as handlePre, rt as validateRulesLoaded, s as mcpPreIntercept, st as validateTeammateOutput, t as handleHook, tt as postEditTypescript, u as cacheQueryOf, ut as validateTailwind, v as detectDuplication, vt as runSessionStartCleanups, w as seoPostToolUseResponse, wt as devContext, x as lifecycleStdout, xt as purgeTtlTree, y as dryGate, yt as sessionStartCore, z as writePluginMap, zt as todayUtc } from "../handle-
|
|
3
|
+
import { A as trackMcpResearch, At as defaultStateDir, B as generateProjectMap, C as seoPostToolUse, Ct as trimLogFile, D as securityAdvisoryForPatch, Dt as claudeMdKey, E as securityAdvisory, Et as projectContext, F as dispatchAipilot, Ft as loadSecurityState, G as countFiles, H as writeTree, I as dispatchLessons, It as saveSecurityState, J as lessonsArchiveFileFor, K as getFileDesc, L as cartoSessionStart, Lt as securityStateDir, M as trackEnrichment, Mt as trackFile, N as dispatchLifecycle, Nt as normalizeEvent, O as postTrackingSideEffects, Ot as promptSubmitContext, P as aipilotPostToolUse, Pt as isoUtc, R as generateEcosystemMap, Rt as securityStatePath, S as postEditContext, St as removeOldFiles, T as dispatchMemory, Tt as gitContext, U as loadEnriched, V as isProject, W as mergeLines, X as lessonsStateFileFor, Y as lessonsFileFor, _ as preCommitGate, _t as readRules, a as recordActivity, at as saveApexState, b as extractSymbols, bt as pruneEmptyDirs, c as MCP_TTL_MS, ct as trackAgentMemory, d as isMcpTool, dt as validateSolidGate, f as queryOf, ft as checkFileSize, g as gate, gt as injectRules, h as TRIVIAL_BUDGET, ht as solidDetectStart, i as respond, it as cleanupSession, j as trackSkillRead, jt as projectHash, k as trackWatchResearch, kt as taskContext, l as WEBFETCH_TTL_MS, lt as subagentCacheContext, m as REQUIRED_AGENTS, mt as detectSolidProfile, n as activityFor, nt as trackSessionChanges, o as mcpPostStore, ot as logToolFailure, p as DEFAULT_WINDOW_MS, pt as countLoc, q as listChildren, r as handlePre, rt as validateRulesLoaded, s as mcpPreIntercept, st as validateTeammateOutput, t as handleHook, tt as postEditTypescript, u as cacheQueryOf, ut as validateTailwind, v as detectDuplication, vt as runSessionStartCleanups, w as seoPostToolUseResponse, wt as devContext, x as lifecycleStdout, xt as purgeTtlTree, y as dryGate, yt as sessionStartCore, z as writePluginMap, zt as todayUtc } from "../handle-Dthtlp7Z.mjs";
|
|
4
4
|
//#region src/runtime/storage.ts
|
|
5
5
|
/**
|
|
6
6
|
* The project's single state dir (`<root>/.harness`) — neutral + harness-agnostic,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as resolveMaxLines } from "./limits-CHn8AIL1.mjs";
|
|
2
|
-
import { c as ttlLabel } from "./dotenv-Jj8aL1FL.mjs";
|
|
2
|
+
import { c as ttlLabel, t as HOME_DIR } from "./dotenv-Jj8aL1FL.mjs";
|
|
3
3
|
import { R as DEV_KEYWORDS, g as moduleAwarePath } from "./evaluate-ClfbCDiY.mjs";
|
|
4
4
|
import { d as countFrameworkCodeLines, l as PLUGINS_DIR, u as SOLID_REF } from "./home-state-BXf38Zi1.mjs";
|
|
5
5
|
import { r as isDocConsulted, t as formatDocDeny } from "./doc-helpers-CWZegVdR.mjs";
|
|
@@ -1187,9 +1187,48 @@ function getExpertAgent(type, pluginsDirOverride) {
|
|
|
1187
1187
|
return "general-purpose";
|
|
1188
1188
|
}
|
|
1189
1189
|
//#endregion
|
|
1190
|
-
//#region src/policy/
|
|
1191
|
-
/**
|
|
1192
|
-
|
|
1190
|
+
//#region src/policy/apex-target.ts
|
|
1191
|
+
/**
|
|
1192
|
+
* Harness-target helpers for the APEX context builders. Single source of
|
|
1193
|
+
* truth for the home-dir segment / root doc name / plan-tool idiom that
|
|
1194
|
+
* differ between Claude Code and Codex (and beyond), so the builders in
|
|
1195
|
+
* `claude-md-context.ts`, `apex-task-context.ts`, and `inject-apex.ts` never
|
|
1196
|
+
* hardcode `.claude`/`CLAUDE.md`/`TaskCreate` again. Runtime `id` values flow
|
|
1197
|
+
* through the pipeline as plain `string` (see `handleHook(id: string, ...)`),
|
|
1198
|
+
* so these helpers accept `string` and narrow internally — same idiom as the
|
|
1199
|
+
* existing `HOME_DIR[id] ?? ".claude"` precedent in `cartographer/detect.ts`.
|
|
1200
|
+
*/
|
|
1201
|
+
/**
|
|
1202
|
+
* Home-dir segment for a harness target (".claude", ".codex", ...). Falls
|
|
1203
|
+
* back to ".claude" for any harness without a {@link HOME_DIR} entry.
|
|
1204
|
+
* @param id - Harness id (e.g. "claude-code", "codex").
|
|
1205
|
+
* @returns The home-dir segment, e.g. ".claude".
|
|
1206
|
+
*/
|
|
1207
|
+
function harnessHomeSegment(id) {
|
|
1208
|
+
return HOME_DIR[id] ?? ".claude";
|
|
1209
|
+
}
|
|
1210
|
+
/**
|
|
1211
|
+
* Root instructions doc name for a harness target: "AGENTS.md" for Codex,
|
|
1212
|
+
* "CLAUDE.md" for every other target (including the default/unknown fallback).
|
|
1213
|
+
* @param id - Harness id.
|
|
1214
|
+
* @returns The doc file name.
|
|
1215
|
+
*/
|
|
1216
|
+
function apexDocName(id) {
|
|
1217
|
+
return id === "codex" ? "AGENTS.md" : "CLAUDE.md";
|
|
1218
|
+
}
|
|
1219
|
+
/**
|
|
1220
|
+
* Native plan/track tool name for a harness target: Codex's `update_plan` vs
|
|
1221
|
+
* Claude's `TaskCreate`. Covers only the PLAN-step line — other Claude-only
|
|
1222
|
+
* tool references (`TaskList`, `TaskUpdate`) are branched inline by the
|
|
1223
|
+
* builders since their surrounding prose differs structurally, not just by token.
|
|
1224
|
+
* @param id - Harness id.
|
|
1225
|
+
* @returns The plan-tool name.
|
|
1226
|
+
*/
|
|
1227
|
+
function apexPlanTool(id) {
|
|
1228
|
+
return id === "codex" ? "update_plan" : "TaskCreate";
|
|
1229
|
+
}
|
|
1230
|
+
//#endregion
|
|
1231
|
+
//#region src/policy/detect-claude-md-project-type.ts
|
|
1193
1232
|
/**
|
|
1194
1233
|
* Detect the project type from the cwd, reproducing the legacy Python logic:
|
|
1195
1234
|
* package.json containing "next" → nextjs, else "react" → react; else
|
|
@@ -1212,31 +1251,35 @@ function detectClaudeMdProjectType(cwd) {
|
|
|
1212
1251
|
} catch {}
|
|
1213
1252
|
return "generic";
|
|
1214
1253
|
}
|
|
1254
|
+
//#endregion
|
|
1255
|
+
//#region src/policy/claude-md-context.ts
|
|
1256
|
+
/** Dev-verb regex (FR/EN) that triggers the APEX preamble (case-insensitive). */
|
|
1257
|
+
const DEV_VERBS = /(cr[ée]er|impl[ée]menter|ajouter|d[ée]velopper|construire|build|refactor|migrer|implement|create|add|develop)/i;
|
|
1215
1258
|
/**
|
|
1216
1259
|
* Build the APEX instruction preamble for a development task.
|
|
1217
1260
|
* @param projectType - Detected project type label.
|
|
1218
1261
|
* @param maxLines - SOLID per-file line ceiling.
|
|
1262
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
1219
1263
|
* @returns The APEX instruction text.
|
|
1220
1264
|
*/
|
|
1221
|
-
function buildApexInstruction(projectType, maxLines) {
|
|
1265
|
+
function buildApexInstruction(projectType, maxLines, id = "claude-code") {
|
|
1222
1266
|
const expertAgent = getExpertAgent(projectType);
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
**TRACKING FILE**: [project]/.claude/apex/task.json — create it yourself via apex-methodology Step 0 (init-tracking) if missing
|
|
1226
|
-
|
|
1227
|
-
1. **ANALYZE** (MANDATORY - 3 AGENTS IN PARALLEL):
|
|
1228
|
-
- explore-codebase + research-expert + ${expertAgent} (framework expertise)\n - Project type detected: ${projectType}\n\n2. **PLAN**: Use TaskCreate to break down tasks (<${maxLines} lines per file)\n\n3. **EXECUTE**: ${expertAgent}, follow SOLID principles, split at ${maxLines - 10} lines\n\n4. **eLICIT**: self-review with NAMED elicitation techniques (apex ref 03.5-elicit) — fix findings BEFORE validation\n\n5. **VERIFY**: functional check — run it, confirm references⇔declarations consistency\n\n6. **eXAMINE**: Run sniper agent after ANY modification\n\n**GATE**: eLicit + Verify BEFORE sniper — NEVER skip.\n\n**IMPORTANT**: Read .claude/apex/task.json to check documentation status before writing code.`;
|
|
1267
|
+
const seg = harnessHomeSegment(id);
|
|
1268
|
+
return `INSTRUCTION: This is a development task. Use APEX methodology:\n\n**TRACKING FILE**: [project]/${seg}/apex/task.json — create it yourself via apex-methodology Step 0 (init-tracking) if missing\n\n1. **ANALYZE** (MANDATORY - 3 AGENTS IN PARALLEL):\n - explore-codebase + research-expert + ${expertAgent} (framework expertise)\n - Project type detected: ${projectType}\n\n2. **PLAN**: Use ${apexPlanTool(id)} to break down tasks (<${maxLines} lines per file)\n\n3. **EXECUTE**: ${expertAgent}, follow SOLID principles, split at ${maxLines - 10} lines\n\n4. **eLICIT**: self-review with NAMED elicitation techniques (apex ref 03.5-elicit) — fix findings BEFORE validation\n\n5. **VERIFY**: functional check — run it, confirm references⇔declarations consistency\n\n6. **eXAMINE**: Run sniper agent after ANY modification\n\n**GATE**: eLicit + Verify BEFORE sniper — NEVER skip.\n\n**IMPORTANT**: Read ${seg}/apex/task.json to check documentation status before writing code.`;
|
|
1229
1269
|
}
|
|
1230
1270
|
/**
|
|
1231
|
-
* Build the UserPromptSubmit injection text: read
|
|
1271
|
+
* Build the UserPromptSubmit injection text: read the target's root
|
|
1272
|
+
* instructions doc (`~/.claude/CLAUDE.md`, `~/.codex/AGENTS.md`, ...) and,
|
|
1232
1273
|
* when the prompt matches a dev verb, prepend the APEX instruction. Returns
|
|
1233
|
-
* `null` when
|
|
1274
|
+
* `null` when the doc is absent/unreadable (the hook then emits nothing).
|
|
1234
1275
|
* @param prompt - The raw user prompt.
|
|
1235
1276
|
* @param cwd - Project root (for project-type detection).
|
|
1277
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
1236
1278
|
* @returns The injection text, or `null` to emit nothing.
|
|
1237
1279
|
*/
|
|
1238
|
-
function buildClaudeMdContext(prompt, cwd) {
|
|
1239
|
-
const
|
|
1280
|
+
function buildClaudeMdContext(prompt, cwd, id = "claude-code") {
|
|
1281
|
+
const docName = apexDocName(id);
|
|
1282
|
+
const claudeMd = join(homedir(), harnessHomeSegment(id), docName);
|
|
1240
1283
|
if (!existsSync(claudeMd)) return null;
|
|
1241
1284
|
let claudeContent;
|
|
1242
1285
|
try {
|
|
@@ -1244,8 +1287,8 @@ function buildClaudeMdContext(prompt, cwd) {
|
|
|
1244
1287
|
} catch {
|
|
1245
1288
|
return null;
|
|
1246
1289
|
}
|
|
1247
|
-
if (!DEV_VERBS.test(prompt) && !DEV_KEYWORDS.test(prompt)) return `#
|
|
1248
|
-
return `${buildApexInstruction(detectClaudeMdProjectType(cwd), resolveMaxLines())}\n\n#
|
|
1290
|
+
if (!DEV_VERBS.test(prompt) && !DEV_KEYWORDS.test(prompt)) return `# ${docName}\n${claudeContent}`;
|
|
1291
|
+
return `${buildApexInstruction(detectClaudeMdProjectType(cwd), resolveMaxLines(), id)}\n\n# ${docName}\n${claudeContent}`;
|
|
1249
1292
|
}
|
|
1250
1293
|
//#endregion
|
|
1251
1294
|
//#region src/policy/apex-task-context.ts
|
|
@@ -1282,21 +1325,29 @@ function loadApexTaskState(taskFile) {
|
|
|
1282
1325
|
* Build the APEX context string injected into a Task sub-agent prompt.
|
|
1283
1326
|
* @param state - The parsed task state.
|
|
1284
1327
|
* @param maxLines - SOLID per-file line ceiling.
|
|
1328
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
1285
1329
|
* @returns The injection text.
|
|
1286
1330
|
*/
|
|
1287
|
-
function buildApexTaskContext(state, maxLines) {
|
|
1288
|
-
|
|
1331
|
+
function buildApexTaskContext(state, maxLines, id = "claude-code") {
|
|
1332
|
+
const seg = harnessHomeSegment(id);
|
|
1333
|
+
const isCodex = id === "codex";
|
|
1334
|
+
const step3 = isCodex ? "update_plan → review the current plan" : "TaskList → see pending tasks";
|
|
1335
|
+
const step4 = isCodex ? "update_plan → mark the active step in_progress before starting" : "TaskUpdate(in_progress) → before starting";
|
|
1336
|
+
const step7 = isCodex ? "update_plan → mark the step completed when done" : "TaskUpdate(completed) → triggers auto-commit";
|
|
1337
|
+
return `⚠️ APEX MODE - Read ${seg}/apex/AGENTS.md for rules\n\nCurrent: Task #${state.id} - ${state.subject} (Phase: ${state.phase})\nDocs consulted: ${state.docs}\n\nAgent must:\n1. Read task.json → find last 3 completed tasks\n2. Read their notes in docs/ (task-{ID}-{subject}.md)\n3. ${step3}\n4. ${step4}\n5. Apply SOLID (files < ${maxLines} lines)\n6. Write notes to docs/task-{ID}-{subject}.md\n7. ${step7}`;
|
|
1289
1338
|
}
|
|
1290
1339
|
/**
|
|
1291
|
-
* Build the PreToolUse Task injection, gated on the existence of the
|
|
1292
|
-
* `.claude/apex
|
|
1340
|
+
* Build the PreToolUse Task injection, gated on the existence of the
|
|
1341
|
+
* project's target apex dir (`.claude/apex/`, `.codex/apex/`, ...). Returns
|
|
1342
|
+
* `null` when APEX is not active (no dir).
|
|
1293
1343
|
* @param projectRoot - `CLAUDE_PROJECT_DIR` or cwd.
|
|
1344
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
1294
1345
|
* @returns The injection text, or `null` to emit nothing.
|
|
1295
1346
|
*/
|
|
1296
|
-
function buildApexTaskInjection(projectRoot) {
|
|
1297
|
-
const apexDir = join(projectRoot,
|
|
1347
|
+
function buildApexTaskInjection(projectRoot, id = "claude-code") {
|
|
1348
|
+
const apexDir = join(projectRoot, harnessHomeSegment(id), "apex");
|
|
1298
1349
|
if (!existsSync(apexDir)) return null;
|
|
1299
|
-
return buildApexTaskContext(loadApexTaskState(join(apexDir, "task.json")), resolveMaxLines());
|
|
1350
|
+
return buildApexTaskContext(loadApexTaskState(join(apexDir, "task.json")), resolveMaxLines(), id);
|
|
1300
1351
|
}
|
|
1301
1352
|
//#endregion
|
|
1302
1353
|
//#region src/policy/cartographer/indicators.ts
|
|
@@ -1540,4 +1591,4 @@ function missingSeoElements(html) {
|
|
|
1540
1591
|
return missing;
|
|
1541
1592
|
}
|
|
1542
1593
|
//#endregion
|
|
1543
|
-
export {
|
|
1594
|
+
export { capVerbosity as A, usesTailwindUtilities as C, isExcludedSwiftPath as D, isExcludedJsPath as E, brainstormGate as F, docConsultedGate as I, evaluateApex as L, APEX_GATES as M, POST_AUTH_GATES as N, MAX_EXA_RESULTS as O, PRE_AUTH_GATES as P, freshnessGate as R, skillTriggerGate as S, frameworkSolidGate as T, harnessHomeSegment as _, firstHeading as a, parseField as b, EXCLUDE_DIRS as c, buildApexTaskInjection as d, loadApexTaskState as f, detectClaudeMdProjectType as g, buildClaudeMdContext as h, firstComment as i, detectCreationIntent as j, MAX_TOKENS as k, PROJECT_INDICATORS as l, buildApexInstruction as m, missingSeoElements as n, parseEnrichment as o, DEV_VERBS as p, descFromText as r, parseEntry as s, isHtmlLike as t, buildApexTaskContext as u, scanPlugin as v, SKILL_TRIGGERS as w, detectRequiredSkills as x, parseBodyDesc as y, solidReadGate as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fusengine/harness",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.78",
|
|
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",
|