@fusengine/harness 0.1.46 → 0.1.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/adapters/claude/index.mjs +1 -1
- package/dist/adapters/cline/index.mjs +1 -1
- package/dist/adapters/codex/index.mjs +1 -1
- package/dist/adapters/cursor/index.mjs +1 -1
- package/dist/adapters/gemini/index.mjs +1 -1
- package/dist/{claude-DLh0fHWM.mjs → claude-U1KTaRG4.mjs} +1 -1
- package/dist/cli/bin.mjs +2 -2
- package/dist/cli/index.mjs +1 -1
- package/dist/{evaluate-d7Pp8XJH.mjs → evaluate-qYC5xClb.mjs} +52 -13
- package/dist/{handle-vK4kMxfE.mjs → handle-CCV0ycdr.mjs} +178 -40
- package/dist/{index-CVhw7eA0.d.mts → index-BqfOFKm7.d.mts} +36 -5
- package/dist/index.d.mts +2 -3
- package/dist/index.mjs +3 -4
- package/dist/policy/index.d.mts +2 -2
- package/dist/policy/index.mjs +3 -3
- package/dist/{run-DLXtA5DH.mjs → run-CaqeQFYp.mjs} +1 -1
- package/dist/runtime/index.d.mts +4 -1
- package/dist/runtime/index.mjs +1 -1
- package/dist/{session-state-D4F_Dub6.d.mts → session-state-V8Jp-KE5.d.mts} +18 -2
- package/dist/{store-CNjFenWe.mjs → store-ARx-GQSQ.mjs} +20 -4
- package/dist/tracking/index.d.mts +1 -1
- package/dist/tracking/index.mjs +1 -1
- package/dist/{validate-CjFtMZz0.mjs → validate-ebDjIyOa.mjs} +30 -9
- package/package.json +2 -7
- package/dist/index-BA-SqNR7.d.mts +0 -59
- package/dist/state/index.d.mts +0 -2
- package/dist/state/index.mjs +0 -2
- package/dist/state-9lWvvWk8.mjs +0 -104
package/README.md
CHANGED
|
@@ -116,6 +116,8 @@ Ten portable guards + the APEX gate chain, all evaluated before a tool runs:
|
|
|
116
116
|
| APEX freshness | `explore-codebase` + `research-expert` not run within the window |
|
|
117
117
|
| APEX doc-consulted | no doc source (Context7 / Exa / fuse-browser / WebSearch / WebFetch) consulted this session |
|
|
118
118
|
| APEX solid-read | required SOLID refs (auto-discovered, or `FUSE_HARNESS_REFS`) not read |
|
|
119
|
+
| framework sub-skill | framework / shadcn / Tailwind code whose required skill (`skills/<name>/`) wasn't read this session |
|
|
120
|
+
| Gemini MCP (opt-in) | hand-written Tailwind UI without a `mcp__gemini-design__*` call — only when `FUSE_ENFORCE_GEMINI_MCP` is set |
|
|
119
121
|
| brainstorm | creating a new file without brainstorming (when flagged) |
|
|
120
122
|
| MCP verbosity / cache | caps exa `numResults`; serves a fresh cached MCP/WebFetch result |
|
|
121
123
|
|
|
@@ -131,6 +133,8 @@ through per window without the full APEX gates.
|
|
|
131
133
|
| `FUSE_HARNESS_MARKETPLACES` | Comma-list of marketplace names whose `solid-*` skill refs are auto-discovered when `FUSE_HARNESS_REFS` is unset (default `fusengine-plugins`; an absent marketplace contributes nothing). Standalone `.claude`/`.codex`/`.cursor`/`.agents` skills are always scanned. |
|
|
132
134
|
| `FUSE_ENFORCE_TTL_SEC` | APEX freshness window in seconds. |
|
|
133
135
|
| `FUSE_LESSONS_THROTTLE_MIN` | Lessons-injection throttle (memory module). |
|
|
136
|
+
| `FUSE_ENFORCE_GEMINI_MCP` | **Opt-in (default off).** `1`/`true` blocks hand-written Tailwind UI (`.tsx/.jsx/.vue/.svelte`, ≥3 utility classes) until a `mcp__gemini-design__*` call is made this session. Read fresh per call. |
|
|
137
|
+
| `RALPH_MODE` | **Opt-in (default off).** `1`/`true` enables autonomous "Ralph" mode: exempts the safe git commands (`add`/`commit`/`checkout -b`/`status`/`diff`/`log`) from the confirmation ASK and auto-approves project installs. Destructive git (force-push, `reset --hard`) and system installs still gate. |
|
|
134
138
|
|
|
135
139
|
## Library usage
|
|
136
140
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as guard, c as toClaudeResponse, i as fileSizeGuard, n as contextResponse, o as readClaudeInput, r as denyResponse, s as systemMessage, t as blockResponse } from "../../claude-
|
|
1
|
+
import { a as guard, c as toClaudeResponse, i as fileSizeGuard, n as contextResponse, o as readClaudeInput, r as denyResponse, s as systemMessage, t as blockResponse } from "../../claude-U1KTaRG4.mjs";
|
|
2
2
|
export { blockResponse, contextResponse, denyResponse, fileSizeGuard, guard, readClaudeInput, systemMessage, toClaudeResponse };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as guard, n as contextResponse, o as readClaudeInput, r as denyResponse } from "../../claude-
|
|
1
|
+
import { a as guard, n as contextResponse, o as readClaudeInput, r as denyResponse } from "../../claude-U1KTaRG4.mjs";
|
|
2
2
|
export { contextResponse, denyResponse, guard, readClaudeInput as readCodexInput };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as evaluate } from "./evaluate-
|
|
1
|
+
import { t as evaluate } from "./evaluate-qYC5xClb.mjs";
|
|
2
2
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
package/dist/cli/bin.mjs
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import { r as loadDotenv, s as resolveTtlSec } from "../dotenv-B9nM4cuQ.mjs";
|
|
3
3
|
import { t as detectHarness } from "../harness-C8Nxxyn_.mjs";
|
|
4
4
|
import { t as claudeHome } from "../home-state-mKZxP4oZ.mjs";
|
|
5
|
-
import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-
|
|
5
|
+
import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-CaqeQFYp.mjs";
|
|
6
6
|
import { n as writeInitFile, t as initFor } from "../run-Do2JltgU.mjs";
|
|
7
|
-
import { Mt as todayUtc, t as handleHook } from "../handle-
|
|
7
|
+
import { Mt as todayUtc, t as handleHook } from "../handle-CCV0ycdr.mjs";
|
|
8
8
|
import { delimiter, dirname, join } from "node:path";
|
|
9
9
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { homedir } from "node:os";
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-
|
|
1
|
+
import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-CaqeQFYp.mjs";
|
|
2
2
|
export { checkStaged, stagedContent, stagedFiles };
|
|
@@ -147,6 +147,27 @@ const PROJECT_INSTALL = [
|
|
|
147
147
|
function matchPatterns(cmd, patterns) {
|
|
148
148
|
return patterns.some((re) => re.test(cmd));
|
|
149
149
|
}
|
|
150
|
+
/** Git commands exempt from confirmation when Ralph mode is on (parity git-guard.py RALPH_SAFE). */
|
|
151
|
+
const RALPH_SAFE = [
|
|
152
|
+
"git add",
|
|
153
|
+
"git commit",
|
|
154
|
+
"git checkout -b",
|
|
155
|
+
"git branch --show-current",
|
|
156
|
+
"git status",
|
|
157
|
+
"git diff",
|
|
158
|
+
"git log"
|
|
159
|
+
];
|
|
160
|
+
/**
|
|
161
|
+
* OPT-IN autonomous "Ralph" mode: OFF unless `RALPH_MODE` is `1`/`true`, read
|
|
162
|
+
* fresh on each call. Only the env var activates it — the Python source also
|
|
163
|
+
* auto-enabled on a `.claude/ralph/prd.json` file or a `feature/*` branch, but
|
|
164
|
+
* those silent activations are dropped here: a mode that removes git/install
|
|
165
|
+
* confirmations must never turn on implicitly (owner: default OFF, env opt-in).
|
|
166
|
+
*/
|
|
167
|
+
function isRalphMode() {
|
|
168
|
+
const v = process.env.RALPH_MODE;
|
|
169
|
+
return v === "1" || v === "true";
|
|
170
|
+
}
|
|
150
171
|
//#endregion
|
|
151
172
|
//#region src/policy/file-size-scope.ts
|
|
152
173
|
/**
|
|
@@ -302,23 +323,32 @@ function stripHeredoc(cmd) {
|
|
|
302
323
|
}
|
|
303
324
|
return out;
|
|
304
325
|
}
|
|
305
|
-
/**
|
|
326
|
+
/**
|
|
327
|
+
* Guards against dangerous Bash commands. Parity `security_rules.validate_command`
|
|
328
|
+
* + `security-guard.py`: ACCUMULATE every matched violation, then deny when any
|
|
329
|
+
* critical matched else ask, joining all violation labels in the reason. The
|
|
330
|
+
* generic `rm` ask is exempt when the command targets a trash location (parity
|
|
331
|
+
* `not re.search('trash', cmd)`).
|
|
332
|
+
*/
|
|
306
333
|
function securityGuard(ctx) {
|
|
307
334
|
if (ctx.tool !== "Bash" || !ctx.command) return null;
|
|
308
335
|
const cmd = stripHeredoc(ctx.command);
|
|
309
|
-
|
|
336
|
+
const hasTrash = /trash/i.test(cmd);
|
|
337
|
+
const critical = CRITICAL_PATTERNS.filter(({ re }) => re.test(cmd)).map((p) => p.label);
|
|
338
|
+
const ask = ASK_PATTERNS.filter(({ re, label }) => re.test(cmd) && !(hasTrash && label.startsWith("DELETE: 'rm'"))).map((p) => p.label);
|
|
339
|
+
if (critical.length === 0 && ask.length === 0) return null;
|
|
340
|
+
if (critical.length > 0) return {
|
|
310
341
|
kind: "block",
|
|
311
342
|
title: "Dangerous command",
|
|
312
|
-
reason: `${
|
|
343
|
+
reason: `${[...critical, ...ask].join(", ")}.`,
|
|
313
344
|
actions: ["Remove the destructive command", "Scope the operation to a specific safe path"]
|
|
314
345
|
};
|
|
315
|
-
|
|
346
|
+
return {
|
|
316
347
|
kind: "ask",
|
|
317
348
|
title: "Dangerous command",
|
|
318
|
-
reason: `${
|
|
349
|
+
reason: `${ask.join(", ")}.`,
|
|
319
350
|
actions: ["Confirm this command is intended", "Run with least privilege"]
|
|
320
351
|
};
|
|
321
|
-
return null;
|
|
322
352
|
}
|
|
323
353
|
//#endregion
|
|
324
354
|
//#region src/policy/guards/protected-path.ts
|
|
@@ -611,7 +641,7 @@ function bashWriteGuard(ctx) {
|
|
|
611
641
|
/** TS/JS component files: top-level `interface`/`type Foo`. */
|
|
612
642
|
const TS_DECL_RE = /^\s*(export\s+)?(interface|type)\s+[A-Z]/m;
|
|
613
643
|
/** Python view models: class subclassing a schema/protocol base. */
|
|
614
|
-
const PY_MODEL_RE = /^\s*class\s+\w+\((BaseModel|TypedDict|Protocol)\
|
|
644
|
+
const PY_MODEL_RE = /^\s*class\s+\w+\s*\([^)]*\b(?:BaseModel|TypedDict|Protocol)\b/m;
|
|
615
645
|
/**
|
|
616
646
|
* PHP controllers: top-level `interface`, `abstract class`, or a concrete
|
|
617
647
|
* `class …Interface/DTO/Request`. Union of the TS-only `abstract class` rule
|
|
@@ -662,16 +692,23 @@ function interfaceSeparationGuard(ctx) {
|
|
|
662
692
|
}
|
|
663
693
|
//#endregion
|
|
664
694
|
//#region src/policy/guards/install.ts
|
|
665
|
-
/**
|
|
695
|
+
/**
|
|
696
|
+
* Asks for confirmation before a dependency or system package install. In Ralph
|
|
697
|
+
* mode (opt-in `RALPH_MODE`) a PROJECT install is auto-approved for autonomous
|
|
698
|
+
* runs (parity install-guard.py:52), but a SYSTEM install always asks.
|
|
699
|
+
*/
|
|
666
700
|
function installGuard(ctx) {
|
|
667
701
|
if (ctx.tool !== "Bash" || !ctx.command) return null;
|
|
668
|
-
|
|
702
|
+
const isSystem = matchPatterns(ctx.command, SYSTEM_INSTALL);
|
|
703
|
+
const isProject = matchPatterns(ctx.command, PROJECT_INSTALL);
|
|
704
|
+
if (!isSystem && !isProject) return null;
|
|
705
|
+
if (!isSystem && isProject && isRalphMode()) return null;
|
|
706
|
+
return {
|
|
669
707
|
kind: "ask",
|
|
670
708
|
title: "Dependency install",
|
|
671
709
|
reason: `This command installs packages: ${ctx.command.trim()}`,
|
|
672
710
|
actions: ["Confirm this install is intended"]
|
|
673
711
|
};
|
|
674
|
-
return null;
|
|
675
712
|
}
|
|
676
713
|
//#endregion
|
|
677
714
|
//#region src/policy/guards/index.ts
|
|
@@ -730,7 +767,9 @@ function evaluate(ctx) {
|
|
|
730
767
|
message: guard.reason,
|
|
731
768
|
prompt: guard
|
|
732
769
|
};
|
|
733
|
-
|
|
770
|
+
const cmd = ctx.command;
|
|
771
|
+
const ralphSafe = !!cmd && isRalphMode() && RALPH_SAFE.some((s) => cmd.startsWith(s));
|
|
772
|
+
if (!ralphSafe && ctx.command && matchPatterns(ctx.command, GIT_BLOCKED)) {
|
|
734
773
|
const reason = `Destructive git command: ${ctx.command}`;
|
|
735
774
|
return {
|
|
736
775
|
decision: "deny",
|
|
@@ -743,7 +782,7 @@ function evaluate(ctx) {
|
|
|
743
782
|
}
|
|
744
783
|
};
|
|
745
784
|
}
|
|
746
|
-
if (ctx.command && matchPatterns(ctx.command, GIT_ASK)) return {
|
|
785
|
+
if (!ralphSafe && ctx.command && matchPatterns(ctx.command, GIT_ASK)) return {
|
|
747
786
|
decision: "deny",
|
|
748
787
|
message: `Git operation requires confirmation: ${ctx.command}`,
|
|
749
788
|
prompt: {
|
|
@@ -781,4 +820,4 @@ function evaluate(ctx) {
|
|
|
781
820
|
};
|
|
782
821
|
}
|
|
783
822
|
//#endregion
|
|
784
|
-
export { PROJECT_INSTALL as A, PROTECTED_GIT_RE as C, securityGuard as D, CRITICAL_PATTERNS as E,
|
|
823
|
+
export { PROJECT_INSTALL as A, PROTECTED_GIT_RE as C, securityGuard as D, CRITICAL_PATTERNS as E, PLUGINS_DIR as F, SOLID_REF as I, countFrameworkCodeLines as L, SYSTEM_INSTALL as M, isRalphMode as N, GIT_ASK as O, matchPatterns as P, countLines as R, PROTECTED_FRAGMENTS as S, ASK_PATTERNS as T, CODE_MUTATORS as _, registerGuard as a, SAFE_PREFIXES as b, GO_DECL_RE as c, PY_MODEL_RE as d, SWIFT_PROTO_RE as f, ASK_WRITERS as g, bashWriteGuard as h, clearUserGuards as i, RALPH_SAFE as j, GIT_BLOCKED as k, JAVA_DECL_RE as l, interfaceSeparationGuard as m, FAIL_CLOSED as n, runGuards as o, TS_DECL_RE as p, GUARDS as r, installGuard as s, evaluate as t, PHP_DECL_RE as u, CODE_REDIRECT as v, protectedPathGuard as w, SESSION_STATE_FRAGMENT as x, FILE_REDIRECT as y, evaluateFileSize as z };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { a as parseEnvInt, i as splitTarget, r as resolveMaxLines } from "./limits-CHn8AIL1.mjs";
|
|
2
2
|
import { r as projectLayout } from "./layout-C0jaaCQC.mjs";
|
|
3
3
|
import { i as walkUpFor, n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./project-root-3kk7gCOp.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import { F as
|
|
4
|
+
import { B as detectFramework, C as resolveSkillPath, D as frameworkSolidGate, G as requiredArchSkill, H as detectModularArchitecture, I as docConsultedGate, L as evaluateApex, M as capVerbosity, N as detectCreationIntent, O as isExcludedJsPath, S as usesTailwindUtilities, T as readPluginMeta, U as detectProjectType$1, _ as scanPlugin, c as EXCLUDE_DIRS$1, d as buildApexTaskInjection, h as buildClaudeMdContext, k as isExcludedSwiftPath, l as PROJECT_INDICATORS, n as missingSeoElements, o as parseEnrichment, r as descFromText, s as parseEntry, t as isHtmlLike, w as findMarketplacePlugins, x as skillTriggerGate, y as parseField } from "./validate-ebDjIyOa.mjs";
|
|
5
|
+
import { F as PLUGINS_DIR, L as countFrameworkCodeLines, R as countLines, n as FAIL_CLOSED, t as evaluate } from "./evaluate-qYC5xClb.mjs";
|
|
6
6
|
import { a as writeJsonFile, i as readJsonFile, r as hashText, t as atomicWrite } from "./json-io-DisYd2fb.mjs";
|
|
7
7
|
import { a as sanitizeSessionId, c as sessionsDir, i as loadSessionState, n as fuseHarnessHome, o as saveSessionState, r as fusengineCache, t as claudeHome } from "./home-state-mKZxP4oZ.mjs";
|
|
8
8
|
import { r as isDocConsulted } from "./doc-helpers-CWZegVdR.mjs";
|
|
@@ -10,8 +10,8 @@ import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
|
10
10
|
import { a as nowStamp, l as throttleMs, n as readRoots, o as readState, s as setStateField, t as addRoot } from "./registry-CymilZiZ.mjs";
|
|
11
11
|
import { d as loadIndex, i as cacheLookupMeta, n as webfetchCacheWrite, o as cacheLookupSubstringMeta, t as mcpCacheWrite, u as extractText } from "./mcp-store-BkBDmuxN.mjs";
|
|
12
12
|
import { t as loadRefs } from "./loader-AGz4nK7d.mjs";
|
|
13
|
-
import { c as recordDoc, d as trivialCount, i as agentsFresh, l as recordRefRead, n as saveTrack, o as recordAgent, r as verifyTrack, s as recordBrainstormRequired, t as loadTrack, u as recordTrivialEdit } from "./store-
|
|
14
|
-
import { d as readText, f as sleep, l as collectFiles, m as writeText, n as contextResponse, p as spawnCapture, r as denyResponse, s as systemMessage, t as blockResponse, u as pathExists } from "./claude-
|
|
13
|
+
import { c as recordDoc, d as trivialCount, i as agentsFresh, l as recordRefRead, n as saveTrack, o as recordAgent, r as verifyTrack, s as recordBrainstormRequired, t as loadTrack, u as recordTrivialEdit } from "./store-ARx-GQSQ.mjs";
|
|
14
|
+
import { d as readText, f as sleep, l as collectFiles, m as writeText, n as contextResponse, p as spawnCapture, r as denyResponse, s as systemMessage, t as blockResponse, u as pathExists } from "./claude-U1KTaRG4.mjs";
|
|
15
15
|
import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
16
16
|
import { appendFileSync, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
17
17
|
import { homedir } from "node:os";
|
|
@@ -3537,7 +3537,7 @@ const EXEMPT_PATTERNS = [
|
|
|
3537
3537
|
/\.claude-plugin\//,
|
|
3538
3538
|
/CHANGELOG\.md$/,
|
|
3539
3539
|
/marketplace\.json$/,
|
|
3540
|
-
/\/\.claude\/(apex|memory|logs)\//,
|
|
3540
|
+
/\/\.claude\/(apex|memory|logs|fusengine-cache)\//,
|
|
3541
3541
|
/\/\.fuse-harness\//
|
|
3542
3542
|
];
|
|
3543
3543
|
/**
|
|
@@ -3849,11 +3849,12 @@ function nextModular(filePath, content, cwd) {
|
|
|
3849
3849
|
}
|
|
3850
3850
|
/** Laravel FuseCore architecture: `app/` domain ban + module.json + cross-module `use` rules. */
|
|
3851
3851
|
function fusecore(filePath, content, cwd) {
|
|
3852
|
-
for (const b of PHP_BLOCKED_IN_APP) if (filePath.includes(b)) return block$1(`BLOCKED: FuseCore
|
|
3852
|
+
for (const b of PHP_BLOCKED_IN_APP) if (filePath.includes(b)) return block$1(`BLOCKED: FuseCore project. Code in '${b.replace(/^\/|\/$/g, "")}' FORBIDDEN. Move to '${b.replaceAll("/app/", "/FuseCore/{Module}/App/").replace(/^\/|\/$/g, "")}'.`);
|
|
3853
3853
|
const mod = filePath.match(/\/FuseCore\/([A-Za-z]+)\//);
|
|
3854
3854
|
if (!mod) return null;
|
|
3855
3855
|
const name = mod[1] ?? "";
|
|
3856
|
-
|
|
3856
|
+
const mj = join(cwd, "FuseCore", name, "module.json");
|
|
3857
|
+
if (!existsSync(mj)) return block$1(`BLOCKED: Module '${name}' missing module.json. Create ${mj} first.`);
|
|
3857
3858
|
for (const m of content.matchAll(/use\s+FuseCore\\(\w+)\\/g)) {
|
|
3858
3859
|
const imported = m[1] ?? "";
|
|
3859
3860
|
if (name === "Core") {
|
|
@@ -3873,6 +3874,19 @@ function modularGate(tool, filePath, content, cwd) {
|
|
|
3873
3874
|
}
|
|
3874
3875
|
//#endregion
|
|
3875
3876
|
//#region src/runtime/framework-skill-gate.ts
|
|
3877
|
+
/** PHP paths the Laravel skill check skips — broader than the SOLID vendor-only exclude (parity check-laravel-skill.py `/(vendor|storage|bootstrap/cache)/`). */
|
|
3878
|
+
const PHP_SKILL_EXCLUDE_RE = /\/(vendor|storage|bootstrap\/cache)\//;
|
|
3879
|
+
/**
|
|
3880
|
+
* Whether the sub-skill gate should skip this path — parity with the early return
|
|
3881
|
+
* in check-*-skill.py (build/vendor artifacts never require a sub-skill).
|
|
3882
|
+
* @param framework - the detected framework ("laravel" | "swift" | JS otherwise).
|
|
3883
|
+
* @param filePath - the file being written.
|
|
3884
|
+
*/
|
|
3885
|
+
function skillGateExcluded(framework, filePath) {
|
|
3886
|
+
if (framework === "laravel") return PHP_SKILL_EXCLUDE_RE.test(filePath);
|
|
3887
|
+
if (framework === "swift") return isExcludedSwiftPath(filePath);
|
|
3888
|
+
return isExcludedJsPath(filePath);
|
|
3889
|
+
}
|
|
3876
3890
|
/**
|
|
3877
3891
|
* Effective line count for the SOLID size check. On an Edit, `content` is only
|
|
3878
3892
|
* the `new_string` snippet, so for Next.js — mirroring the base file-size guard
|
|
@@ -3912,6 +3926,7 @@ function frameworkSkillGate(input, refsRead, existingCodeLines) {
|
|
|
3912
3926
|
const content = input.content ?? "";
|
|
3913
3927
|
const solid = frameworkSolidGate(input.filePath, content, effectiveLines(input.tool, input.framework, content, existingCodeLines));
|
|
3914
3928
|
if (solid) return solid;
|
|
3929
|
+
if (skillGateExcluded(input.framework, input.filePath)) return null;
|
|
3915
3930
|
const forced = input.cwd ? requiredArchSkill(input.cwd) : null;
|
|
3916
3931
|
return skillTriggerGate(input.framework, content, refsRead, forced, input.cwd, input.filePath);
|
|
3917
3932
|
}
|
|
@@ -3979,6 +3994,122 @@ function shadcnSkillGate(tool, filePath, content, ev) {
|
|
|
3979
3994
|
});
|
|
3980
3995
|
}
|
|
3981
3996
|
//#endregion
|
|
3997
|
+
//#region src/policy/tailwind-skill-gate.ts
|
|
3998
|
+
/** Vendored/build paths exempt (parity check-tailwind-skill.py `/(node_modules|dist|build)/`). */
|
|
3999
|
+
const TW_EXCLUDE_RE = /(^|\/)(node_modules|dist|build)\//;
|
|
4000
|
+
/** A read reference proving a base Tailwind skill was consulted (Phase 1). */
|
|
4001
|
+
const TW_BASE_SKILL_RE = /skills\/(tailwindcss-v4|tailwindcss-utilities)\//;
|
|
4002
|
+
/**
|
|
4003
|
+
* True when this Write/Edit is a `.tsx`/`.jsx` file carrying Tailwind utility
|
|
4004
|
+
* classes, outside vendored dirs — the trigger condition of Phase 1.
|
|
4005
|
+
* @param tool - the tool name.
|
|
4006
|
+
* @param filePath - the file being written.
|
|
4007
|
+
* @param content - the written content.
|
|
4008
|
+
*/
|
|
4009
|
+
function isTailwindWrite(tool, filePath, content) {
|
|
4010
|
+
if (tool !== "Write" && tool !== "Edit") return false;
|
|
4011
|
+
if (TW_EXCLUDE_RE.test(filePath)) return false;
|
|
4012
|
+
return usesTailwindUtilities(filePath, content);
|
|
4013
|
+
}
|
|
4014
|
+
/** True when `tailwindcss-v4` or `tailwindcss-utilities` was read this session (Phase 1 base skill). */
|
|
4015
|
+
function tailwindBaseSkillRead(refsRead) {
|
|
4016
|
+
return refsRead.some((p) => TW_BASE_SKILL_RE.test(p));
|
|
4017
|
+
}
|
|
4018
|
+
/**
|
|
4019
|
+
* Gate a Tailwind-scoped write: block until a base Tailwind skill is read
|
|
4020
|
+
* (parity check-tailwind-skill.py Phase 1). Returns `null` when out of scope
|
|
4021
|
+
* or already satisfied.
|
|
4022
|
+
* @param tool - the tool name.
|
|
4023
|
+
* @param filePath - the file being written.
|
|
4024
|
+
* @param content - the written content.
|
|
4025
|
+
* @param refsRead - in-session read reference paths.
|
|
4026
|
+
*/
|
|
4027
|
+
function tailwindSkillGate(tool, filePath, content, refsRead) {
|
|
4028
|
+
if (!isTailwindWrite(tool, filePath, content)) return null;
|
|
4029
|
+
if (tailwindBaseSkillRead(refsRead)) return null;
|
|
4030
|
+
return {
|
|
4031
|
+
kind: "block",
|
|
4032
|
+
title: "Tailwind CSS skill",
|
|
4033
|
+
reason: "BLOCKED: Tailwind skill not consulted. Read tailwindcss-v4/SKILL.md or tailwindcss-utilities/SKILL.md, or use mcp__context7__query-docs (topic: tailwindcss), then retry.",
|
|
4034
|
+
actions: [`Read ${resolveSkillPath("tailwindcss-v4")} or ${resolveSkillPath("tailwindcss-utilities")}`]
|
|
4035
|
+
};
|
|
4036
|
+
}
|
|
4037
|
+
//#endregion
|
|
4038
|
+
//#region src/policy/gemini-mcp-gate.ts
|
|
4039
|
+
/** UI file extensions the gate polices (parity enforce-gemini-mcp.py UI_EXT). */
|
|
4040
|
+
const UI_EXT_RE = /\.(tsx|jsx|vue|svelte)$/;
|
|
4041
|
+
/** Paths exempt from the gate (parity EXEMPT_DIRS). */
|
|
4042
|
+
const GEMINI_EXEMPT_RE = /(node_modules|dist|build|\.next|\.claude)\//;
|
|
4043
|
+
/** A live `mcp__gemini-design__*` call recorded this session proves consultation. */
|
|
4044
|
+
const GEMINI_MCP_SOURCE_RE = /gemini-mcp/i;
|
|
4045
|
+
/** Tailwind utility patterns (parity TAILWIND_PATTERNS). */
|
|
4046
|
+
const TW_PATTERNS = [
|
|
4047
|
+
/\bflex\b/,
|
|
4048
|
+
/\bgrid\b/,
|
|
4049
|
+
/\bp-\d/,
|
|
4050
|
+
/\bpx-\d/,
|
|
4051
|
+
/\bpy-\d/,
|
|
4052
|
+
/\bm-\d/,
|
|
4053
|
+
/\bmx-\d/,
|
|
4054
|
+
/\bmy-\d/,
|
|
4055
|
+
/\bmt-\d/,
|
|
4056
|
+
/\bmb-\d/,
|
|
4057
|
+
/\bbg-\w+/,
|
|
4058
|
+
/\btext-\w+/,
|
|
4059
|
+
/\brounded/,
|
|
4060
|
+
/\bshadow/,
|
|
4061
|
+
/\bborder\b/,
|
|
4062
|
+
/\bgap-\d/,
|
|
4063
|
+
/\bw-\w+/,
|
|
4064
|
+
/\bh-\w+/,
|
|
4065
|
+
/\bjustify-\w+/,
|
|
4066
|
+
/\bitems-\w+/,
|
|
4067
|
+
/\bspace-\w+-\d/
|
|
4068
|
+
];
|
|
4069
|
+
/** Min distinct Tailwind patterns before the gate fires (parity MIN_TAILWIND_CLASSES). */
|
|
4070
|
+
const MIN_TW = 3;
|
|
4071
|
+
/** Min newlines an Edit's new_string must have to be gated (parity MIN_LINES_FOR_EDIT). */
|
|
4072
|
+
const MIN_LINES_EDIT = 2;
|
|
4073
|
+
/** OPT-IN: off unless `FUSE_ENFORCE_GEMINI_MCP` is `1`/`true`. Read fresh each call (dynamic). */
|
|
4074
|
+
function geminiMcpEnforced() {
|
|
4075
|
+
const v = process.env.FUSE_ENFORCE_GEMINI_MCP;
|
|
4076
|
+
return v === "1" || v === "true";
|
|
4077
|
+
}
|
|
4078
|
+
/** Count distinct Tailwind utility patterns present in `content` (parity count_tailwind_classes). */
|
|
4079
|
+
function countTailwindClasses(content) {
|
|
4080
|
+
return TW_PATTERNS.reduce((n, re) => re.test(content) ? n + 1 : n, 0);
|
|
4081
|
+
}
|
|
4082
|
+
/** True when a `mcp__gemini-design__*` call was recorded this session (parity gemini_was_called). */
|
|
4083
|
+
function geminiMcpConsulted(authorizations, sessionId) {
|
|
4084
|
+
if (!authorizations) return false;
|
|
4085
|
+
return Object.values(authorizations).some((a) => a.doc_sessions?.includes(sessionId) && (a.sources ?? (a.source ? [a.source] : [])).some((s) => GEMINI_MCP_SOURCE_RE.test(s)));
|
|
4086
|
+
}
|
|
4087
|
+
/**
|
|
4088
|
+
* Gate a UI write when `FUSE_ENFORCE_GEMINI_MCP` is on: block hand-written
|
|
4089
|
+
* Tailwind (>= 3 classes) in a `.tsx/.jsx/.vue/.svelte` file unless a Gemini
|
|
4090
|
+
* Design MCP call was made this session. Returns `null` when disabled (default),
|
|
4091
|
+
* out of scope, or already satisfied.
|
|
4092
|
+
* @param tool - the tool name.
|
|
4093
|
+
* @param filePath - the file being written.
|
|
4094
|
+
* @param content - the written content (new_string on Edit).
|
|
4095
|
+
* @param ev - session evidence (authorizations + sessionId).
|
|
4096
|
+
*/
|
|
4097
|
+
function geminiMcpGate(tool, filePath, content, ev) {
|
|
4098
|
+
if (!geminiMcpEnforced()) return null;
|
|
4099
|
+
if (tool !== "Write" && tool !== "Edit") return null;
|
|
4100
|
+
if (!filePath || !UI_EXT_RE.test(filePath) || GEMINI_EXEMPT_RE.test(filePath)) return null;
|
|
4101
|
+
if (!content) return null;
|
|
4102
|
+
if (tool === "Edit" && (content.match(/\n/g)?.length ?? 0) < MIN_LINES_EDIT) return null;
|
|
4103
|
+
if (countTailwindClasses(content) < MIN_TW) return null;
|
|
4104
|
+
if (geminiMcpConsulted(ev.authorizations, ev.sessionId)) return null;
|
|
4105
|
+
return {
|
|
4106
|
+
kind: "block",
|
|
4107
|
+
title: "Gemini Design MCP",
|
|
4108
|
+
reason: "BLOCKED: UI code with Tailwind detected but Gemini Design MCP not used. Use mcp__gemini-design__create_frontend, modify_frontend, or snippet_frontend BEFORE writing UI code manually.",
|
|
4109
|
+
actions: ["Call mcp__gemini-design__create_frontend / modify_frontend / snippet_frontend, then retry"]
|
|
4110
|
+
};
|
|
4111
|
+
}
|
|
4112
|
+
//#endregion
|
|
3982
4113
|
//#region src/freshness/explore-tools.ts
|
|
3983
4114
|
/** Native exploration tools (parity `apex_constants.EXPLORE_TOOLS`). */
|
|
3984
4115
|
const EXPLORE_TOOLS = /* @__PURE__ */ new Set(["Glob", "Grep"]);
|
|
@@ -4005,9 +4136,17 @@ const EXPLORE_BASH_CMDS = /* @__PURE__ */ new Set([
|
|
|
4005
4136
|
"head",
|
|
4006
4137
|
"tail"
|
|
4007
4138
|
]);
|
|
4008
|
-
/**
|
|
4139
|
+
/** Legacy Python cache names still credited (parity `CACHE_READ_RE` + doc-helpers). */
|
|
4009
4140
|
const CACHE_READ_RE = /\/context\/mcp\/(exa-search|exa-code-context|context7)-/;
|
|
4010
4141
|
/**
|
|
4142
|
+
* Real TS cache stores: `<root>/.harness/cache/<fnv16>.md` (core MCP store,
|
|
4143
|
+
* `projectLayout().cacheDir`) and `~/.fuse-harness/cache/**` (ai-pilot doc
|
|
4144
|
+
* caches, `cache-base.cacheBaseDir`). Segment-matched — not resolved against
|
|
4145
|
+
* `homedir()` — so tilde-prefixed and absolute paths both hit; the `.md`
|
|
4146
|
+
* suffix keeps session-state JSON reads from counting as research.
|
|
4147
|
+
*/
|
|
4148
|
+
const TS_CACHE_READ_RE = /\.(?:fuse-)?harness[\\/]cache[\\/].*\.md$/;
|
|
4149
|
+
/**
|
|
4011
4150
|
* First non-assignment shell token's basename, or "" — mirrors Python
|
|
4012
4151
|
* `_bash_executable` (skips leading `VAR=value` env prefixes, e.g. `FOO=1 grep`).
|
|
4013
4152
|
* @param cmd - Raw Bash `command` string.
|
|
@@ -4039,7 +4178,7 @@ function classifyExplore(tool, input) {
|
|
|
4039
4178
|
};
|
|
4040
4179
|
if (tool === "Read") {
|
|
4041
4180
|
const path = String(input?.file_path ?? input?.path ?? "");
|
|
4042
|
-
if (path && CACHE_READ_RE.test(path)) return {
|
|
4181
|
+
if (path && (CACHE_READ_RE.test(path) || TS_CACHE_READ_RE.test(path))) return {
|
|
4043
4182
|
phase: "research-expert",
|
|
4044
4183
|
cacheHit: true
|
|
4045
4184
|
};
|
|
@@ -4157,10 +4296,12 @@ async function apexScopedGate(input, track, window) {
|
|
|
4157
4296
|
authorizations: track.authorizations,
|
|
4158
4297
|
refs: input.refs,
|
|
4159
4298
|
refsRead: track.refsRead,
|
|
4299
|
+
refsReadAt: track.refsReadAt,
|
|
4300
|
+
now: input.now,
|
|
4160
4301
|
agentsFresh: freshnessFor([...REQUIRED_AGENTS]),
|
|
4161
4302
|
missingAgents: REQUIRED_AGENTS.filter((name) => !freshnessFor([name])),
|
|
4162
4303
|
windowMs: window,
|
|
4163
|
-
brainstormRequired: input.tool === "Edit" ? false : track.brainstormRequired,
|
|
4304
|
+
brainstormRequired: input.tool === "Edit" || input.agentId ? false : track.brainstormRequired,
|
|
4164
4305
|
brainstormFresh: freshnessFor(["brainstorming"], Number.MAX_SAFE_INTEGER)
|
|
4165
4306
|
};
|
|
4166
4307
|
try {
|
|
@@ -4221,6 +4362,15 @@ async function gate(input) {
|
|
|
4221
4362
|
});
|
|
4222
4363
|
if (shadcnBlock) return shadcnBlock;
|
|
4223
4364
|
}
|
|
4365
|
+
if (isTailwindWrite(input.tool, filePath, input.content ?? "")) {
|
|
4366
|
+
const twBlock = tailwindSkillGate(input.tool, filePath, input.content ?? "", track.refsRead);
|
|
4367
|
+
if (twBlock) return twBlock;
|
|
4368
|
+
}
|
|
4369
|
+
const geminiBlock = geminiMcpGate(input.tool, filePath, input.content ?? "", {
|
|
4370
|
+
authorizations: track.authorizations,
|
|
4371
|
+
sessionId: input.sessionId
|
|
4372
|
+
});
|
|
4373
|
+
if (geminiBlock) return geminiBlock;
|
|
4224
4374
|
if (isApexScoped(input.filePath)) {
|
|
4225
4375
|
const apex = await apexScopedGate(input, track, window);
|
|
4226
4376
|
if (apex) return apex;
|
|
@@ -4343,7 +4493,7 @@ function mcpPostStore(tool, input, response, dir, now = Date.now()) {
|
|
|
4343
4493
|
/** Apply an activity to a session's track and persist it (PostToolUse path). */
|
|
4344
4494
|
async function recordActivity(file, activity) {
|
|
4345
4495
|
const track = await loadTrack(file);
|
|
4346
|
-
await saveTrack(file, activity.kind === "agent" ? recordAgent(track, activity.name, activity.ts, activity.quality) : activity.kind === "doc" ? recordDoc(track, activity.framework, activity.sessionId, activity.source) : recordRefRead(track, activity.path));
|
|
4496
|
+
await saveTrack(file, activity.kind === "agent" ? recordAgent(track, activity.name, activity.ts, activity.quality) : activity.kind === "doc" ? recordDoc(track, activity.framework, activity.sessionId, activity.source) : recordRefRead(track, activity.path, activity.ts));
|
|
4347
4497
|
}
|
|
4348
4498
|
//#endregion
|
|
4349
4499
|
//#region src/runtime/respond.ts
|
|
@@ -4646,25 +4796,6 @@ function htmlCssOnlyGate(filePath) {
|
|
|
4646
4796
|
function stateFileGate(filePath) {
|
|
4647
4797
|
return filePath.includes(".design-state-") ? deny("BLOCKED: .design-state files are READ-ONLY for you. Hooks update them automatically as you progress. Do NOT try to modify them — it will not unblock you. Follow the pipeline: Phase 0→1→2→3→4→5→6 in order.") : null;
|
|
4648
4798
|
}
|
|
4649
|
-
/**
|
|
4650
|
-
* Ports browser_helpers.is_exempt: design-system.md is NOT exempt (it must meet
|
|
4651
|
-
* the quota); other `.md` files and vendored dirs ARE exempt from the check.
|
|
4652
|
-
*/
|
|
4653
|
-
function screenshotExempt(filePath) {
|
|
4654
|
-
if (filePath.endsWith("design-system.md")) return false;
|
|
4655
|
-
return filePath.endsWith(".md") || EXEMPT_DIRS.some((d) => filePath.includes(d));
|
|
4656
|
-
}
|
|
4657
|
-
/**
|
|
4658
|
-
* Block ANY non-exempt write (html/css/design-system.md) until the per-mode
|
|
4659
|
-
* screenshot quota is met. Ports check-browser-browsing.py's "Browse N sites
|
|
4660
|
-
* BEFORE writing" deny for the active design agent.
|
|
4661
|
-
*/
|
|
4662
|
-
function preScreenshotWriteGate(filePath, state) {
|
|
4663
|
-
if (screenshotExempt(filePath)) return null;
|
|
4664
|
-
const needed = MIN_SCREENSHOTS[state.mode];
|
|
4665
|
-
if (state.screenshotsCount < needed) return deny(`BLOCKED: ${state.screenshotsCount}/${needed} fuse-browser screenshots for mode '${state.mode}'. Browse ${needed - state.screenshotsCount} more site(s) BEFORE writing. Read design-inspiration.md, then browser_open + browser_navigate + browser_screenshot.`);
|
|
4666
|
-
return null;
|
|
4667
|
-
}
|
|
4668
4799
|
/** Return the requirements missing from a design-system.md (empty = valid). */
|
|
4669
4800
|
function validateDesignSystem(content) {
|
|
4670
4801
|
const missing = [];
|
|
@@ -4759,7 +4890,7 @@ function designGate(payload, event, cacheDir, cwd) {
|
|
|
4759
4890
|
}
|
|
4760
4891
|
if (event.tool === "Write" || event.tool === "Edit") {
|
|
4761
4892
|
const fp = event.filePath ?? "";
|
|
4762
|
-
const base = stateFileGate(fp) ?? htmlCssOnlyGate(fp) ??
|
|
4893
|
+
const base = stateFileGate(fp) ?? htmlCssOnlyGate(fp) ?? designSystemWriteGate(fp, state);
|
|
4763
4894
|
if (base) return base;
|
|
4764
4895
|
if (geminiEnabled() && state.geminiCalls === 0 && /\.(html|css)$/.test(fp)) return {
|
|
4765
4896
|
kind: "block",
|
|
@@ -4775,7 +4906,14 @@ function designGate(payload, event, cacheDir, cwd) {
|
|
|
4775
4906
|
if (!geminiEnabled()) return null;
|
|
4776
4907
|
const block = geminiCreateGate(state);
|
|
4777
4908
|
if (block) return block;
|
|
4778
|
-
const
|
|
4909
|
+
const ds = findDesignSystem(cwd);
|
|
4910
|
+
if (ds === "") return {
|
|
4911
|
+
kind: "block",
|
|
4912
|
+
title: "Design pipeline",
|
|
4913
|
+
reason: "BLOCKED: design-system.md not found. RECOVERY: 1) Read the identity templates 2) Read design-inspiration.md 3) Browse 4 reference sites 4) Write design-system.md, then retry create_frontend.",
|
|
4914
|
+
actions: ["Create design-system.md via the pipeline, then retry create_frontend"]
|
|
4915
|
+
};
|
|
4916
|
+
const missing = validateDesignSystem(ds);
|
|
4779
4917
|
if (missing.length) return {
|
|
4780
4918
|
kind: "block",
|
|
4781
4919
|
title: "Design pipeline",
|
|
@@ -4819,13 +4957,10 @@ async function handlePre(ctx) {
|
|
|
4819
4957
|
stdout: securityAdvisory(event.tool, event.filePath ?? "", opts.now),
|
|
4820
4958
|
exit: 0
|
|
4821
4959
|
};
|
|
4822
|
-
if (opts.scope === "solid"
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
exit: 0
|
|
4827
|
-
};
|
|
4828
|
-
}
|
|
4960
|
+
if (opts.scope === "solid") return {
|
|
4961
|
+
stdout: validateSolidGate(event.tool, event.filePath ?? "", event.content ?? ""),
|
|
4962
|
+
exit: 0
|
|
4963
|
+
};
|
|
4829
4964
|
if (event.tool === "Task") {
|
|
4830
4965
|
const taskCtx = taskContext(opts.cwd);
|
|
4831
4966
|
if (taskCtx) return {
|
|
@@ -4844,6 +4979,7 @@ async function handlePre(ctx) {
|
|
|
4844
4979
|
refs: opts.refsDir ? await loadRefs(opts.refsDir) : void 0,
|
|
4845
4980
|
isReplaceAll: event.input.replace_all === true,
|
|
4846
4981
|
agentType: event.agentType,
|
|
4982
|
+
agentId: typeof payload.agent_id === "string" ? payload.agent_id : void 0,
|
|
4847
4983
|
windowMs: opts.windowMs,
|
|
4848
4984
|
now: opts.now,
|
|
4849
4985
|
trackFile: file,
|
|
@@ -4900,6 +5036,7 @@ function docSourceOf(tool) {
|
|
|
4900
5036
|
if (tool === "WebSearch") return "websearch";
|
|
4901
5037
|
if (tool === "WebFetch") return "webfetch";
|
|
4902
5038
|
if (/^mcp__shadcn__/i.test(tool)) return "shadcn-mcp";
|
|
5039
|
+
if (/^mcp__gemini-design__/i.test(tool)) return "gemini-mcp";
|
|
4903
5040
|
}
|
|
4904
5041
|
/**
|
|
4905
5042
|
* Map a live tool-use to the activity to record, or null when nothing is
|
|
@@ -4932,7 +5069,8 @@ function activityFor(event) {
|
|
|
4932
5069
|
const path = String(event.input?.file_path ?? event.input?.path ?? "");
|
|
4933
5070
|
if (path.endsWith(".md")) out.push({
|
|
4934
5071
|
kind: "ref",
|
|
4935
|
-
path
|
|
5072
|
+
path,
|
|
5073
|
+
ts: event.now
|
|
4936
5074
|
});
|
|
4937
5075
|
}
|
|
4938
5076
|
return out;
|
|
@@ -113,6 +113,16 @@ declare const SYSTEM_INSTALL: ReadonlyArray<RegExp>;
|
|
|
113
113
|
declare const PROJECT_INSTALL: ReadonlyArray<RegExp>;
|
|
114
114
|
/** True when `cmd` matches any pattern in `patterns`. */
|
|
115
115
|
declare function matchPatterns(cmd: string, patterns: ReadonlyArray<RegExp>): boolean;
|
|
116
|
+
/** Git commands exempt from confirmation when Ralph mode is on (parity git-guard.py RALPH_SAFE). */
|
|
117
|
+
declare const RALPH_SAFE: ReadonlyArray<string>;
|
|
118
|
+
/**
|
|
119
|
+
* OPT-IN autonomous "Ralph" mode: OFF unless `RALPH_MODE` is `1`/`true`, read
|
|
120
|
+
* fresh on each call. Only the env var activates it — the Python source also
|
|
121
|
+
* auto-enabled on a `.claude/ralph/prd.json` file or a `feature/*` branch, but
|
|
122
|
+
* those silent activations are dropped here: a mode that removes git/install
|
|
123
|
+
* confirmations must never turn on implicitly (owner: default OFF, env opt-in).
|
|
124
|
+
*/
|
|
125
|
+
declare function isRalphMode(): boolean;
|
|
116
126
|
//#endregion
|
|
117
127
|
//#region src/policy/interfaces/types.d.ts
|
|
118
128
|
/** Harness-agnostic input to {@link evaluate}. */
|
|
@@ -146,7 +156,7 @@ interface PolicyResult {
|
|
|
146
156
|
declare function evaluate(ctx: PolicyContext): PolicyResult;
|
|
147
157
|
//#endregion
|
|
148
158
|
//#region src/policy/apex-gates.d.ts
|
|
149
|
-
/** Gate: the routed SOLID references for this edit must have been read. */
|
|
159
|
+
/** Gate: the routed SOLID references for this edit must have been read within the TTL. */
|
|
150
160
|
declare const solidReadGate: ApexGate;
|
|
151
161
|
/** Gate: the required prior agents (explore + research) must have run within the window. */
|
|
152
162
|
declare const freshnessGate: ApexGate;
|
|
@@ -168,11 +178,22 @@ interface ApexContext {
|
|
|
168
178
|
refs?: RefMeta[];
|
|
169
179
|
/** Absolute paths of SOLID refs already read this session. */
|
|
170
180
|
refsRead?: string[];
|
|
181
|
+
/**
|
|
182
|
+
* Epoch-ms read timestamp per `refsRead` path (parity track-solid-reads.py
|
|
183
|
+
* `solid_reads[].timestamp`). PARITY: the TTL applies ONLY to solidReadGate —
|
|
184
|
+
* Python TTL-izes SOLID reads exclusively (require-solid-read.py); the
|
|
185
|
+
* skill-trigger/design/shadcn gates stay session-scoped (no TTL). A path in
|
|
186
|
+
* `refsRead` with no stamp here (tracks recorded before this field existed)
|
|
187
|
+
* counts as read — backward compat.
|
|
188
|
+
*/
|
|
189
|
+
refsReadAt?: Record<string, number>;
|
|
190
|
+
/** Current epoch ms for the SOLID-read TTL check; absent → reads never expire. */
|
|
191
|
+
now?: number;
|
|
171
192
|
/** Whether the required prior agents (explore + research) ran within the freshness window. */
|
|
172
193
|
agentsFresh?: boolean;
|
|
173
194
|
/** Names of REQUIRED_AGENTS that have NOT run fresh (subset), for a precise freshnessGate message. Absent → generic wording. */
|
|
174
195
|
missingAgents?: string[];
|
|
175
|
-
/** Freshness window in ms
|
|
196
|
+
/** Freshness window in ms: labels the block messages' TTL (e.g. "2min") and bounds the SOLID-read TTL in solidReadGate. */
|
|
176
197
|
windowMs?: number;
|
|
177
198
|
/** Whether brainstorming is required for this edit (creation intent on a new file). */
|
|
178
199
|
brainstormRequired?: boolean;
|
|
@@ -214,7 +235,13 @@ interface LabeledPattern {
|
|
|
214
235
|
declare const CRITICAL_PATTERNS: LabeledPattern[];
|
|
215
236
|
/** Patterns that warrant explicit confirmation before running — parity `security_rules.py`'s ask-level violation names. */
|
|
216
237
|
declare const ASK_PATTERNS: LabeledPattern[];
|
|
217
|
-
/**
|
|
238
|
+
/**
|
|
239
|
+
* Guards against dangerous Bash commands. Parity `security_rules.validate_command`
|
|
240
|
+
* + `security-guard.py`: ACCUMULATE every matched violation, then deny when any
|
|
241
|
+
* critical matched else ask, joining all violation labels in the reason. The
|
|
242
|
+
* generic `rm` ask is exempt when the command targets a trash location (parity
|
|
243
|
+
* `not re.search('trash', cmd)`).
|
|
244
|
+
*/
|
|
218
245
|
declare function securityGuard(ctx: GuardContext): Prompt | null;
|
|
219
246
|
//#endregion
|
|
220
247
|
//#region src/policy/guards/protected-path.d.ts
|
|
@@ -323,7 +350,11 @@ declare const JAVA_DECL_RE: RegExp;
|
|
|
323
350
|
declare function interfaceSeparationGuard(ctx: GuardContext): Prompt | null;
|
|
324
351
|
//#endregion
|
|
325
352
|
//#region src/policy/guards/install.d.ts
|
|
326
|
-
/**
|
|
353
|
+
/**
|
|
354
|
+
* Asks for confirmation before a dependency or system package install. In Ralph
|
|
355
|
+
* mode (opt-in `RALPH_MODE`) a PROJECT install is auto-approved for autonomous
|
|
356
|
+
* runs (parity install-guard.py:52), but a SYSTEM install always asks.
|
|
357
|
+
*/
|
|
327
358
|
declare function installGuard(ctx: GuardContext): Prompt | null;
|
|
328
359
|
//#endregion
|
|
329
360
|
//#region src/policy/guards/index.d.ts
|
|
@@ -571,4 +602,4 @@ declare function isHtmlLike(path: string): boolean;
|
|
|
571
602
|
*/
|
|
572
603
|
declare function missingSeoElements(html: string): string[];
|
|
573
604
|
//#endregion
|
|
574
|
-
export { ASK_PATTERNS as $, FAIL_CLOSED as A,
|
|
605
|
+
export { ASK_PATTERNS as $, FAIL_CLOSED as A, DEV_KEYWORDS as At, SWIFT_PROTO_RE as B, usesTailwindUtilities as C, FileSizeVerdict as Ct, MAX_TOKENS as D, countLines as Dt, MAX_EXA_RESULTS as E, countFrameworkCodeLines as Et, installGuard as F, isApexCommand as Ft, CODE_MUTATORS as G, interfaceSeparationGuard as H, GO_DECL_RE as I, requiredArchSkill as It, SAFE_PREFIXES as J, CODE_REDIRECT as K, JAVA_DECL_RE as L, clearUserGuards as M, ProjectType as Mt, registerGuard as N, detectModularArchitecture as Nt, capVerbosity as O, evaluateFileSize as Ot, runGuards as P, detectProjectType as Pt, protectedPathGuard as Q, PHP_DECL_RE as R, skillTriggerGate as S, matchPatterns as St, frameworkSolidGate as T, SOLID_REF as Tt, bashWriteGuard as U, TS_DECL_RE as V, ASK_WRITERS as W, PROTECTED_FRAGMENTS as X, SESSION_STATE_FRAGMENT as Y, PROTECTED_GIT_RE as Z, DEV_VERBS as _, GIT_BLOCKED as _t, firstHeading as a, APEX_GATES as at, detectClaudeMdProjectType as b, SYSTEM_INSTALL as bt, parseEntry as c, brainstormGate as ct, EXCLUDE_DIRS as d, freshnessGate as dt, CRITICAL_PATTERNS as et, PROJECT_INDICATORS as f, solidReadGate as ft, loadApexTaskState as g, GIT_ASK as gt, buildApexTaskInjection as h, PolicyResult as ht, firstComment as i, GuardContext as it, GUARDS as j, ModularArchitecture as jt, detectCreationIntent as k, detectFramework as kt, parseBodyDesc as l, docConsultedGate as lt, buildApexTaskContext as m, PolicyContext as mt, missingSeoElements as n, securityGuard as nt, TreeEntry as o, ApexContext as ot, ApexTaskState as p, evaluate as pt, FILE_REDIRECT as q, descFromText as r, Guard as rt, parseEnrichment as s, ApexGate as st, isHtmlLike as t, LabeledPattern as tt, parseField as u, evaluateApex as ut, buildApexInstruction as v, PROJECT_INSTALL as vt, SKILL_TRIGGERS as w, PLUGINS_DIR as wt, detectRequiredSkills as x, isRalphMode as xt, buildClaudeMdContext as y, RALPH_SAFE as yt, PY_MODEL_RE as z };
|
package/dist/index.d.mts
CHANGED
|
@@ -5,10 +5,9 @@ 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-BNfYWvYv.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 ASK_PATTERNS, A as FAIL_CLOSED, At as
|
|
8
|
+
import { $ as ASK_PATTERNS, A as FAIL_CLOSED, At as DEV_KEYWORDS, B as SWIFT_PROTO_RE, C as usesTailwindUtilities, Ct as FileSizeVerdict, D as MAX_TOKENS, Dt as countLines, E as MAX_EXA_RESULTS, Et as countFrameworkCodeLines, F as installGuard, Ft as isApexCommand, G as CODE_MUTATORS, H as interfaceSeparationGuard, I as GO_DECL_RE, It as requiredArchSkill, J as SAFE_PREFIXES, K as CODE_REDIRECT, L as JAVA_DECL_RE, M as clearUserGuards, Mt as ProjectType, N as registerGuard, Nt as detectModularArchitecture, O as capVerbosity, Ot as evaluateFileSize, P as runGuards, Pt as detectProjectType, Q as protectedPathGuard, R as PHP_DECL_RE, S as skillTriggerGate, St as matchPatterns, T as frameworkSolidGate, Tt as SOLID_REF, U as bashWriteGuard, V as TS_DECL_RE, W as ASK_WRITERS, X as PROTECTED_FRAGMENTS, Y as SESSION_STATE_FRAGMENT, Z as PROTECTED_GIT_RE, _ as DEV_VERBS, _t as GIT_BLOCKED, a as firstHeading, at as APEX_GATES, b as detectClaudeMdProjectType, bt as SYSTEM_INSTALL, c as parseEntry, ct as brainstormGate, d as EXCLUDE_DIRS, dt as freshnessGate, et as CRITICAL_PATTERNS, f as PROJECT_INDICATORS, ft as solidReadGate, g as loadApexTaskState, gt as GIT_ASK, h as buildApexTaskInjection, ht as PolicyResult, i as firstComment, it as GuardContext, j as GUARDS, jt as ModularArchitecture, k as detectCreationIntent, kt as detectFramework, l as parseBodyDesc, lt as docConsultedGate, m as buildApexTaskContext, mt as PolicyContext, n as missingSeoElements, nt as securityGuard, o as TreeEntry, ot as ApexContext, p as ApexTaskState, pt as evaluate, q as FILE_REDIRECT, r as descFromText, rt as Guard, s as parseEnrichment, st as ApexGate, t as isHtmlLike, tt as LabeledPattern, u as parseField, ut as evaluateApex, v as buildApexInstruction, vt as PROJECT_INSTALL, w as SKILL_TRIGGERS, wt as PLUGINS_DIR, x as detectRequiredSkills, xt as isRalphMode, y as buildClaudeMdContext, yt as RALPH_SAFE, z as PY_MODEL_RE } from "./index-BqfOFKm7.mjs";
|
|
9
9
|
import { n as RouteResult, r as ScoredRef, t as RefMeta } from "./types-CY5qT2X1.mjs";
|
|
10
10
|
import { a as ReminderState, c as 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";
|
|
11
11
|
import { a as globToRe, i as scoreReferences, n as toRefMeta, o as parseFrontmatter, r as routeReferences, t as loadRefs } from "./index-mmRF3KNp.mjs";
|
|
12
|
-
import { a as taskStart, c as ensureStateDir, d as stateFilePath, f as acquireLock, i as taskCreate, l as loadState, n as ApexTaskFile, o as ApexState, r as taskComplete, s as apexStateDir, t as ApexTask, u as saveState } from "./index-BA-SqNR7.mjs";
|
|
13
12
|
import { _ as TIME_INTERVALS, a as formatCost, c as formatTokens, d as colors, f as progressiveColor, g as PROGRESS_CHARS, h as PROGRESS_BAR_DEFAULTS, i as formatBasename, l as ColorFn, m as GRADIENT_BLOCKS, n as generateGradientBar, o as formatPath, p as COLOR_THRESHOLDS, r as generateProgressBar, s as formatTimeLeft, t as ProgressBarOptions, u as Palette } from "./index-BWK8slRi.mjs";
|
|
14
|
-
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, ApexContext, ApexGate,
|
|
13
|
+
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, ApexContext, ApexGate, ApexTaskState, AuthEntry, CODE_MUTATORS, CODE_REDIRECT, COLOR_THRESHOLDS, CRITICAL_PATTERNS, CacheHit, ColorFn, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, DEV_VERBS, DocSatisfactionStatus, EXCLUDE_DIRS, FAIL_CLOSED, FILE_REDIRECT, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, GO_DECL_RE, GRADIENT_BLOCKS, GUARDS, Guard, GuardContext, HOME_DIR, type HarnessId, type HarnessInfo, type HarnessMode, type HarnessVia, IndexSummary, JAVA_DECL_RE, LabeledPattern, MAX_EXA_RESULTS, MAX_LINES_ENV_KEY, MAX_TOKENS, ModularArchitecture, PHP_DECL_RE, PLUGINS_DIR, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, Palette, type PolicyContext, type PolicyResult, ProgressBarOptions, ProjectLayout, ProjectType, Prompt, PromptKind, RALPH_SAFE, RefMeta, ReminderState, RouteResult, SAFE_PREFIXES, SESSION_STATE_FRAGMENT, SKILL_TRIGGERS, SOLID_REF, STATE_GITIGNORE, STATE_ROOT, SWIFT_PROTO_RE, SYSTEM_INSTALL, ScoredRef, TIME_INTERVALS, TS_DECL_RE, TTL_ENV_KEY, TreeEntry, addRoot, bashWriteGuard, brainstormGate, buildApexInstruction, buildApexTaskContext, buildApexTaskInjection, buildClaudeMdContext, cacheLookup, cacheLookupMeta, cacheLookupSubstring, cacheLookupSubstringMeta, cachePath, cacheStore, capVerbosity, clearUserGuards, colors, compactJson, compactMarkdown, countFrameworkCodeLines, countLines, descFromText, detectClaudeMdProjectType, detectCreationIntent, detectFramework, detectHarness, detectMode, detectModularArchitecture, detectProjectType, detectRequiredSkills, docConsultedGate, ensureMemoryGitignore, envCandidates, evaluate, evaluateApex, evaluateFileSize, extractText, firstComment, firstHeading, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, frameworkSolidGate, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, installGuard, interfaceSeparationGuard, isApexCommand, isCodeFile, isDocConsulted, isHtmlLike, isRalphMode, jaccardSimilar, lessonsFileFor, loadApexTaskState, loadDotenv, loadIndex, loadRefs, matchPatterns, mcpCacheKey, mcpCacheWrite, missingSeoElements, modeFor, nowStamp, parseBodyDesc, parseEnrichment, parseEntry, parseEnvFile, parseEnvInt, parseField, parseFrontmatter, progressiveColor, projectLayout, projectRoot, projectRootOrNull, protectedPathGuard, queryHash, readRoots, readState, registerGuard, registryFile, requiredArchSkill, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, runGuards, scoreReferences, securityGuard, setStateField, skillTriggerGate, solidReadGate, splitTarget, stateFileFor, summarizeIndex, throttleMs, toRefMeta, ttlLabel, usesTailwindUtilities, walkUpFor, webfetchCacheWrite };
|
package/dist/index.mjs
CHANGED
|
@@ -4,8 +4,8 @@ import { n as STATE_ROOT, r as projectLayout, t as STATE_GITIGNORE } from "./lay
|
|
|
4
4
|
import { t as compactJson } from "./compact-json-DK2nX-MK.mjs";
|
|
5
5
|
import { i as walkUpFor, n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./project-root-3kk7gCOp.mjs";
|
|
6
6
|
import { n as detectMode, r as modeFor, t as detectHarness } from "./harness-C8Nxxyn_.mjs";
|
|
7
|
-
import { A as
|
|
8
|
-
import { A as PROJECT_INSTALL, C as PROTECTED_GIT_RE, D as securityGuard, E as CRITICAL_PATTERNS, F as
|
|
7
|
+
import { A as MAX_EXA_RESULTS, B as detectFramework, D as frameworkSolidGate, E as SKILL_TRIGGERS, F as brainstormGate, G as requiredArchSkill, H as detectModularArchitecture, I as docConsultedGate, L as evaluateApex, M as capVerbosity, N as detectCreationIntent, P as APEX_GATES, R as freshnessGate, S as usesTailwindUtilities, U as detectProjectType, V as DEV_KEYWORDS, W as isApexCommand, a as firstHeading, b as detectRequiredSkills, c as EXCLUDE_DIRS, d as buildApexTaskInjection, f as loadApexTaskState, g as detectClaudeMdProjectType, h as buildClaudeMdContext, i as firstComment, j 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, v as parseBodyDesc, x as skillTriggerGate, y as parseField, z as solidReadGate } from "./validate-ebDjIyOa.mjs";
|
|
8
|
+
import { A as PROJECT_INSTALL, C as PROTECTED_GIT_RE, D as securityGuard, E as CRITICAL_PATTERNS, F as PLUGINS_DIR, I as SOLID_REF, L as countFrameworkCodeLines, M as SYSTEM_INSTALL, N as isRalphMode, O as GIT_ASK, P as matchPatterns, R as countLines, S as PROTECTED_FRAGMENTS, T as ASK_PATTERNS, _ as CODE_MUTATORS, a as registerGuard, b as SAFE_PREFIXES, c as GO_DECL_RE, d as PY_MODEL_RE, f as SWIFT_PROTO_RE, g as ASK_WRITERS, h as bashWriteGuard, i as clearUserGuards, j as RALPH_SAFE, k as GIT_BLOCKED, l as JAVA_DECL_RE, m as interfaceSeparationGuard, n as FAIL_CLOSED, o as runGuards, p as TS_DECL_RE, r as GUARDS, s as installGuard, t as evaluate, u as PHP_DECL_RE, v as CODE_REDIRECT, w as protectedPathGuard, x as SESSION_STATE_FRAGMENT, y as FILE_REDIRECT, z as evaluateFileSize } from "./evaluate-qYC5xClb.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
11
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
@@ -13,6 +13,5 @@ import { a as nowStamp, c as stateFileFor, i as lessonsFileFor, l as throttleMs,
|
|
|
13
13
|
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";
|
|
14
14
|
import { t as incrementTrivialEditCounter } from "./freshness-BV3PQkDB.mjs";
|
|
15
15
|
import { n as toRefMeta, t as loadRefs } from "./loader-AGz4nK7d.mjs";
|
|
16
|
-
import { a as ensureStateDir, c as stateFilePath, i as apexStateDir, l as acquireLock, n as taskCreate, o as loadState, r as taskStart, s as saveState, t as taskComplete } from "./state-9lWvvWk8.mjs";
|
|
17
16
|
import { a as formatPath, c as colors, d as GRADIENT_BLOCKS, f as PROGRESS_BAR_DEFAULTS, i as formatCost, l as progressiveColor, m as TIME_INTERVALS, n as generateProgressBar, o as formatTimeLeft, p as PROGRESS_CHARS, r as formatBasename, s as formatTokens, t as generateGradientBar, u as COLOR_THRESHOLDS } from "./statusline-D87eUNXl.mjs";
|
|
18
|
-
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, COLOR_THRESHOLDS, CRITICAL_PATTERNS, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, DEV_VERBS, EXCLUDE_DIRS, FAIL_CLOSED, FILE_REDIRECT, GIT_ASK, GIT_BLOCKED, GO_DECL_RE, GRADIENT_BLOCKS, GUARDS, HOME_DIR, JAVA_DECL_RE, MAX_EXA_RESULTS, MAX_LINES_ENV_KEY, MAX_TOKENS, PHP_DECL_RE, PLUGINS_DIR, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, SAFE_PREFIXES, SESSION_STATE_FRAGMENT, SKILL_TRIGGERS, SOLID_REF, STATE_GITIGNORE, STATE_ROOT, SWIFT_PROTO_RE, SYSTEM_INSTALL, TIME_INTERVALS, TS_DECL_RE, TTL_ENV_KEY,
|
|
17
|
+
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, COLOR_THRESHOLDS, CRITICAL_PATTERNS, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, DEV_VERBS, EXCLUDE_DIRS, FAIL_CLOSED, FILE_REDIRECT, GIT_ASK, GIT_BLOCKED, GO_DECL_RE, GRADIENT_BLOCKS, GUARDS, HOME_DIR, JAVA_DECL_RE, MAX_EXA_RESULTS, MAX_LINES_ENV_KEY, MAX_TOKENS, PHP_DECL_RE, PLUGINS_DIR, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, RALPH_SAFE, SAFE_PREFIXES, SESSION_STATE_FRAGMENT, SKILL_TRIGGERS, SOLID_REF, STATE_GITIGNORE, STATE_ROOT, SWIFT_PROTO_RE, SYSTEM_INSTALL, TIME_INTERVALS, TS_DECL_RE, TTL_ENV_KEY, addRoot, bashWriteGuard, brainstormGate, buildApexInstruction, buildApexTaskContext, buildApexTaskInjection, buildClaudeMdContext, cacheLookup, cacheLookupMeta, cacheLookupSubstring, cacheLookupSubstringMeta, cachePath, cacheStore, capVerbosity, clearUserGuards, colors, compactJson, compactMarkdown, countFrameworkCodeLines, countLines, descFromText, detectClaudeMdProjectType, detectCreationIntent, detectFramework, detectHarness, detectMode, detectModularArchitecture, detectProjectType, detectRequiredSkills, docConsultedGate, ensureMemoryGitignore, envCandidates, evaluate, evaluateApex, evaluateFileSize, extractText, firstComment, firstHeading, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, frameworkSolidGate, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, installGuard, interfaceSeparationGuard, isApexCommand, isCodeFile, isDocConsulted, isHtmlLike, isRalphMode, jaccardSimilar, lessonsFileFor, loadApexTaskState, loadDotenv, loadIndex, loadRefs, matchPatterns, mcpCacheKey, mcpCacheWrite, missingSeoElements, modeFor, nowStamp, parseBodyDesc, parseEnrichment, parseEntry, parseEnvFile, parseEnvInt, parseField, parseFrontmatter, progressiveColor, projectLayout, projectRoot, projectRootOrNull, protectedPathGuard, queryHash, readRoots, readState, registerGuard, registryFile, requiredArchSkill, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, runGuards, scoreReferences, securityGuard, setStateField, skillTriggerGate, solidReadGate, splitTarget, stateFileFor, summarizeIndex, throttleMs, toRefMeta, ttlLabel, usesTailwindUtilities, walkUpFor, webfetchCacheWrite };
|
package/dist/policy/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $ as ASK_PATTERNS, A as FAIL_CLOSED, At as
|
|
2
|
-
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, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, type PolicyContext, type PolicyResult, ProjectType, 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, loadApexTaskState, matchPatterns, missingSeoElements, parseBodyDesc, parseEnrichment, parseEntry, parseField, protectedPathGuard, registerGuard, requiredArchSkill, runGuards, securityGuard, skillTriggerGate, solidReadGate, usesTailwindUtilities };
|
|
1
|
+
import { $ as ASK_PATTERNS, A as FAIL_CLOSED, At as DEV_KEYWORDS, B as SWIFT_PROTO_RE, C as usesTailwindUtilities, Ct as FileSizeVerdict, D as MAX_TOKENS, Dt as countLines, E as MAX_EXA_RESULTS, Et as countFrameworkCodeLines, F as installGuard, Ft as isApexCommand, G as CODE_MUTATORS, H as interfaceSeparationGuard, I as GO_DECL_RE, It as requiredArchSkill, J as SAFE_PREFIXES, K as CODE_REDIRECT, L as JAVA_DECL_RE, M as clearUserGuards, Mt as ProjectType, N as registerGuard, Nt as detectModularArchitecture, O as capVerbosity, Ot as evaluateFileSize, P as runGuards, Pt as detectProjectType, Q as protectedPathGuard, R as PHP_DECL_RE, S as skillTriggerGate, St as matchPatterns, T as frameworkSolidGate, Tt as SOLID_REF, U as bashWriteGuard, V as TS_DECL_RE, W as ASK_WRITERS, X as PROTECTED_FRAGMENTS, Y as SESSION_STATE_FRAGMENT, Z as PROTECTED_GIT_RE, _ as DEV_VERBS, _t as GIT_BLOCKED, a as firstHeading, at as APEX_GATES, b as detectClaudeMdProjectType, bt as SYSTEM_INSTALL, c as parseEntry, ct as brainstormGate, d as EXCLUDE_DIRS, dt as freshnessGate, et as CRITICAL_PATTERNS, f as PROJECT_INDICATORS, ft as solidReadGate, g as loadApexTaskState, gt as GIT_ASK, h as buildApexTaskInjection, ht as PolicyResult, i as firstComment, it as GuardContext, j as GUARDS, jt as ModularArchitecture, k as detectCreationIntent, kt as detectFramework, l as parseBodyDesc, lt as docConsultedGate, m as buildApexTaskContext, mt as PolicyContext, n as missingSeoElements, nt as securityGuard, o as TreeEntry, ot as ApexContext, p as ApexTaskState, pt as evaluate, q as FILE_REDIRECT, r as descFromText, rt as Guard, s as parseEnrichment, st as ApexGate, t as isHtmlLike, tt as LabeledPattern, u as parseField, ut as evaluateApex, v as buildApexInstruction, vt as PROJECT_INSTALL, w as SKILL_TRIGGERS, wt as PLUGINS_DIR, x as detectRequiredSkills, xt as isRalphMode, y as buildClaudeMdContext, yt as RALPH_SAFE, z as PY_MODEL_RE } from "../index-BqfOFKm7.mjs";
|
|
2
|
+
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, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, type PolicyContext, type PolicyResult, ProjectType, RALPH_SAFE, 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,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import { A as PROJECT_INSTALL, C as PROTECTED_GIT_RE, D as securityGuard, E as CRITICAL_PATTERNS, F as
|
|
1
|
+
import { A as MAX_EXA_RESULTS, B as detectFramework, D as frameworkSolidGate, E as SKILL_TRIGGERS, F as brainstormGate, G as requiredArchSkill, H as detectModularArchitecture, I as docConsultedGate, L as evaluateApex, M as capVerbosity, N as detectCreationIntent, P as APEX_GATES, R as freshnessGate, S as usesTailwindUtilities, U as detectProjectType, V as DEV_KEYWORDS, W as isApexCommand, a as firstHeading, b as detectRequiredSkills, c as EXCLUDE_DIRS, d as buildApexTaskInjection, f as loadApexTaskState, g as detectClaudeMdProjectType, h as buildClaudeMdContext, i as firstComment, j 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, v as parseBodyDesc, x as skillTriggerGate, y as parseField, z as solidReadGate } from "../validate-ebDjIyOa.mjs";
|
|
2
|
+
import { A as PROJECT_INSTALL, C as PROTECTED_GIT_RE, D as securityGuard, E as CRITICAL_PATTERNS, F as PLUGINS_DIR, I as SOLID_REF, L as countFrameworkCodeLines, M as SYSTEM_INSTALL, N as isRalphMode, O as GIT_ASK, P as matchPatterns, R as countLines, S as PROTECTED_FRAGMENTS, T as ASK_PATTERNS, _ as CODE_MUTATORS, a as registerGuard, b as SAFE_PREFIXES, c as GO_DECL_RE, d as PY_MODEL_RE, f as SWIFT_PROTO_RE, g as ASK_WRITERS, h as bashWriteGuard, i as clearUserGuards, j as RALPH_SAFE, k as GIT_BLOCKED, l as JAVA_DECL_RE, m as interfaceSeparationGuard, n as FAIL_CLOSED, o as runGuards, p as TS_DECL_RE, r as GUARDS, s as installGuard, t as evaluate, u as PHP_DECL_RE, v as CODE_REDIRECT, w as protectedPathGuard, x as SESSION_STATE_FRAGMENT, y as FILE_REDIRECT, z as evaluateFileSize } from "../evaluate-qYC5xClb.mjs";
|
|
3
3
|
import "../policy-la_KkjCS.mjs";
|
|
4
|
-
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, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_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, loadApexTaskState, matchPatterns, missingSeoElements, parseBodyDesc, parseEnrichment, parseEntry, parseField, protectedPathGuard, registerGuard, requiredArchSkill, runGuards, securityGuard, skillTriggerGate, solidReadGate, usesTailwindUtilities };
|
|
4
|
+
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, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, RALPH_SAFE, 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 };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as isCodeFile } from "./project-root-3kk7gCOp.mjs";
|
|
2
|
-
import { t as evaluate } from "./evaluate-
|
|
2
|
+
import { t as evaluate } from "./evaluate-qYC5xClb.mjs";
|
|
3
3
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
4
4
|
import { execSync } from "node:child_process";
|
|
5
5
|
//#region src/cli/run.ts
|
package/dist/runtime/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as Prompt } from "../types-D56jSgD9.mjs";
|
|
2
2
|
import { t as RefMeta } from "../types-CY5qT2X1.mjs";
|
|
3
|
-
import { t as AgentQuality } from "../session-state-
|
|
3
|
+
import { t as AgentQuality } from "../session-state-V8Jp-KE5.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/runtime/paths.d.ts
|
|
6
6
|
/**
|
|
@@ -53,6 +53,7 @@ type Activity = {
|
|
|
53
53
|
} | {
|
|
54
54
|
kind: "ref";
|
|
55
55
|
path: string;
|
|
56
|
+
ts?: number;
|
|
56
57
|
};
|
|
57
58
|
/** Apply an activity to a session's track and persist it (PostToolUse path). */
|
|
58
59
|
declare function recordActivity(file: string, activity: Activity): Promise<void>;
|
|
@@ -96,6 +97,8 @@ interface GateInput {
|
|
|
96
97
|
windowMs?: number;
|
|
97
98
|
isReplaceAll?: boolean;
|
|
98
99
|
agentType?: string;
|
|
100
|
+
/** Claude `agent_id` when the tool-use comes from a subagent (parity require-apex-agents.py:41 — subagents inherit the lead's brainstorm decision). */
|
|
101
|
+
agentId?: string;
|
|
99
102
|
/** Absolute path to the session transcript (Claude `transcript_path`) for evidence-based freshness. */
|
|
100
103
|
transcriptPath?: string;
|
|
101
104
|
}
|
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-mKZxP4oZ.mjs";
|
|
3
|
-
import { $ as saveApexState, A as trackSkillRead, At as securityStateDir, B as writePluginMap, C as seoPostToolUse, Ct as defaultStateDir, D as postTrackingSideEffects, Dt as isoUtc, E as securityAdvisory, Et as normalizeEvent, F as dispatchLessons, G as mergeLines, H as isProject, I as lessonsFileFor, J as listChildren, K as countFiles, L as lessonsStateFileFor, M as dispatchLifecycle, Mt as todayUtc, N as aipilotPostToolUse, O as trackWatchResearch, Ot as loadSecurityState, P as dispatchAipilot, Q as cleanupSession, R as cartoSessionStart, S as postEditContext, St as taskContext, T as dispatchMemory, Tt as trackFile, U as writeTree, V as generateProjectMap, W as loadEnriched, X as trackSessionChanges, Y as postEditTypescript, Z as validateRulesLoaded, _ as preCommitGate, _t as trimLogFile, a as recordActivity, at as validateSolidGate, b as extractSymbols, bt as projectContext, c as MCP_TTL_MS, ct as detectSolidProfile, d as isMcpTool, dt as readRules, et as logToolFailure, f as queryOf, ft as runSessionStartCleanups, g as gate, gt as removeOldFiles, h as TRIVIAL_BUDGET, ht as purgeTtlTree, i as respond, it as validateTailwind, j as trackEnrichment, jt as securityStatePath, k as trackMcpResearch, kt as saveSecurityState, l as WEBFETCH_TTL_MS, lt as solidDetectStart, m as REQUIRED_AGENTS, mt as pruneEmptyDirs, n as activityFor, nt as trackAgentMemory, o as mcpPostStore, ot as checkFileSize, p as DEFAULT_WINDOW_MS, pt as sessionStartCore, q as getFileDesc, r as handlePre, rt as subagentCacheContext, s as mcpPreIntercept, st as countLoc, t as handleHook, tt as validateTeammateOutput, u as cacheQueryOf, ut as injectRules, v as detectDuplication, vt as devContext, w as seoPostToolUseResponse, wt as projectHash, x as lifecycleStdout, xt as promptSubmitContext, y as dryGate, yt as gitContext, z as generateEcosystemMap } from "../handle-
|
|
3
|
+
import { $ as saveApexState, A as trackSkillRead, At as securityStateDir, B as writePluginMap, C as seoPostToolUse, Ct as defaultStateDir, D as postTrackingSideEffects, Dt as isoUtc, E as securityAdvisory, Et as normalizeEvent, F as dispatchLessons, G as mergeLines, H as isProject, I as lessonsFileFor, J as listChildren, K as countFiles, L as lessonsStateFileFor, M as dispatchLifecycle, Mt as todayUtc, N as aipilotPostToolUse, O as trackWatchResearch, Ot as loadSecurityState, P as dispatchAipilot, Q as cleanupSession, R as cartoSessionStart, S as postEditContext, St as taskContext, T as dispatchMemory, Tt as trackFile, U as writeTree, V as generateProjectMap, W as loadEnriched, X as trackSessionChanges, Y as postEditTypescript, Z as validateRulesLoaded, _ as preCommitGate, _t as trimLogFile, a as recordActivity, at as validateSolidGate, b as extractSymbols, bt as projectContext, c as MCP_TTL_MS, ct as detectSolidProfile, d as isMcpTool, dt as readRules, et as logToolFailure, f as queryOf, ft as runSessionStartCleanups, g as gate, gt as removeOldFiles, h as TRIVIAL_BUDGET, ht as purgeTtlTree, i as respond, it as validateTailwind, j as trackEnrichment, jt as securityStatePath, k as trackMcpResearch, kt as saveSecurityState, l as WEBFETCH_TTL_MS, lt as solidDetectStart, m as REQUIRED_AGENTS, mt as pruneEmptyDirs, n as activityFor, nt as trackAgentMemory, o as mcpPostStore, ot as checkFileSize, p as DEFAULT_WINDOW_MS, pt as sessionStartCore, q as getFileDesc, r as handlePre, rt as subagentCacheContext, s as mcpPreIntercept, st as countLoc, t as handleHook, tt as validateTeammateOutput, u as cacheQueryOf, ut as injectRules, v as detectDuplication, vt as devContext, w as seoPostToolUseResponse, wt as projectHash, x as lifecycleStdout, xt as promptSubmitContext, y as dryGate, yt as gitContext, z as generateEcosystemMap } from "../handle-CCV0ycdr.mjs";
|
|
4
4
|
//#region src/runtime/storage.ts
|
|
5
5
|
/**
|
|
6
6
|
* The project's single state dir (`<root>/.harness`) — neutral + harness-agnostic,
|
|
@@ -7,6 +7,15 @@ type AgentQuality = "sufficient" | "insufficient";
|
|
|
7
7
|
interface SessionTrack {
|
|
8
8
|
authorizations: Record<string, AuthEntry>;
|
|
9
9
|
refsRead: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Epoch-ms timestamp of each reference read, keyed by `refsRead` path (parity
|
|
12
|
+
* track-solid-reads.py `solid_reads[].timestamp`). Optional: tracks persisted
|
|
13
|
+
* before this field existed only carry `refsRead`, and such paths count as
|
|
14
|
+
* read (backward compat). PARITY: only the SOLID-read gate TTL-checks these —
|
|
15
|
+
* Python TTL-izes SOLID reads exclusively (require-solid-read.py); the other
|
|
16
|
+
* refsRead consumers (skill-trigger/shadcn/tailwind/design) stay session-scoped.
|
|
17
|
+
*/
|
|
18
|
+
refsReadAt?: Record<string, number>;
|
|
10
19
|
agents: {
|
|
11
20
|
name: string;
|
|
12
21
|
ts: number;
|
|
@@ -19,8 +28,15 @@ interface SessionTrack {
|
|
|
19
28
|
declare function emptyTrack(): SessionTrack;
|
|
20
29
|
/** Record a doc consultation (Context7/Exa) for a framework in this session. Immutable. */
|
|
21
30
|
declare function recordDoc(track: SessionTrack, framework: string, sessionId: string, source: string): SessionTrack;
|
|
22
|
-
/**
|
|
23
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Record that a SOLID reference file was read (deduped). Immutable. When `now`
|
|
33
|
+
* (epoch ms — the tool event's own timestamp, never a fresh `Date.now()`) is
|
|
34
|
+
* supplied, the read is stamped in `refsReadAt`, refreshed on re-reads so the
|
|
35
|
+
* LATEST read drives the SOLID TTL (parity track-solid-reads.py, which appends
|
|
36
|
+
* a timestamped entry per read; require-solid-read.py checks the most recent).
|
|
37
|
+
* Callers that omit `now` keep the legacy untimestamped behavior.
|
|
38
|
+
*/
|
|
39
|
+
declare function recordRefRead(track: SessionTrack, path: string, now?: number): SessionTrack;
|
|
24
40
|
/** Record an agent/tool call with a timestamp + optional quality. Immutable. */
|
|
25
41
|
declare function recordAgent(track: SessionTrack, name: string, ts: number, quality?: AgentQuality): SessionTrack;
|
|
26
42
|
/** True when ALL of `names` ran within `windowMs` with non-insufficient quality. */
|
|
@@ -32,11 +32,27 @@ function recordDoc(track, framework, sessionId, source) {
|
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Record that a SOLID reference file was read (deduped). Immutable. When `now`
|
|
37
|
+
* (epoch ms — the tool event's own timestamp, never a fresh `Date.now()`) is
|
|
38
|
+
* supplied, the read is stamped in `refsReadAt`, refreshed on re-reads so the
|
|
39
|
+
* LATEST read drives the SOLID TTL (parity track-solid-reads.py, which appends
|
|
40
|
+
* a timestamped entry per read; require-solid-read.py checks the most recent).
|
|
41
|
+
* Callers that omit `now` keep the legacy untimestamped behavior.
|
|
42
|
+
*/
|
|
43
|
+
function recordRefRead(track, path, now) {
|
|
44
|
+
const refsRead = track.refsRead.includes(path) ? track.refsRead : [...track.refsRead, path];
|
|
45
|
+
if (now === void 0) return refsRead === track.refsRead ? track : {
|
|
46
|
+
...track,
|
|
47
|
+
refsRead
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
38
50
|
...track,
|
|
39
|
-
refsRead
|
|
51
|
+
refsRead,
|
|
52
|
+
refsReadAt: {
|
|
53
|
+
...track.refsReadAt,
|
|
54
|
+
[path]: now
|
|
55
|
+
}
|
|
40
56
|
};
|
|
41
57
|
}
|
|
42
58
|
/** Record an agent/tool call with a timestamp + optional quality. Immutable. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as recordAgent, c as recordRefRead, i as emptyTrack, l as recordTrivialEdit, n as SessionTrack, o as recordBrainstormRequired, r as agentsFresh, s as recordDoc, t as AgentQuality, u as trivialCount } from "../session-state-
|
|
1
|
+
import { a as recordAgent, c as recordRefRead, i as emptyTrack, l as recordTrivialEdit, n as SessionTrack, o as recordBrainstormRequired, r as agentsFresh, s as recordDoc, t as AgentQuality, u as trivialCount } from "../session-state-V8Jp-KE5.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/tracking/store.d.ts
|
|
4
4
|
/**
|
package/dist/tracking/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as emptyTrack, c as recordDoc, d as trivialCount, i as agentsFresh, l as recordRefRead, n as saveTrack, o as recordAgent, s as recordBrainstormRequired, t as loadTrack, u as recordTrivialEdit } from "../store-
|
|
1
|
+
import { a as emptyTrack, c as recordDoc, d as trivialCount, i as agentsFresh, l as recordRefRead, n as saveTrack, o as recordAgent, s as recordBrainstormRequired, t as loadTrack, u as recordTrivialEdit } from "../store-ARx-GQSQ.mjs";
|
|
2
2
|
export { agentsFresh, emptyTrack, loadTrack, recordAgent, recordBrainstormRequired, recordDoc, recordRefRead, recordTrivialEdit, saveTrack, trivialCount };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { r as resolveMaxLines } from "./limits-CHn8AIL1.mjs";
|
|
2
2
|
import { c as ttlLabel, t as HOME_DIR } from "./dotenv-B9nM4cuQ.mjs";
|
|
3
3
|
import { t as detectHarness } from "./harness-C8Nxxyn_.mjs";
|
|
4
|
-
import { F as
|
|
4
|
+
import { F as PLUGINS_DIR, I as SOLID_REF, L as countFrameworkCodeLines } from "./evaluate-qYC5xClb.mjs";
|
|
5
5
|
import { r as isDocConsulted, t as formatDocDeny } from "./doc-helpers-CWZegVdR.mjs";
|
|
6
6
|
import { t as routeReferences } from "./router-PKVNBHge.mjs";
|
|
7
7
|
import { extname, join } from "node:path";
|
|
@@ -95,7 +95,7 @@ function detectProjectType(dir) {
|
|
|
95
95
|
*/
|
|
96
96
|
function detectFramework(filePath, content) {
|
|
97
97
|
if (/\.(tsx?|jsx?|vue|svelte)$/.test(filePath) || /from ['"]react|useState|className=/.test(content)) {
|
|
98
|
-
if (/(page|layout|loading|error|route)\.(ts|tsx)$/.test(filePath) || /use client|use server/.test(content)) return "nextjs";
|
|
98
|
+
if (/(page|layout|loading|error|route|middleware)\.(ts|tsx)$/.test(filePath) || /use client|use server|NextRequest|NextResponse|from ['"]next|getServerSideProps|getStaticProps/.test(content)) return "nextjs";
|
|
99
99
|
return "react";
|
|
100
100
|
}
|
|
101
101
|
if (/\.swift$/.test(filePath)) return "swift";
|
|
@@ -109,11 +109,26 @@ function detectFramework(filePath, content) {
|
|
|
109
109
|
}
|
|
110
110
|
//#endregion
|
|
111
111
|
//#region src/policy/apex-gates.ts
|
|
112
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* True when the SOLID ref read at `path` still counts. Parity require-solid-read.py
|
|
114
|
+
* `_check_solid_read` (FUSE_ENFORCE_TTL_SEC, default 120s): a SOLID read older than
|
|
115
|
+
* the window has expired and must be redone. PARITY NOTE: the TTL applies ONLY to
|
|
116
|
+
* this gate — Python TTL-izes SOLID reads exclusively; the other refsRead consumers
|
|
117
|
+
* (skillTriggerGate, shadcn/tailwind/design gates) stay session-scoped, so this
|
|
118
|
+
* predicate must never leak into them. Backward compat: a path with no `refsReadAt`
|
|
119
|
+
* stamp (tracks recorded before the field existed) or a context without a clock
|
|
120
|
+
* (`now` absent) counts as read.
|
|
121
|
+
*/
|
|
122
|
+
function refReadFresh(ctx, path, windowMs) {
|
|
123
|
+
const at = ctx.refsReadAt?.[path];
|
|
124
|
+
return at === void 0 || ctx.now === void 0 || ctx.now - at < windowMs;
|
|
125
|
+
}
|
|
126
|
+
/** Gate: the routed SOLID references for this edit must have been read within the TTL. */
|
|
113
127
|
const solidReadGate = (ctx) => {
|
|
114
128
|
if (!ctx.refs?.length) return null;
|
|
115
129
|
const routed = routeReferences(ctx.refs, ctx.filePath, ctx.content);
|
|
116
|
-
const
|
|
130
|
+
const windowMs = ctx.windowMs ?? 120 * 1e3;
|
|
131
|
+
const ttl = ttlLabel(Math.round(windowMs / 1e3));
|
|
117
132
|
if (!routed) {
|
|
118
133
|
const ref = SOLID_REF[ctx.framework] ?? "generic/";
|
|
119
134
|
return {
|
|
@@ -123,7 +138,7 @@ const solidReadGate = (ctx) => {
|
|
|
123
138
|
actions: [`Read ${PLUGINS_DIR}/${ref}SKILL.md`]
|
|
124
139
|
};
|
|
125
140
|
}
|
|
126
|
-
const read = new Set(ctx.refsRead ?? []);
|
|
141
|
+
const read = new Set((ctx.refsRead ?? []).filter((p) => refReadFresh(ctx, p, windowMs)));
|
|
127
142
|
const missing = !!routed.skillPath && read.has(routed.skillPath) ? [] : routed.required.map((r) => r.meta.filePath).filter((p) => !read.has(p));
|
|
128
143
|
if (missing.length === 0) return null;
|
|
129
144
|
const optional = routed.optional.map((r) => r.meta.filePath);
|
|
@@ -232,8 +247,8 @@ function capVerbosity(tool, input) {
|
|
|
232
247
|
}
|
|
233
248
|
//#endregion
|
|
234
249
|
//#region src/policy/framework-solid-exclude.ts
|
|
235
|
-
/** Build
|
|
236
|
-
const JS_EXCLUDE_RE = /(node_modules|dist|build|\.next)/;
|
|
250
|
+
/** Build/dep paths excluded from React/Next.js SOLID gating — segment-anchored (parity `r"/(node_modules|dist|build|\.next)/"`), NOT a bare substring, so `distance.ts`/`rebuild/` stay gated. */
|
|
251
|
+
const JS_EXCLUDE_RE = /(^|\/)(node_modules|dist|build|\.next)(\/|$)/;
|
|
237
252
|
/** Vendored dependency paths excluded from Laravel/PHP SOLID gating. */
|
|
238
253
|
const PHP_EXCLUDE_RE = /\/vendor\//;
|
|
239
254
|
/** Derived/build artifact paths excluded from Swift SOLID gating. */
|
|
@@ -945,6 +960,12 @@ function usesTailwindUtilities(filePath, content) {
|
|
|
945
960
|
* @returns a `block` Prompt naming the missing sub-skills, or `null` when satisfied.
|
|
946
961
|
*/
|
|
947
962
|
function skillTriggerGate(framework, content, refsRead, forcedSkill, cwd, filePath) {
|
|
963
|
+
if (forcedSkill === "fusecore" && !refsRead.some((r) => r.includes("skills/fusecore/"))) return {
|
|
964
|
+
kind: "block",
|
|
965
|
+
title: "FuseCore skill not consulted",
|
|
966
|
+
reason: "BLOCKED: FuseCore project detected. Read the FuseCore skill before writing code in FuseCore modules.",
|
|
967
|
+
actions: [`Read ${resolveSkillPath("fusecore")}`]
|
|
968
|
+
};
|
|
948
969
|
let required = detectRequiredSkills(framework, content);
|
|
949
970
|
if (framework !== "tailwind" && filePath && usesTailwindUtilities(filePath, content)) required = [...required, ...detectRequiredSkills("tailwind", content)];
|
|
950
971
|
if (forcedSkill && !required.includes(forcedSkill)) required.push(forcedSkill);
|
|
@@ -1243,7 +1264,7 @@ function buildClaudeMdContext(prompt, cwd) {
|
|
|
1243
1264
|
} catch {
|
|
1244
1265
|
return null;
|
|
1245
1266
|
}
|
|
1246
|
-
if (!DEV_VERBS.test(prompt)) return `# CLAUDE.md\n${claudeContent}`;
|
|
1267
|
+
if (!DEV_VERBS.test(prompt) && !DEV_KEYWORDS.test(prompt)) return `# CLAUDE.md\n${claudeContent}`;
|
|
1247
1268
|
return `${buildApexInstruction(detectClaudeMdProjectType(cwd), resolveMaxLines())}\n\n# CLAUDE.md\n${claudeContent}`;
|
|
1248
1269
|
}
|
|
1249
1270
|
//#endregion
|
|
@@ -1539,4 +1560,4 @@ function missingSeoElements(html) {
|
|
|
1539
1560
|
return missing;
|
|
1540
1561
|
}
|
|
1541
1562
|
//#endregion
|
|
1542
|
-
export {
|
|
1563
|
+
export { MAX_EXA_RESULTS as A, detectFramework as B, resolveSkillPath as C, frameworkSolidGate as D, SKILL_TRIGGERS as E, brainstormGate as F, requiredArchSkill as G, detectModularArchitecture as H, docConsultedGate as I, evaluateApex as L, capVerbosity as M, detectCreationIntent as N, isExcludedJsPath as O, APEX_GATES as P, freshnessGate as R, usesTailwindUtilities as S, readPluginMeta as T, detectProjectType as U, DEV_KEYWORDS as V, isApexCommand as W, scanPlugin as _, firstHeading as a, detectRequiredSkills as b, EXCLUDE_DIRS as c, buildApexTaskInjection as d, loadApexTaskState as f, detectClaudeMdProjectType as g, buildClaudeMdContext as h, firstComment as i, MAX_TOKENS as j, isExcludedSwiftPath 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, parseBodyDesc as v, findMarketplacePlugins as w, skillTriggerGate as x, parseField 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.48",
|
|
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",
|
|
@@ -59,11 +59,6 @@
|
|
|
59
59
|
"types": "./dist/refs/index.d.mts",
|
|
60
60
|
"import": "./dist/refs/index.mjs"
|
|
61
61
|
},
|
|
62
|
-
"./state": {
|
|
63
|
-
"bun": "./src/state/index.ts",
|
|
64
|
-
"types": "./dist/state/index.d.mts",
|
|
65
|
-
"import": "./dist/state/index.mjs"
|
|
66
|
-
},
|
|
67
62
|
"./statusline": {
|
|
68
63
|
"bun": "./src/statusline/index.ts",
|
|
69
64
|
"types": "./dist/statusline/index.d.mts",
|
|
@@ -132,7 +127,7 @@
|
|
|
132
127
|
"test": "bun test",
|
|
133
128
|
"typecheck": "tsc --noEmit",
|
|
134
129
|
"docs:api": "typedoc",
|
|
135
|
-
"build": "tsdown src/index.ts src/config/index.ts src/util/index.ts src/detect/index.ts src/policy/index.ts src/prompt/index.ts src/memory/index.ts src/cache/index.ts src/freshness/index.ts src/refs/index.ts src/
|
|
130
|
+
"build": "tsdown src/index.ts src/config/index.ts src/util/index.ts src/detect/index.ts src/policy/index.ts src/prompt/index.ts src/memory/index.ts src/cache/index.ts src/freshness/index.ts src/refs/index.ts src/statusline/index.ts src/cli/index.ts src/cli/bin.ts src/init/index.ts src/tracking/index.ts src/runtime/index.ts src/adapters/claude/index.ts src/adapters/codex/index.ts src/adapters/cursor/index.ts src/adapters/cline/index.ts src/adapters/gemini/index.ts --dts --format esm --clean --out-dir dist",
|
|
136
131
|
"prepublishOnly": "bun test && tsc --noEmit && bun run build"
|
|
137
132
|
},
|
|
138
133
|
"publishConfig": {
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { t as AuthEntry } from "./doc-helpers-BNfYWvYv.mjs";
|
|
2
|
-
|
|
3
|
-
//#region src/state/lock.d.ts
|
|
4
|
-
/**
|
|
5
|
-
* Acquire a directory-based lock (atomic `mkdir`, EEXIST = already held) with a timeout.
|
|
6
|
-
* @returns a release function, or null if the lock could not be acquired in time.
|
|
7
|
-
*/
|
|
8
|
-
declare function acquireLock(lockDir: string, timeoutMs?: number): Promise<(() => Promise<void>) | null>;
|
|
9
|
-
//#endregion
|
|
10
|
-
//#region src/state/apex-state.d.ts
|
|
11
|
-
declare const DEFAULT_STATE: {
|
|
12
|
-
$schema: string;
|
|
13
|
-
description: string;
|
|
14
|
-
target: Record<string, string>;
|
|
15
|
-
authorizations: Record<string, AuthEntry & {
|
|
16
|
-
doc_consulted?: string;
|
|
17
|
-
}>;
|
|
18
|
-
};
|
|
19
|
-
/** APEX state shape. */
|
|
20
|
-
type ApexState = typeof DEFAULT_STATE;
|
|
21
|
-
/** APEX state directory under a home dir. */
|
|
22
|
-
declare function apexStateDir(home?: string): string;
|
|
23
|
-
/** Daily state file path: `<home>/.claude/logs/00-apex/<YYYY-MM-DD>-state.json`. */
|
|
24
|
-
declare function stateFilePath(home?: string, today?: string): string;
|
|
25
|
-
/** Ensure the state directory exists and return its path. */
|
|
26
|
-
declare function ensureStateDir(home?: string): Promise<string>;
|
|
27
|
-
/** Load APEX state, or a fresh default. */
|
|
28
|
-
declare function loadState(path: string): Promise<ApexState>;
|
|
29
|
-
/** Save APEX state. */
|
|
30
|
-
declare function saveState(path: string, state: ApexState): Promise<void>;
|
|
31
|
-
//#endregion
|
|
32
|
-
//#region src/state/task-helpers.d.ts
|
|
33
|
-
/** A task entry in task.json. */
|
|
34
|
-
interface ApexTask {
|
|
35
|
-
subject: string;
|
|
36
|
-
description: string;
|
|
37
|
-
status: string;
|
|
38
|
-
phase: string;
|
|
39
|
-
started_at?: string;
|
|
40
|
-
completed_at?: string;
|
|
41
|
-
created_at?: string;
|
|
42
|
-
doc_consulted: Record<string, unknown>;
|
|
43
|
-
files_modified: string[];
|
|
44
|
-
blockedBy?: string[];
|
|
45
|
-
}
|
|
46
|
-
/** Structure of `.claude/apex/task.json`. */
|
|
47
|
-
interface ApexTaskFile {
|
|
48
|
-
current_task: string;
|
|
49
|
-
created_at: string;
|
|
50
|
-
tasks: Record<string, ApexTask>;
|
|
51
|
-
}
|
|
52
|
-
/** Add a new pending task. */
|
|
53
|
-
declare function taskCreate(file: string, id: string, subject: string, desc: string): Promise<void>;
|
|
54
|
-
/** Mark a task in_progress (creating a stub if absent). */
|
|
55
|
-
declare function taskStart(file: string, id: string, subject?: string, desc?: string, blocked?: string): Promise<void>;
|
|
56
|
-
/** Mark a task completed. */
|
|
57
|
-
declare function taskComplete(file: string, id: string): Promise<void>;
|
|
58
|
-
//#endregion
|
|
59
|
-
export { taskStart as a, ensureStateDir as c, stateFilePath as d, acquireLock as f, taskCreate as i, loadState as l, ApexTaskFile as n, ApexState as o, taskComplete as r, apexStateDir as s, ApexTask as t, saveState as u };
|
package/dist/state/index.d.mts
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import { a as taskStart, c as ensureStateDir, d as stateFilePath, f as acquireLock, i as taskCreate, l as loadState, n as ApexTaskFile, o as ApexState, r as taskComplete, s as apexStateDir, t as ApexTask, u as saveState } from "../index-BA-SqNR7.mjs";
|
|
2
|
-
export { ApexState, ApexTask, ApexTaskFile, acquireLock, apexStateDir, ensureStateDir, loadState, saveState, stateFilePath, taskComplete, taskCreate, taskStart };
|
package/dist/state/index.mjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import { a as ensureStateDir, c as stateFilePath, i as apexStateDir, l as acquireLock, n as taskCreate, o as loadState, r as taskStart, s as saveState, t as taskComplete } from "../state-9lWvvWk8.mjs";
|
|
2
|
-
export { acquireLock, apexStateDir, ensureStateDir, loadState, saveState, stateFilePath, taskComplete, taskCreate, taskStart };
|
package/dist/state-9lWvvWk8.mjs
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { a as writeJsonFile, i as readJsonFile, n as ensureDir } from "./json-io-DisYd2fb.mjs";
|
|
2
|
-
import { mkdir, rmdir } from "node:fs/promises";
|
|
3
|
-
//#region src/state/lock.ts
|
|
4
|
-
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
5
|
-
/**
|
|
6
|
-
* Acquire a directory-based lock (atomic `mkdir`, EEXIST = already held) with a timeout.
|
|
7
|
-
* @returns a release function, or null if the lock could not be acquired in time.
|
|
8
|
-
*/
|
|
9
|
-
async function acquireLock(lockDir, timeoutMs = 5e3) {
|
|
10
|
-
const start = Date.now();
|
|
11
|
-
while (Date.now() - start < timeoutMs) try {
|
|
12
|
-
await mkdir(lockDir, { recursive: false });
|
|
13
|
-
return async () => {
|
|
14
|
-
try {
|
|
15
|
-
await rmdir(lockDir);
|
|
16
|
-
} catch {}
|
|
17
|
-
};
|
|
18
|
-
} catch {
|
|
19
|
-
await sleep(100);
|
|
20
|
-
}
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
//#endregion
|
|
24
|
-
//#region src/state/apex-state.ts
|
|
25
|
-
const DEFAULT_STATE = {
|
|
26
|
-
$schema: "apex-state-v1",
|
|
27
|
-
description: "APEX/SOLID state - sessions[] + 2min expiry",
|
|
28
|
-
target: {},
|
|
29
|
-
authorizations: {}
|
|
30
|
-
};
|
|
31
|
-
/** APEX state directory under a home dir. */
|
|
32
|
-
function apexStateDir(home = process.env.HOME ?? "") {
|
|
33
|
-
return `${home}/.claude/logs/00-apex`;
|
|
34
|
-
}
|
|
35
|
-
/** Daily state file path: `<home>/.claude/logs/00-apex/<YYYY-MM-DD>-state.json`. */
|
|
36
|
-
function stateFilePath(home = process.env.HOME ?? "", today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)) {
|
|
37
|
-
return `${apexStateDir(home)}/${today}-state.json`;
|
|
38
|
-
}
|
|
39
|
-
/** Ensure the state directory exists and return its path. */
|
|
40
|
-
async function ensureStateDir(home) {
|
|
41
|
-
const dir = apexStateDir(home);
|
|
42
|
-
await ensureDir(dir);
|
|
43
|
-
return dir;
|
|
44
|
-
}
|
|
45
|
-
/** Load APEX state, or a fresh default. */
|
|
46
|
-
async function loadState(path) {
|
|
47
|
-
return await readJsonFile(path) ?? { ...DEFAULT_STATE };
|
|
48
|
-
}
|
|
49
|
-
/** Save APEX state. */
|
|
50
|
-
async function saveState(path, state) {
|
|
51
|
-
await writeJsonFile(path, state);
|
|
52
|
-
}
|
|
53
|
-
//#endregion
|
|
54
|
-
//#region src/state/task-helpers.ts
|
|
55
|
-
/** Add a new pending task. */
|
|
56
|
-
async function taskCreate(file, id, subject, desc) {
|
|
57
|
-
const data = await readJsonFile(file);
|
|
58
|
-
if (!data) return;
|
|
59
|
-
data.tasks[id] = {
|
|
60
|
-
subject,
|
|
61
|
-
description: desc,
|
|
62
|
-
status: "pending",
|
|
63
|
-
phase: "pending",
|
|
64
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
65
|
-
doc_consulted: {},
|
|
66
|
-
files_modified: [],
|
|
67
|
-
blockedBy: []
|
|
68
|
-
};
|
|
69
|
-
await writeJsonFile(file, data);
|
|
70
|
-
}
|
|
71
|
-
/** Mark a task in_progress (creating a stub if absent). */
|
|
72
|
-
async function taskStart(file, id, subject, desc, blocked) {
|
|
73
|
-
const data = await readJsonFile(file);
|
|
74
|
-
if (!data) return;
|
|
75
|
-
const task = data.tasks[id] ?? {
|
|
76
|
-
subject: "",
|
|
77
|
-
description: "",
|
|
78
|
-
status: "in_progress",
|
|
79
|
-
phase: "analyze",
|
|
80
|
-
doc_consulted: {},
|
|
81
|
-
files_modified: []
|
|
82
|
-
};
|
|
83
|
-
data.tasks[id] = task;
|
|
84
|
-
data.current_task = id;
|
|
85
|
-
task.status = "in_progress";
|
|
86
|
-
task.phase = "analyze";
|
|
87
|
-
task.started_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
88
|
-
if (subject) task.subject = subject;
|
|
89
|
-
if (desc) task.description = desc;
|
|
90
|
-
if (blocked) task.blockedBy = blocked.split(",");
|
|
91
|
-
await writeJsonFile(file, data);
|
|
92
|
-
}
|
|
93
|
-
/** Mark a task completed. */
|
|
94
|
-
async function taskComplete(file, id) {
|
|
95
|
-
const data = await readJsonFile(file);
|
|
96
|
-
const task = data?.tasks[id];
|
|
97
|
-
if (!data || !task) return;
|
|
98
|
-
task.status = "completed";
|
|
99
|
-
task.phase = "completed";
|
|
100
|
-
task.completed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
101
|
-
await writeJsonFile(file, data);
|
|
102
|
-
}
|
|
103
|
-
//#endregion
|
|
104
|
-
export { ensureStateDir as a, stateFilePath as c, apexStateDir as i, acquireLock as l, taskCreate as n, loadState as o, taskStart as r, saveState as s, taskComplete as t };
|