@fusengine/harness 0.1.79 → 0.1.80
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 +14 -1
- package/dist/adapters/kimi/index.d.mts +79 -0
- package/dist/adapters/kimi/index.mjs +2 -0
- package/dist/cli/bin.mjs +4 -4
- package/dist/config/index.d.mts +1 -1
- package/dist/config/index.mjs +1 -1
- package/dist/detect/index.d.mts +1 -1
- package/dist/detect/index.mjs +1 -1
- package/dist/{dotenv-Jj8aL1FL.mjs → dotenv-B26N_uGr.mjs} +2 -1
- package/dist/{handle-Dthtlp7Z.mjs → handle-B7YufFz9.mjs} +46 -23
- package/dist/{harness-Cb9xR8dC.mjs → harness-BMuLJ9lm.mjs} +4 -2
- package/dist/{harness-s1Fce_dL.d.mts → harness-BQ5_Wnva.d.mts} +1 -1
- package/dist/{index-jj_EzZRv.d.mts → index-B_VHbsaJ.d.mts} +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +3 -3
- package/dist/init/index.d.mts +1 -1
- package/dist/kimi-C7FGprQz.mjs +80 -0
- package/dist/policy/index.mjs +1 -1
- package/dist/runtime/index.d.mts +10 -3
- package/dist/runtime/index.mjs +1 -1
- package/dist/{skill-path-BO2N0XvB.mjs → skill-path-CDvR4bbF.mjs} +2 -2
- package/dist/{store-Bcn08y9m.mjs → store-nDZ3eLBQ.mjs} +2 -2
- package/dist/tracking/index.mjs +1 -1
- package/dist/{validate-DhOX5hDK.mjs → validate-CkmquRDV.mjs} +72 -15
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -130,6 +130,7 @@ also works elsewhere.
|
|
|
130
130
|
| **gemini-cli** | `BeforeTool` denies via `{decision:"deny",reason}` (`gemini/index.ts:22-36`) | none | Thin stateless adapter — no session track, no APEX gates wired through it. |
|
|
131
131
|
| **cline** | `PreToolUse` only; block → `{cancel:true}`, non-block → `contextModification` (`cline/index.ts:24-36`) | none | Same as gemini-cli: stateless guard only. |
|
|
132
132
|
| **hermes** | `pre_tool_call` proven: reuses the Claude stdin reader, blocks via `{decision:"block",reason}` (`hermes/index.ts:12-36`) | untested — no lifecycle dispatch wired for Hermes in this repo | `ask`/`inform` degrade to non-blocking `{context}` — Hermes "has no interactive ask state" (`hermes/index.ts:27-28`). |
|
|
133
|
+
| **kimi** | `PreToolUse` denies via the camelCase JSON channel `{"hookSpecificOutput":{"permissionDecision":"deny","permissionDecisionReason":"…"}}` on stdout at **exit 0** (verified live against kimi-code v0.27.0 — exit 2 with stderr = reason also blocks, but is not required) — only `deny` is documented. `ask` is **downgraded to deny** prefixed `[downgraded from ask — Kimi Code has no interactive approval]`; `inform` rides plain stdout text at exit 0, never wrapped in JSON. Blocking events: `UserPromptSubmit`, `PreToolUse`, `Stop` (`kimi/index.ts`). | Observation only: `PostToolUse`, `PostToolUseFailure`, `PermissionRequest`, `PermissionResult`, `SessionStart`, `SessionEnd`, `SubagentStart`, `SubagentStop`, `StopFailure`, `Interrupt`, `PreCompact`, `PostCompact`, `Notification` — Kimi delivers them but **ignores any response**, so no verdict can be returned from them. | A **hook** cannot request approval — Kimi's `ask` lives in a parallel, hook-unreachable system (`[[permission.rules]] decision = "ask"` in `config.toml`), hence the ask→deny downgrade. Hooks are configured **only** in the global `~/.kimi-code/config.toml` (no project-local hooks file), so `harness init` writes no kimi wiring — copy the TOML snippet from [docs/adapters.md](docs/adapters.md#kimi-code--manual-wiring), and emit **no field** beyond `event`/`matcher`/`command`/`timeout` or the whole config fails to load. **Fail-open by design**: any exit code other than 0/2, a timeout, or a crash lets the call through. Stdin payload is snake_case and carries only `hook_event_name`, `session_id`, `cwd`, `tool_name`, `tool_input.command` and an undocumented `tool_call_id` (unused here) — no `transcript_path`, no `permission_mode`, no `tool_response`. Verified live against kimi-code v0.27.0 for `PreToolUse`/`Bash`; validate hand-written config with `kimi doctor`. Instructions file is `AGENTS.md`, not `CLAUDE.md`. |
|
|
133
134
|
|
|
134
135
|
## What it enforces
|
|
135
136
|
|
|
@@ -191,6 +192,14 @@ Features shipped since 0.1.44, each with its own test:
|
|
|
191
192
|
version + drift vs. npm, `.claude/BOARD.md`, and the one-shot summary, each
|
|
192
193
|
collector isolated so one failure can't blank the rest
|
|
193
194
|
(`src/runtime/lifecycle/snapshot/index.ts`, `test/snapshot.test.ts`).
|
|
195
|
+
- **Target-aware root-doc injection** — `SessionStart` and `UserPromptSubmit`
|
|
196
|
+
inject the target's own root instructions doc, not a hardcoded `CLAUDE.md`:
|
|
197
|
+
`apexDocName(id)` + `harnessHomeSegment(id)` (`src/policy/apex-target.ts`)
|
|
198
|
+
resolve `AGENTS.md` under `.codex`/`.kimi-code` for Codex/Kimi, `CLAUDE.md`
|
|
199
|
+
under `.claude` for every other target (byte-identical to the pre-target-aware
|
|
200
|
+
default). The `systemMessage` label follows suit (`"AGENTS.md injected"`
|
|
201
|
+
under Codex/Kimi) (`src/runtime/lifecycle/session-start.ts`,
|
|
202
|
+
`src/runtime/inject-context.ts`).
|
|
194
203
|
- **Injection budget cap** — harness-produced context fragments (lessons,
|
|
195
204
|
snapshot, APEX task context) are capped at ~8000 chars each; owner-authored
|
|
196
205
|
content (CLAUDE.md/rules) is never capped
|
|
@@ -202,7 +211,11 @@ Features shipped since 0.1.44, each with its own test:
|
|
|
202
211
|
carrying `tool_input.agent_type` is recorded as `subagent-<agent_type>` in the
|
|
203
212
|
same SessionTrack as Claude Task evidence, so a Codex explore-codebase/sniper
|
|
204
213
|
satisfies the APEX freshness gates exactly like a Claude subagent
|
|
205
|
-
(`src/tracking/session-state.ts`, sim scenario 32b).
|
|
214
|
+
(`src/tracking/session-state.ts`, sim scenario 32b). Recognized
|
|
215
|
+
agent-launcher tools across targets: `Task` (Claude), `Agent`/`AgentSwarm`
|
|
216
|
+
(Kimi Code) — each credits its `subagent_type`/`agent_type` toward the same
|
|
217
|
+
freshness evidence (`src/runtime/activity.ts`,
|
|
218
|
+
`src/freshness/agent-evidence-record.ts`).
|
|
206
219
|
- **File-size gate judges the real Edit outcome, not the on-disk count** — an
|
|
207
220
|
Edit on a file already over `FUSE_SOLID_MAX_LINES` used to be denied
|
|
208
221
|
unconditionally (the on-disk count always won); the gate now computes the
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { t as Prompt } from "../../types-DVbIl9md.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/adapters/kimi/interfaces/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Kimi Code CLI shell-hook wire types. Verified 2026 against
|
|
6
|
+
* kimi.com/code/docs/en/kimi-code-cli/customization/hooks.html (v0.27.0):
|
|
7
|
+
* hooks are configured in `~/.kimi-code/config.toml` under `[[hooks]]`,
|
|
8
|
+
* receive a snake_case JSON payload on stdin, and answer with a camelCase
|
|
9
|
+
* JSON envelope on stdout (a real, documented casing inconsistency).
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* `PreToolUse`/`UserPromptSubmit`/`Stop` stdin payload (subset). Same base
|
|
13
|
+
* wire shape as Claude Code (`hook_event_name`/`tool_name`/`tool_input`/
|
|
14
|
+
* `cwd`); `tool_input.command` is confirmed for `Bash` by the docs' example
|
|
15
|
+
* script (`payload.tool_input?.command`). Verified live against kimi-code
|
|
16
|
+
* v0.27.0 on `PreToolUse`/`Bash`: `tool_name` is present at top level (the
|
|
17
|
+
* docs never name it as a stdin key) and `tool_input.command` carries the
|
|
18
|
+
* shell string. The live payload also carries an UNDOCUMENTED `tool_call_id`
|
|
19
|
+
* (e.g. `"tool_LsTnl0gBxM62XuWvKw0GFGHs"`); the harness does not consume it,
|
|
20
|
+
* so it is deliberately absent from this interface. `cwd` is the
|
|
21
|
+
* symlink-resolved path (macOS: `/private/tmp/…` for a shell in `/tmp/…`).
|
|
22
|
+
*/
|
|
23
|
+
interface KimiHookInput {
|
|
24
|
+
hook_event_name?: string;
|
|
25
|
+
tool_name?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Verified against the official tool reference (`docs/en/reference/tools.md`,
|
|
28
|
+
* moonshotai/kimi-code) AND live against kimi-code v0.27.0: `Write` sends
|
|
29
|
+
* `{ path, content, mode? }`, `Edit` sends
|
|
30
|
+
* `{ path, old_string, new_string, replace_all? }` — the path key is `path`,
|
|
31
|
+
* NOT Claude's `file_path`. `file_path` is kept as a tolerated alias (read
|
|
32
|
+
* second) for Claude-shaped replay payloads, mirroring
|
|
33
|
+
* `runtime/normalize.ts` which resolves both on the CLI path.
|
|
34
|
+
*/
|
|
35
|
+
tool_input?: {
|
|
36
|
+
path?: string;
|
|
37
|
+
file_path?: string;
|
|
38
|
+
content?: string;
|
|
39
|
+
new_string?: string;
|
|
40
|
+
command?: string | string[];
|
|
41
|
+
};
|
|
42
|
+
session_id?: string;
|
|
43
|
+
cwd?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Hook stdout response. Only `"deny"` is documented for `permissionDecision`
|
|
47
|
+
* — no `"ask"`/`"allow"` value, and no `additionalContext` field exists on
|
|
48
|
+
* this envelope. Blocking events are `UserPromptSubmit`, `PreToolUse`, and
|
|
49
|
+
* `Stop` only; every other event's return value is ignored.
|
|
50
|
+
*/
|
|
51
|
+
interface KimiResponse {
|
|
52
|
+
hookSpecificOutput?: {
|
|
53
|
+
permissionDecision?: "deny";
|
|
54
|
+
permissionDecisionReason?: string;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/adapters/kimi/index.d.ts
|
|
59
|
+
/** Read & parse the Kimi hook stdin payload (same wire shape as Claude for the fields we consume). */
|
|
60
|
+
declare function readKimiInput(): Promise<KimiHookInput>;
|
|
61
|
+
/**
|
|
62
|
+
* Render a portable {@link Prompt} as a Kimi hook response string:
|
|
63
|
+
* `block`/`ask` -> `{hookSpecificOutput:{permissionDecision:"deny",...}}`
|
|
64
|
+
* (only `"deny"` is documented; `ask` is downgraded with {@link ASK_PREFIX});
|
|
65
|
+
* `inform` -> raw text (Kimi appends stdout to context on exit 0 — no
|
|
66
|
+
* documented JSON envelope for non-blocking context injection).
|
|
67
|
+
* @param prompt - The portable policy prompt.
|
|
68
|
+
* @returns The native Kimi response string.
|
|
69
|
+
*/
|
|
70
|
+
declare function toKimiResponse(prompt: Prompt): string;
|
|
71
|
+
/**
|
|
72
|
+
* Run the bundled policy over a Kimi hook payload and return the native
|
|
73
|
+
* response string, or null to allow (the hook then exits 0 with empty stdout).
|
|
74
|
+
* @param input - Parsed hook stdin payload.
|
|
75
|
+
* @returns Native response string, or null when the tool use is allowed.
|
|
76
|
+
*/
|
|
77
|
+
declare function guard(input: KimiHookInput): string | null;
|
|
78
|
+
//#endregion
|
|
79
|
+
export { type KimiHookInput, type KimiResponse, guard, readKimiInput, toKimiResponse };
|
package/dist/cli/bin.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { r as loadDotenv, s as resolveTtlSec } from "../dotenv-
|
|
3
|
-
import { t as detectHarness } from "../harness-
|
|
2
|
+
import { r as loadDotenv, s as resolveTtlSec } from "../dotenv-B26N_uGr.mjs";
|
|
3
|
+
import { t as detectHarness } from "../harness-BMuLJ9lm.mjs";
|
|
4
4
|
import { t as claudeHome } from "../home-state-BXf38Zi1.mjs";
|
|
5
5
|
import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-3tyTW9Ud.mjs";
|
|
6
6
|
import { n as writeInitFile, t as initFor } from "../run-Do2JltgU.mjs";
|
|
7
|
-
import { $ as runningVersion, Q as runDoctor, Z as notify, et as versionBanner, t as handleHook, zt as todayUtc } from "../handle-
|
|
7
|
+
import { $ as runningVersion, Q as runDoctor, Z as notify, et as versionBanner, t as handleHook, zt as todayUtc } from "../handle-B7YufFz9.mjs";
|
|
8
8
|
import { p as readStdin$1 } from "../claude-51RSspJ1.mjs";
|
|
9
9
|
import { delimiter, join } from "node:path";
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
@@ -1032,7 +1032,7 @@ function maybePlaySound(argv) {
|
|
|
1032
1032
|
* harness changelog fetch + diff the Claude Code changelog, print a JSON summary (changelog-watcher)
|
|
1033
1033
|
* harness codex-rules generate a Codex execpolicy .rules (Starlark) file from security.ts; stdout or --out <path>
|
|
1034
1034
|
*/
|
|
1035
|
-
const hookDebug = process.env.FUSE_HARNESS_DEBUG === "1";
|
|
1035
|
+
const hookDebug = process.env.FUSE_HARNESS_DEBUG === "1" && process.env.CI === "true";
|
|
1036
1036
|
function traceHook(label, data) {
|
|
1037
1037
|
if (hookDebug) process.stderr.write(`[hook-debug] ${label}: ${typeof data === "string" ? data : JSON.stringify(data)}\n`);
|
|
1038
1038
|
}
|
package/dist/config/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as parseEnvInt, a as ProjectLayout, c as projectLayout, d as resolveMaxLines, f as splitTarget, g as ttlLabel, h as resolveTtlSec, i as parseEnvFile, l as DEFAULT_MAX_LINES, m as TTL_ENV_KEY, n as envCandidates, o as STATE_GITIGNORE, p as DEFAULT_TTL_SEC, r as loadDotenv, s as STATE_ROOT, t as HOME_DIR, u as MAX_LINES_ENV_KEY } from "../index-
|
|
1
|
+
import { _ as parseEnvInt, a as ProjectLayout, c as projectLayout, d as resolveMaxLines, f as splitTarget, g as ttlLabel, h as resolveTtlSec, i as parseEnvFile, l as DEFAULT_MAX_LINES, m as TTL_ENV_KEY, n as envCandidates, o as STATE_GITIGNORE, p as DEFAULT_TTL_SEC, r as loadDotenv, s as STATE_ROOT, t as HOME_DIR, u as MAX_LINES_ENV_KEY } from "../index-B_VHbsaJ.mjs";
|
|
2
2
|
export { DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, HOME_DIR, MAX_LINES_ENV_KEY, ProjectLayout, STATE_GITIGNORE, STATE_ROOT, TTL_ENV_KEY, envCandidates, loadDotenv, parseEnvFile, parseEnvInt, projectLayout, resolveMaxLines, resolveTtlSec, splitTarget, ttlLabel };
|
package/dist/config/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as parseEnvInt, i as splitTarget, n as MAX_LINES_ENV_KEY, r as resolveMaxLines, t as DEFAULT_MAX_LINES } from "../limits-CHn8AIL1.mjs";
|
|
2
|
-
import { a as DEFAULT_TTL_SEC, c as ttlLabel, i as parseEnvFile, n as envCandidates, o as TTL_ENV_KEY, r as loadDotenv, s as resolveTtlSec, t as HOME_DIR } from "../dotenv-
|
|
2
|
+
import { a as DEFAULT_TTL_SEC, c as ttlLabel, i as parseEnvFile, n as envCandidates, o as TTL_ENV_KEY, r as loadDotenv, s as resolveTtlSec, t as HOME_DIR } from "../dotenv-B26N_uGr.mjs";
|
|
3
3
|
import { n as STATE_ROOT, r as projectLayout, t as STATE_GITIGNORE } from "../layout-C0jaaCQC.mjs";
|
|
4
4
|
import "../config-la_KkjCS.mjs";
|
|
5
5
|
export { DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, HOME_DIR, MAX_LINES_ENV_KEY, STATE_GITIGNORE, STATE_ROOT, TTL_ENV_KEY, envCandidates, loadDotenv, parseEnvFile, parseEnvInt, projectLayout, resolveMaxLines, resolveTtlSec, splitTarget, ttlLabel };
|
package/dist/detect/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as HarnessInfo, i as HarnessId, n as detectMode, o as HarnessMode, r as modeFor, s as HarnessVia, t as detectHarness } from "../harness-
|
|
1
|
+
import { a as HarnessInfo, i as HarnessId, n as detectMode, o as HarnessMode, r as modeFor, s as HarnessVia, t as detectHarness } from "../harness-BQ5_Wnva.mjs";
|
|
2
2
|
export { type HarnessId, type HarnessInfo, type HarnessMode, type HarnessVia, detectHarness, detectMode, modeFor };
|
package/dist/detect/index.mjs
CHANGED
|
@@ -42,7 +42,8 @@ const HOME_DIR = {
|
|
|
42
42
|
cline: ".clinerules",
|
|
43
43
|
"gemini-cli": ".gemini",
|
|
44
44
|
opencode: ".opencode",
|
|
45
|
-
hermes: ".hermes"
|
|
45
|
+
hermes: ".hermes",
|
|
46
|
+
kimi: ".kimi-code"
|
|
46
47
|
};
|
|
47
48
|
/** Parse a `.env` file into a key→value map (`export KEY="v"` or `KEY=v`). */
|
|
48
49
|
function parseEnvFile(path) {
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import { a as parseEnvInt, i as splitTarget, r as resolveMaxLines } from "./limits-CHn8AIL1.mjs";
|
|
2
|
-
import { s as resolveTtlSec } from "./dotenv-
|
|
2
|
+
import { s as resolveTtlSec } from "./dotenv-B26N_uGr.mjs";
|
|
3
3
|
import { r as projectLayout } from "./layout-C0jaaCQC.mjs";
|
|
4
4
|
import { i as walkUpFor, n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./project-root-3kk7gCOp.mjs";
|
|
5
5
|
import { B as detectProjectType$1, H as requiredArchSkill, L as detectFramework, n as FAIL_CLOSED, t as evaluate, z as detectModularArchitecture } from "./evaluate-ClfbCDiY.mjs";
|
|
6
6
|
import { a as sanitizeSessionId, c as sessionsDir, d as countFrameworkCodeLines, f as countLines, i as loadSessionState, l as PLUGINS_DIR, n as fuseHarnessHome, o as saveSessionState, r as fusengineCache, t as claudeHome } from "./home-state-BXf38Zi1.mjs";
|
|
7
7
|
import { a as writeJsonFile, i as readJsonFile, r as hashText, t as atomicWrite } from "./json-io-DisYd2fb.mjs";
|
|
8
8
|
import { r as isDocConsulted } from "./doc-helpers-CWZegVdR.mjs";
|
|
9
|
-
import {
|
|
10
|
-
import { n as findMarketplacePlugins, r as readPluginMeta, t as resolveSkillPath } from "./skill-path-
|
|
9
|
+
import { D as frameworkSolidGate, F as POST_AUTH_GATES, I as PRE_AUTH_GATES, M as capVerbosity, N as detectCreationIntent, O as isExcludedJsPath, R as docConsultedGate, S as parseField, T as usesTailwindUtilities, b as scanPlugin, c as EXCLUDE_DIRS$1, d as buildApexTaskInjection, g as buildClaudeMdContext, k as isExcludedSwiftPath, l as PROJECT_INDICATORS, n as missingSeoElements, o as parseEnrichment, p as apexAgentSteps, r as descFromText, s as parseEntry, t as isHtmlLike, v as apexDocName, w as skillTriggerGate, y as harnessHomeSegment, z as evaluateApex } from "./validate-CkmquRDV.mjs";
|
|
10
|
+
import { n as findMarketplacePlugins, r as readPluginMeta, t as resolveSkillPath } from "./skill-path-CDvR4bbF.mjs";
|
|
11
11
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
12
12
|
import { a as nowStamp, l as throttleMs, n as readRoots, o as readState, s as setStateField, t as addRoot } from "./registry-CymilZiZ.mjs";
|
|
13
13
|
import { d as loadIndex, i as cacheLookupMeta, n as webfetchCacheWrite, o as cacheLookupSubstringMeta, t as mcpCacheWrite, u as extractText } from "./mcp-store-BkBDmuxN.mjs";
|
|
14
14
|
import { t as loadRefs } from "./loader-AGz4nK7d.mjs";
|
|
15
|
-
import { a as writeLastNonce, c as recordAgent, d as recordRefRead, f as recordTarget, h as apexAuthorizationGate, i as verifyTrack, l as recordBrainstormRequired, m as trivialCount, n as saveTrack, o as agentsFresh, p as recordTrivialEdit, r as signTrack, s as emptyTrack, t as loadTrack, u as recordDoc } from "./store-
|
|
15
|
+
import { a as writeLastNonce, c as recordAgent, d as recordRefRead, f as recordTarget, h as apexAuthorizationGate, i as verifyTrack, l as recordBrainstormRequired, m as trivialCount, n as saveTrack, o as agentsFresh, p as recordTrivialEdit, r as signTrack, s as emptyTrack, t as loadTrack, u as recordDoc } from "./store-nDZ3eLBQ.mjs";
|
|
16
16
|
import { n as parseApplyPatch, t as isBypassPermissions } from "./permission-mode-BN3MNgbm.mjs";
|
|
17
17
|
import { _ as writeText, d as collectFiles, f as pathExists, g as spawnCapture, h as sleep, i as denyResponse, l as systemMessage, m as readText, n as blockResponse, r as contextResponse, s as informResponse, t as attachSystemMessage, v as commandToString } from "./claude-51RSspJ1.mjs";
|
|
18
18
|
import { r as toHermesResponse } from "./hermes-rTmVdhUL.mjs";
|
|
19
|
+
import { r as toKimiResponse } from "./kimi-C7FGprQz.mjs";
|
|
19
20
|
import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
20
21
|
import { appendFileSync, copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, renameSync, rmSync, rmdirSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
|
|
21
22
|
import { homedir } from "node:os";
|
|
@@ -598,7 +599,7 @@ function promptSubmitContext(prompt, cwd, id = "claude-code") {
|
|
|
598
599
|
const ctx = buildClaudeMdContext(prompt, cwd, id);
|
|
599
600
|
if (!ctx) return "";
|
|
600
601
|
if (!oncePerWindow(claudeMdKey(prompt, ctx), 3e3)) return "";
|
|
601
|
-
return attachSystemMessage(contextResponse("UserPromptSubmit", ctx),
|
|
602
|
+
return attachSystemMessage(contextResponse("UserPromptSubmit", ctx), `${apexDocName(id)} injected`);
|
|
602
603
|
}
|
|
603
604
|
/**
|
|
604
605
|
* PreToolUse Task context injection: render the APEX sub-agent context as a
|
|
@@ -738,9 +739,15 @@ const PURGEABLE = {
|
|
|
738
739
|
doc: 48 * 3600,
|
|
739
740
|
explore: 48 * 3600
|
|
740
741
|
};
|
|
741
|
-
/**
|
|
742
|
-
|
|
743
|
-
|
|
742
|
+
/**
|
|
743
|
+
* Read the target's root instructions doc (`~/.claude/CLAUDE.md`,
|
|
744
|
+
* `~/.codex/AGENTS.md`, `~/.kimi-code/AGENTS.md`, ...), or "" when
|
|
745
|
+
* missing/unreadable.
|
|
746
|
+
* @param home - Home dir.
|
|
747
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
748
|
+
*/
|
|
749
|
+
function claudeMd(home, id = "claude-code") {
|
|
750
|
+
const path = join(home, harnessHomeSegment(id), apexDocName(id));
|
|
744
751
|
try {
|
|
745
752
|
return existsSync(path) ? readFileSync(path, "utf-8") : "";
|
|
746
753
|
} catch {
|
|
@@ -765,14 +772,15 @@ function runSessionStartCleanups(home = homedir(), now = Date.now()) {
|
|
|
765
772
|
* @param cwd - Project root for dev-context detection.
|
|
766
773
|
* @param home - Home dir (defaults to `~`).
|
|
767
774
|
* @param now - Clock for TTL cleanup (defaults to `Date.now()`).
|
|
775
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
768
776
|
* @returns The native hook stdout (possibly empty).
|
|
769
777
|
*/
|
|
770
|
-
function sessionStartCore(cwd, home = homedir(), now = Date.now()) {
|
|
771
|
-
const md = claudeMd(home);
|
|
778
|
+
function sessionStartCore(cwd, home = homedir(), now = Date.now(), id = "claude-code") {
|
|
779
|
+
const md = claudeMd(home, id);
|
|
772
780
|
const dev = devContext(cwd);
|
|
773
781
|
runSessionStartCleanups(home, now);
|
|
774
782
|
const ctx = [md, dev].filter(Boolean).join("\n");
|
|
775
|
-
return ctx ? attachSystemMessage(contextResponse("SessionStart", ctx),
|
|
783
|
+
return ctx ? attachSystemMessage(contextResponse("SessionStart", ctx), `${apexDocName(id)} injected`) : "";
|
|
776
784
|
}
|
|
777
785
|
//#endregion
|
|
778
786
|
//#region src/runtime/lifecycle/inject-rules.ts
|
|
@@ -1724,6 +1732,7 @@ const RESEARCH_TOOLS = /* @__PURE__ */ new Set([
|
|
|
1724
1732
|
"mcp__exa__deep_researcher_start",
|
|
1725
1733
|
"WebSearch",
|
|
1726
1734
|
"WebFetch",
|
|
1735
|
+
"FetchURL",
|
|
1727
1736
|
"mcp__fuse-browser__browser_fetch",
|
|
1728
1737
|
"mcp__fuse-browser__browser_fetch_batch",
|
|
1729
1738
|
"mcp__fuse-browser__browser_crawl",
|
|
@@ -1808,8 +1817,17 @@ function classifyExplore(tool, input) {
|
|
|
1808
1817
|
* session track the freshness gate scans FIRST — sidechain research and
|
|
1809
1818
|
* Workflow-spawned agents count, unlike the lead-transcript scan.
|
|
1810
1819
|
*/
|
|
1811
|
-
/**
|
|
1812
|
-
|
|
1820
|
+
/**
|
|
1821
|
+
* Tools the existing Task tracking already credits as agent LAUNCHES.
|
|
1822
|
+
* `AgentSwarm` is Kimi Code's batch launcher (`prompt_template` + `items`,
|
|
1823
|
+
* one `subagent_type` applied to every spawned sub-agent) — same launch
|
|
1824
|
+
* semantics as `Task`/`Agent`, so it is excluded here too (anti-double-count).
|
|
1825
|
+
*/
|
|
1826
|
+
const AGENT_LAUNCH_TOOLS = /* @__PURE__ */ new Set([
|
|
1827
|
+
"Task",
|
|
1828
|
+
"Agent",
|
|
1829
|
+
"AgentSwarm"
|
|
1830
|
+
]);
|
|
1813
1831
|
/**
|
|
1814
1832
|
* `JSON.stringify` length of the raw `tool_response` OBJECT (parity Python
|
|
1815
1833
|
* `len(str(tool_response))`); 0 when absent or unserializable (circular).
|
|
@@ -4095,9 +4113,7 @@ async function injectApexSubagentContext(cwd, home = homedir(), id = "claude-cod
|
|
|
4095
4113
|
const taskData = await readJsonFile(join(apexDir, "task.json"));
|
|
4096
4114
|
const completed = taskData ? completedTasks(taskData.tasks) : "none";
|
|
4097
4115
|
const pending = taskData ? pendingTasks(taskData.tasks) : "none";
|
|
4098
|
-
const
|
|
4099
|
-
const beforeStart = isCodex ? "Use update_plan → mark the active step in_progress before starting" : "Use TaskUpdate(taskId, status: in_progress) before starting";
|
|
4100
|
-
const whenDone = isCodex ? "update_plan → mark the step completed when done" : "TaskUpdate(taskId, status: completed) triggers auto-commit";
|
|
4116
|
+
const { beforeStart, whenDone } = apexAgentSteps(id);
|
|
4101
4117
|
return contextResponse("SubagentStart", capFragment("apex-subagent", `## APEX Sub-Agent Instructions
|
|
4102
4118
|
|
|
4103
4119
|
You are a sub-agent in APEX workflow. Follow these rules:
|
|
@@ -5325,7 +5341,7 @@ function sessionStart(input) {
|
|
|
5325
5341
|
if (input.scope === "rules") return injectRules(process.env.CLAUDE_PLUGIN_ROOT ?? input.cwd, input.event);
|
|
5326
5342
|
if (input.scope === "carto") return cartoSessionStart(input.cwd, input.now);
|
|
5327
5343
|
if (input.scope === "lessons") return dispatchLessons("SessionStart", input.payload, input.cwd, input.now);
|
|
5328
|
-
const core = sessionStartCore(input.cwd, void 0, input.now);
|
|
5344
|
+
const core = sessionStartCore(input.cwd, void 0, input.now, input.id ?? "claude-code");
|
|
5329
5345
|
return input.scope === "core" ? withSnapshot(core, input.cwd, import.meta.url) : core;
|
|
5330
5346
|
}
|
|
5331
5347
|
/**
|
|
@@ -7071,13 +7087,13 @@ const MCP_TTL_MS = parseEnvInt(process.env.FUSE_MCP_TTL_SEC, MCP_TTL_DEFAULT_SEC
|
|
|
7071
7087
|
const WEBFETCH_TTL_MS = parseEnvInt(process.env.FUSE_WEBFETCH_TTL_SEC, WEBFETCH_TTL_DEFAULT_SEC) * 1e3;
|
|
7072
7088
|
/** WebFetch prompt slice folded into its cache key (parity with Python prompt[:500]). */
|
|
7073
7089
|
const PROMPT_TRUNC = 500;
|
|
7074
|
-
/** True for the WebFetch tool (exact name). */
|
|
7090
|
+
/** True for the WebFetch tool (exact name) or Kimi Code CLI's `FetchURL` equivalent. */
|
|
7075
7091
|
function isWebFetch(tool) {
|
|
7076
|
-
return tool === "WebFetch";
|
|
7092
|
+
return tool === "WebFetch" || tool === "FetchURL";
|
|
7077
7093
|
}
|
|
7078
|
-
/** MCP doc tools + WebFetch whose calls are cached / verbosity-capped. */
|
|
7094
|
+
/** MCP doc tools + WebFetch/FetchURL whose calls are cached / verbosity-capped. */
|
|
7079
7095
|
function isMcpTool(tool) {
|
|
7080
|
-
return /context7|exa|webfetch|web_fetch/i.test(tool) || tool
|
|
7096
|
+
return /context7|exa|webfetch|web_fetch/i.test(tool) || isWebFetch(tool);
|
|
7081
7097
|
}
|
|
7082
7098
|
/** The query/url that keys the cache. */
|
|
7083
7099
|
function queryOf(input) {
|
|
@@ -7195,6 +7211,12 @@ async function recordActivity(file, activity) {
|
|
|
7195
7211
|
* - hermes: delegated to the adapter's `toHermesResponse` — `block` ->
|
|
7196
7212
|
* `{decision:"block",reason}`; `ask`/`inform` degrade to non-blocking
|
|
7197
7213
|
* `{context}` (Hermes has no interactive "ask" state).
|
|
7214
|
+
* - kimi: delegated to the adapter's `toKimiResponse` — `block`/`ask` ->
|
|
7215
|
+
* `hookSpecificOutput.permissionDecision:"deny"` (Kimi Code CLI's hooks
|
|
7216
|
+
* contract documents only "deny"; `ask` downgrades with a prefix, mirroring
|
|
7217
|
+
* the codex branch above), `inform` -> raw text on stdout (Kimi appends
|
|
7218
|
+
* stdout to context on exit 0 — no `hookSpecificOutput` envelope documented
|
|
7219
|
+
* for non-blocking context injection).
|
|
7198
7220
|
*
|
|
7199
7221
|
* User-visible pass notices (`prompt.userMessage`, Python `hook_output.allow_pass`
|
|
7200
7222
|
* / `post_pass` parity), by harness:
|
|
@@ -7259,6 +7281,7 @@ function respond(id, prompt, event = "PreToolUse") {
|
|
|
7259
7281
|
agent_message: message
|
|
7260
7282
|
});
|
|
7261
7283
|
case "hermes": return toHermesResponse(prompt);
|
|
7284
|
+
case "kimi": return toKimiResponse(prompt);
|
|
7262
7285
|
case "cline":
|
|
7263
7286
|
if (kind === "block") return JSON.stringify({
|
|
7264
7287
|
cancel: true,
|
|
@@ -8163,7 +8186,7 @@ function docSourceOf(tool) {
|
|
|
8163
8186
|
* Map a live tool-use to the activity to record, or null when nothing is
|
|
8164
8187
|
* tracked. Works across harnesses — tool names are globally distinct:
|
|
8165
8188
|
* - MCP doc calls (`context7` / `exa`, any separator) → `doc`
|
|
8166
|
-
* - `Task`/`Agent` + `subagent_type` (Claude/Cursor) → `agent` (bare agent name)
|
|
8189
|
+
* - `Task`/`Agent`/`AgentSwarm` + `subagent_type` (Claude/Cursor/Kimi) → `agent` (bare agent name)
|
|
8167
8190
|
* - direct exploration/research (Glob/Grep, explore Bash, web, MCP cache reads)
|
|
8168
8191
|
* → `agent` credited to the matching REQUIRED_AGENTS phase
|
|
8169
8192
|
* - a read tool opening a `.md` reference → `ref`
|
|
@@ -8178,7 +8201,7 @@ function activityFor(event) {
|
|
|
8178
8201
|
source: docSource,
|
|
8179
8202
|
ts: event.now
|
|
8180
8203
|
});
|
|
8181
|
-
if (event.tool === "Task" || event.tool === "Agent") {
|
|
8204
|
+
if (event.tool === "Task" || event.tool === "Agent" || event.tool === "AgentSwarm") {
|
|
8182
8205
|
const name = String(event.input?.subagent_type ?? event.input?.name ?? "").split(":").pop() ?? "";
|
|
8183
8206
|
if (name) out.push(agentActivity(name, event.now, qualityFor(event.responseLength, AGENT_QUALITY_MIN)));
|
|
8184
8207
|
return out;
|
|
@@ -31,7 +31,8 @@ const STD_NAMES = {
|
|
|
31
31
|
gemini: "gemini-cli",
|
|
32
32
|
copilot: "copilot",
|
|
33
33
|
kiro: "kiro",
|
|
34
|
-
hermes: "hermes"
|
|
34
|
+
hermes: "hermes",
|
|
35
|
+
kimi: "kimi"
|
|
35
36
|
};
|
|
36
37
|
/** Harnesses exposing a native hook system (vs CLI-only integration). */
|
|
37
38
|
const HOOK_CAPABLE = /* @__PURE__ */ new Set([
|
|
@@ -41,7 +42,8 @@ const HOOK_CAPABLE = /* @__PURE__ */ new Set([
|
|
|
41
42
|
"cline",
|
|
42
43
|
"gemini-cli",
|
|
43
44
|
"opencode",
|
|
44
|
-
"hermes"
|
|
45
|
+
"hermes",
|
|
46
|
+
"kimi"
|
|
45
47
|
]);
|
|
46
48
|
/** Integration mode for a harness id. */
|
|
47
49
|
function modeFor(id) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region src/detect/interfaces/types.d.ts
|
|
2
2
|
/** Known AI coding harnesses detectable at runtime. */
|
|
3
|
-
type HarnessId = "claude-code" | "codex" | "cursor" | "cline" | "gemini-cli" | "opencode" | "windsurf" | "copilot" | "aider" | "kiro" | "goose" | "amp" | "hermes" | "unknown";
|
|
3
|
+
type HarnessId = "claude-code" | "codex" | "cursor" | "cline" | "gemini-cli" | "opencode" | "windsurf" | "copilot" | "aider" | "kiro" | "goose" | "amp" | "hermes" | "kimi" | "unknown";
|
|
4
4
|
/** Integration mode: `hook` = native lifecycle hooks; `cli` = run as an external step. */
|
|
5
5
|
type HarnessMode = "hook" | "cli";
|
|
6
6
|
/** How the harness was identified. */
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as PromptKind, r as formatPrompt, t as Prompt } from "./types-DVbIl9md.mjs";
|
|
2
2
|
import { _ as queryHash, a as cacheLookupSubstringMeta, c as mcpCacheKey, d as extractText, f as IndexSummary, g as jaccardSimilar, h as compactMarkdown, i as cacheLookupSubstring, l as mcpCacheWrite, m as summarizeIndex, n as cacheLookup, o as cachePath, p as loadIndex, r as cacheLookupMeta, s as cacheStore, t as CacheHit, u as webfetchCacheWrite } from "./index-BUwEmIK-.mjs";
|
|
3
|
-
import { _ as parseEnvInt, a as ProjectLayout, c as projectLayout, d as resolveMaxLines, f as splitTarget, g as ttlLabel, h as resolveTtlSec, i as parseEnvFile, l as DEFAULT_MAX_LINES, m as TTL_ENV_KEY, n as envCandidates, o as STATE_GITIGNORE, p as DEFAULT_TTL_SEC, r as loadDotenv, s as STATE_ROOT, t as HOME_DIR, u as MAX_LINES_ENV_KEY } from "./index-
|
|
4
|
-
import { a as HarnessInfo, i as HarnessId, n as detectMode, o as HarnessMode, r as modeFor, s as HarnessVia, t as detectHarness } from "./harness-
|
|
3
|
+
import { _ as parseEnvInt, a as ProjectLayout, c as projectLayout, d as resolveMaxLines, f as splitTarget, g as ttlLabel, h as resolveTtlSec, i as parseEnvFile, l as DEFAULT_MAX_LINES, m as TTL_ENV_KEY, n as envCandidates, o as STATE_GITIGNORE, p as DEFAULT_TTL_SEC, r as loadDotenv, s as STATE_ROOT, t as HOME_DIR, u as MAX_LINES_ENV_KEY } from "./index-B_VHbsaJ.mjs";
|
|
4
|
+
import { a as HarnessInfo, i as HarnessId, n as detectMode, o as HarnessMode, r as modeFor, s as HarnessVia, t as detectHarness } from "./harness-BQ5_Wnva.mjs";
|
|
5
5
|
import { a as isDocConsulted, i as formatDocSatisfactionStatus, n as DocSatisfactionStatus, o as resolveSessions, r as formatDocDeny, t as AuthEntry } from "./doc-helpers-CEKzGg2u.mjs";
|
|
6
6
|
import { t as incrementTrivialEditCounter } from "./index-BOBXQ91y.mjs";
|
|
7
7
|
import { a as compactJson, i as walkUpFor, n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./index-BEMumjOw.mjs";
|
package/dist/index.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { a as parseEnvInt, i as splitTarget, n as MAX_LINES_ENV_KEY, r as resolveMaxLines, t as DEFAULT_MAX_LINES } from "./limits-CHn8AIL1.mjs";
|
|
2
|
-
import { a as DEFAULT_TTL_SEC, c as ttlLabel, i as parseEnvFile, n as envCandidates, o as TTL_ENV_KEY, r as loadDotenv, s as resolveTtlSec, t as HOME_DIR } from "./dotenv-
|
|
2
|
+
import { a as DEFAULT_TTL_SEC, c as ttlLabel, i as parseEnvFile, n as envCandidates, o as TTL_ENV_KEY, r as loadDotenv, s as resolveTtlSec, t as HOME_DIR } from "./dotenv-B26N_uGr.mjs";
|
|
3
3
|
import { n as STATE_ROOT, r as projectLayout, t as STATE_GITIGNORE } from "./layout-C0jaaCQC.mjs";
|
|
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
|
-
import { n as detectMode, r as modeFor, t as detectHarness } from "./harness-
|
|
6
|
+
import { n as detectMode, r as modeFor, t as detectHarness } from "./harness-BMuLJ9lm.mjs";
|
|
7
7
|
import { A as GIT_ASK, B as detectProjectType, C as SESSION_STATE_FRAGMENT, D as ASK_PATTERNS, E as protectedPathGuard, F as isRalphMode, H as requiredArchSkill, I as matchPatterns, L as detectFramework, M as PROJECT_INSTALL, N as RALPH_SAFE, O as CRITICAL_PATTERNS, P as SYSTEM_INSTALL, R as DEV_KEYWORDS, S as SAFE_PREFIXES, T as PROTECTED_GIT_RE, V as isApexCommand, _ as bashWriteGuard, a as registerGuard, b as CODE_REDIRECT, c as GO_DECL_RE, d as PY_MODEL_RE, f as RUST_DECL_RE, h as interfaceSeparationGuard, i as clearUserGuards, j as GIT_BLOCKED, k as securityGuard, l as JAVA_DECL_RE, m as TS_DECL_RE, n as FAIL_CLOSED, o as runGuards, p as SWIFT_PROTO_RE, r as GUARDS, s as installGuard, t as evaluate, u as PHP_DECL_RE, v as ASK_WRITERS, w as PROTECTED_FRAGMENTS, x as FILE_REDIRECT, y as CODE_MUTATORS, z as detectModularArchitecture } from "./evaluate-ClfbCDiY.mjs";
|
|
8
8
|
import { d as countFrameworkCodeLines, f as countLines, l as PLUGINS_DIR, p as evaluateFileSize, u as SOLID_REF } from "./home-state-BXf38Zi1.mjs";
|
|
9
9
|
import { i as resolveSessions, n as formatDocSatisfactionStatus, r as isDocConsulted, t as formatDocDeny } from "./doc-helpers-CWZegVdR.mjs";
|
|
10
10
|
import { i as parseFrontmatter, n as scoreReferences, r as globToRe, t as routeReferences } from "./router-PKVNBHge.mjs";
|
|
11
|
-
import { A as
|
|
11
|
+
import { A as MAX_EXA_RESULTS, B as freshnessGate, C as detectRequiredSkills, D as frameworkSolidGate, E as SKILL_TRIGGERS, F as POST_AUTH_GATES, I as PRE_AUTH_GATES, L as brainstormGate, M as capVerbosity, N as detectCreationIntent, P as APEX_GATES, R as docConsultedGate, S as parseField, T as usesTailwindUtilities, V as solidReadGate, _ as detectClaudeMdProjectType, a as firstHeading, c as EXCLUDE_DIRS, d as buildApexTaskInjection, f as loadApexTaskState, g as buildClaudeMdContext, h as buildApexInstruction, i as firstComment, j as MAX_TOKENS, l as PROJECT_INDICATORS, m as DEV_VERBS, n as missingSeoElements, o as parseEnrichment, r as descFromText, s as parseEntry, t as isHtmlLike, u as buildApexTaskContext, w as skillTriggerGate, x as parseBodyDesc, z as evaluateApex } from "./validate-CkmquRDV.mjs";
|
|
12
12
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
13
13
|
import { a as nowStamp, c as stateFileFor, i as lessonsFileFor, l as throttleMs, n as readRoots, o as readState, r as registryFile, s as setStateField, t as addRoot, u as ensureMemoryGitignore } from "./registry-CymilZiZ.mjs";
|
|
14
14
|
import { a as cacheLookupSubstring, c as cacheStore, d as loadIndex, f as summarizeIndex, h as queryHash, i as cacheLookupMeta, l as mcpCacheKey, m as jaccardSimilar, n as webfetchCacheWrite, o as cacheLookupSubstringMeta, p as compactMarkdown, r as cacheLookup, s as cachePath, t as mcpCacheWrite, u as extractText } from "./mcp-store-BkBDmuxN.mjs";
|
package/dist/init/index.d.mts
CHANGED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { t as evaluate } from "./evaluate-ClfbCDiY.mjs";
|
|
2
|
+
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
3
|
+
import { c as readClaudeInput, v as commandToString } from "./claude-51RSspJ1.mjs";
|
|
4
|
+
//#region src/adapters/kimi/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* Kimi Code CLI adapter (hook-mode). Moonshot AI's Kimi Code CLI (v0.27.0,
|
|
7
|
+
* 2026) configures hooks in `~/.kimi-code/config.toml` (`[[hooks]]`: event,
|
|
8
|
+
* matcher, command, timeout — exactly 4 fields, any extra breaks config
|
|
9
|
+
* loading) and pipes a JSON payload to `command` on stdin, reading JSON back
|
|
10
|
+
* on stdout. Verified against kimi.com/code/docs/en/kimi-code-cli/
|
|
11
|
+
* customization/hooks.html (2026): the INPUT base shape
|
|
12
|
+
* (`hook_event_name`/`session_id`/`cwd`, `PreToolUse`/`Bash` carrying
|
|
13
|
+
* `tool_name` + `tool_input.command`) matches Claude Code's snake_case wire
|
|
14
|
+
* shape, so the Claude reader is reused — verified live against kimi-code
|
|
15
|
+
* v0.27.0 for both `PreToolUse`/`Bash` and `PreToolUse`/`Write`. Tool fields
|
|
16
|
+
* verified: `Bash` takes `command`; `Write` takes `path` + `content`; `Edit`
|
|
17
|
+
* takes `path` + `old_string`/`new_string` (per `docs/en/reference/tools.md`,
|
|
18
|
+
* moonshotai/kimi-code). The path key is `path`, NOT Claude's `file_path`
|
|
19
|
+
* (see interfaces/types.ts).
|
|
20
|
+
*
|
|
21
|
+
* OUTPUT does not match Claude: only `UserPromptSubmit`, `PreToolUse`, `Stop`
|
|
22
|
+
* are blockable, and the only documented decision is
|
|
23
|
+
* `{"hookSpecificOutput":{"permissionDecision":"deny","permissionDecisionReason"}}`
|
|
24
|
+
* (camelCase, despite the snake_case input — a real Kimi inconsistency). No
|
|
25
|
+
* `"ask"`/`"allow"`/`additionalContext` is documented. Verified live against
|
|
26
|
+
* kimi-code v0.27.0: that deny envelope on stdout at exit 0 blocks the call
|
|
27
|
+
* (exit 2 is NOT required) and `permissionDecisionReason` is re-injected to
|
|
28
|
+
* the model; empty stdout at exit 0 lets the call run. Exit 2 = block, stderr
|
|
29
|
+
* = reason; any other exit/timeout/crash fails open (allow). Whether raw-text
|
|
30
|
+
* stdout at exit 0 (the `inform` case) reaches the context is documented only
|
|
31
|
+
* as "may be" — unverified, needs live test.
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* Downgrade prefix for `ask` — Kimi has no interactive approval channel
|
|
35
|
+
* reachable from a hook. Kimi DOES have an `ask` decision, but only via
|
|
36
|
+
* `[[permission.rules]] decision = "ask"` in config.toml, a separate system
|
|
37
|
+
* a hook's return value cannot invoke.
|
|
38
|
+
*/
|
|
39
|
+
const ASK_PREFIX = "[downgraded from ask — Kimi Code has no interactive approval]";
|
|
40
|
+
/** Read & parse the Kimi hook stdin payload (same wire shape as Claude for the fields we consume). */
|
|
41
|
+
async function readKimiInput() {
|
|
42
|
+
return await readClaudeInput();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Render a portable {@link Prompt} as a Kimi hook response string:
|
|
46
|
+
* `block`/`ask` -> `{hookSpecificOutput:{permissionDecision:"deny",...}}`
|
|
47
|
+
* (only `"deny"` is documented; `ask` is downgraded with {@link ASK_PREFIX});
|
|
48
|
+
* `inform` -> raw text (Kimi appends stdout to context on exit 0 — no
|
|
49
|
+
* documented JSON envelope for non-blocking context injection).
|
|
50
|
+
* @param prompt - The portable policy prompt.
|
|
51
|
+
* @returns The native Kimi response string.
|
|
52
|
+
*/
|
|
53
|
+
function toKimiResponse(prompt) {
|
|
54
|
+
const msg = formatPrompt(prompt);
|
|
55
|
+
if (prompt.kind === "inform") return msg;
|
|
56
|
+
const res = { hookSpecificOutput: {
|
|
57
|
+
permissionDecision: "deny",
|
|
58
|
+
permissionDecisionReason: prompt.kind === "ask" ? `${ASK_PREFIX}\n${msg}` : msg
|
|
59
|
+
} };
|
|
60
|
+
return JSON.stringify(res);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Run the bundled policy over a Kimi hook payload and return the native
|
|
64
|
+
* response string, or null to allow (the hook then exits 0 with empty stdout).
|
|
65
|
+
* @param input - Parsed hook stdin payload.
|
|
66
|
+
* @returns Native response string, or null when the tool use is allowed.
|
|
67
|
+
*/
|
|
68
|
+
function guard(input) {
|
|
69
|
+
const t = input.tool_input;
|
|
70
|
+
const r = evaluate({
|
|
71
|
+
tool: input.tool_name ?? "Write",
|
|
72
|
+
filePath: t?.path ?? t?.file_path,
|
|
73
|
+
content: t?.content ?? t?.new_string,
|
|
74
|
+
command: commandToString(t?.command)
|
|
75
|
+
});
|
|
76
|
+
if (r.decision === "allow" || !r.prompt) return null;
|
|
77
|
+
return toKimiResponse(r.prompt);
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
export { readKimiInput as n, toKimiResponse as r, guard as t };
|
package/dist/policy/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { A as GIT_ASK, B as detectProjectType, C as SESSION_STATE_FRAGMENT, D as ASK_PATTERNS, E as protectedPathGuard, F as isRalphMode, H as requiredArchSkill, I as matchPatterns, L as detectFramework, M as PROJECT_INSTALL, N as RALPH_SAFE, O as CRITICAL_PATTERNS, P as SYSTEM_INSTALL, R as DEV_KEYWORDS, S as SAFE_PREFIXES, T as PROTECTED_GIT_RE, V as isApexCommand, _ as bashWriteGuard, a as registerGuard, b as CODE_REDIRECT, c as GO_DECL_RE, d as PY_MODEL_RE, f as RUST_DECL_RE, h as interfaceSeparationGuard, i as clearUserGuards, j as GIT_BLOCKED, k as securityGuard, l as JAVA_DECL_RE, m as TS_DECL_RE, n as FAIL_CLOSED, o as runGuards, p as SWIFT_PROTO_RE, r as GUARDS, s as installGuard, t as evaluate, u as PHP_DECL_RE, v as ASK_WRITERS, w as PROTECTED_FRAGMENTS, x as FILE_REDIRECT, y as CODE_MUTATORS, z as detectModularArchitecture } from "../evaluate-ClfbCDiY.mjs";
|
|
2
2
|
import { d as countFrameworkCodeLines, f as countLines, l as PLUGINS_DIR, p as evaluateFileSize, u as SOLID_REF } from "../home-state-BXf38Zi1.mjs";
|
|
3
|
-
import { A as
|
|
3
|
+
import { A as MAX_EXA_RESULTS, B as freshnessGate, C as detectRequiredSkills, D as frameworkSolidGate, E as SKILL_TRIGGERS, F as POST_AUTH_GATES, I as PRE_AUTH_GATES, L as brainstormGate, M as capVerbosity, N as detectCreationIntent, P as APEX_GATES, R as docConsultedGate, S as parseField, T as usesTailwindUtilities, V as solidReadGate, _ as detectClaudeMdProjectType, a as firstHeading, c as EXCLUDE_DIRS, d as buildApexTaskInjection, f as loadApexTaskState, g as buildClaudeMdContext, h as buildApexInstruction, i as firstComment, j as MAX_TOKENS, l as PROJECT_INDICATORS, m as DEV_VERBS, n as missingSeoElements, o as parseEnrichment, r as descFromText, s as parseEntry, t as isHtmlLike, u as buildApexTaskContext, w as skillTriggerGate, x as parseBodyDesc, z as evaluateApex } from "../validate-CkmquRDV.mjs";
|
|
4
4
|
import "../policy-la_KkjCS.mjs";
|
|
5
5
|
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, CRITICAL_PATTERNS, DEV_KEYWORDS, DEV_VERBS, EXCLUDE_DIRS, FAIL_CLOSED, FILE_REDIRECT, GIT_ASK, GIT_BLOCKED, GO_DECL_RE, GUARDS, JAVA_DECL_RE, MAX_EXA_RESULTS, MAX_TOKENS, PHP_DECL_RE, PLUGINS_DIR, POST_AUTH_GATES, PRE_AUTH_GATES, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, RALPH_SAFE, RUST_DECL_RE, SAFE_PREFIXES, SESSION_STATE_FRAGMENT, SKILL_TRIGGERS, SOLID_REF, SWIFT_PROTO_RE, SYSTEM_INSTALL, TS_DECL_RE, bashWriteGuard, brainstormGate, buildApexInstruction, buildApexTaskContext, buildApexTaskInjection, buildClaudeMdContext, capVerbosity, clearUserGuards, countFrameworkCodeLines, countLines, descFromText, detectClaudeMdProjectType, detectCreationIntent, detectFramework, detectModularArchitecture, detectProjectType, detectRequiredSkills, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, firstComment, firstHeading, frameworkSolidGate, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, isHtmlLike, isRalphMode, loadApexTaskState, matchPatterns, missingSeoElements, parseBodyDesc, parseEnrichment, parseEntry, parseField, protectedPathGuard, registerGuard, requiredArchSkill, runGuards, securityGuard, skillTriggerGate, solidReadGate, usesTailwindUtilities };
|
package/dist/runtime/index.d.mts
CHANGED
|
@@ -75,7 +75,7 @@ interface ToolEvent {
|
|
|
75
75
|
* Map a live tool-use to the activity to record, or null when nothing is
|
|
76
76
|
* tracked. Works across harnesses — tool names are globally distinct:
|
|
77
77
|
* - MCP doc calls (`context7` / `exa`, any separator) → `doc`
|
|
78
|
-
* - `Task`/`Agent` + `subagent_type` (Claude/Cursor) → `agent` (bare agent name)
|
|
78
|
+
* - `Task`/`Agent`/`AgentSwarm` + `subagent_type` (Claude/Cursor/Kimi) → `agent` (bare agent name)
|
|
79
79
|
* - direct exploration/research (Glob/Grep, explore Bash, web, MCP cache reads)
|
|
80
80
|
* → `agent` credited to the matching REQUIRED_AGENTS phase
|
|
81
81
|
* - a read tool opening a `.md` reference → `ref`
|
|
@@ -207,6 +207,12 @@ declare function normalizeEvent(id: string, payload: Record<string, unknown>): N
|
|
|
207
207
|
* - hermes: delegated to the adapter's `toHermesResponse` — `block` ->
|
|
208
208
|
* `{decision:"block",reason}`; `ask`/`inform` degrade to non-blocking
|
|
209
209
|
* `{context}` (Hermes has no interactive "ask" state).
|
|
210
|
+
* - kimi: delegated to the adapter's `toKimiResponse` — `block`/`ask` ->
|
|
211
|
+
* `hookSpecificOutput.permissionDecision:"deny"` (Kimi Code CLI's hooks
|
|
212
|
+
* contract documents only "deny"; `ask` downgrades with a prefix, mirroring
|
|
213
|
+
* the codex branch above), `inform` -> raw text on stdout (Kimi appends
|
|
214
|
+
* stdout to context on exit 0 — no `hookSpecificOutput` envelope documented
|
|
215
|
+
* for non-blocking context injection).
|
|
210
216
|
*
|
|
211
217
|
* User-visible pass notices (`prompt.userMessage`, Python `hook_output.allow_pass`
|
|
212
218
|
* / `post_pass` parity), by harness:
|
|
@@ -234,7 +240,7 @@ declare function respond(id: string, prompt: Prompt, event?: string): string;
|
|
|
234
240
|
declare const MCP_TTL_MS: number;
|
|
235
241
|
/** WebFetch freshness (ms) from `FUSE_WEBFETCH_TTL_SEC` (default 24h; pages stale faster than docs). */
|
|
236
242
|
declare const WEBFETCH_TTL_MS: number;
|
|
237
|
-
/** MCP doc tools + WebFetch whose calls are cached / verbosity-capped. */
|
|
243
|
+
/** MCP doc tools + WebFetch/FetchURL whose calls are cached / verbosity-capped. */
|
|
238
244
|
declare function isMcpTool(tool: string): boolean;
|
|
239
245
|
/** The query/url that keys the cache. */
|
|
240
246
|
declare function queryOf(input: Record<string, unknown>): string;
|
|
@@ -353,9 +359,10 @@ declare function runSessionStartCleanups(home?: string, now?: number): void;
|
|
|
353
359
|
* @param cwd - Project root for dev-context detection.
|
|
354
360
|
* @param home - Home dir (defaults to `~`).
|
|
355
361
|
* @param now - Clock for TTL cleanup (defaults to `Date.now()`).
|
|
362
|
+
* @param id - Harness target id (defaults to "claude-code" — zero-regression default).
|
|
356
363
|
* @returns The native hook stdout (possibly empty).
|
|
357
364
|
*/
|
|
358
|
-
declare function sessionStartCore(cwd: string, home?: string, now?: number): string;
|
|
365
|
+
declare function sessionStartCore(cwd: string, home?: string, now?: number, id?: string): string;
|
|
359
366
|
//#endregion
|
|
360
367
|
//#region src/runtime/lifecycle/inject-rules.d.ts
|
|
361
368
|
/** Read & concatenate all `*.md` files (sorted) under `rulesDir`. */
|
package/dist/runtime/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { r as projectLayout } from "../layout-C0jaaCQC.mjs";
|
|
2
2
|
import { a as sanitizeSessionId, c as sessionsDir, i as loadSessionState, n as fuseHarnessHome, o as saveSessionState, r as fusengineCache, s as sessionStatePath, t as claudeHome } from "../home-state-BXf38Zi1.mjs";
|
|
3
|
-
import { A as trackMcpResearch, At as defaultStateDir, B as generateProjectMap, C as seoPostToolUse, Ct as trimLogFile, D as securityAdvisoryForPatch, Dt as claudeMdKey, E as securityAdvisory, Et as projectContext, F as dispatchAipilot, Ft as loadSecurityState, G as countFiles, H as writeTree, I as dispatchLessons, It as saveSecurityState, J as lessonsArchiveFileFor, K as getFileDesc, L as cartoSessionStart, Lt as securityStateDir, M as trackEnrichment, Mt as trackFile, N as dispatchLifecycle, Nt as normalizeEvent, O as postTrackingSideEffects, Ot as promptSubmitContext, P as aipilotPostToolUse, Pt as isoUtc, R as generateEcosystemMap, Rt as securityStatePath, S as postEditContext, St as removeOldFiles, T as dispatchMemory, Tt as gitContext, U as loadEnriched, V as isProject, W as mergeLines, X as lessonsStateFileFor, Y as lessonsFileFor, _ as preCommitGate, _t as readRules, a as recordActivity, at as saveApexState, b as extractSymbols, bt as pruneEmptyDirs, c as MCP_TTL_MS, ct as trackAgentMemory, d as isMcpTool, dt as validateSolidGate, f as queryOf, ft as checkFileSize, g as gate, gt as injectRules, h as TRIVIAL_BUDGET, ht as solidDetectStart, i as respond, it as cleanupSession, j as trackSkillRead, jt as projectHash, k as trackWatchResearch, kt as taskContext, l as WEBFETCH_TTL_MS, lt as subagentCacheContext, m as REQUIRED_AGENTS, mt as detectSolidProfile, n as activityFor, nt as trackSessionChanges, o as mcpPostStore, ot as logToolFailure, p as DEFAULT_WINDOW_MS, pt as countLoc, q as listChildren, r as handlePre, rt as validateRulesLoaded, s as mcpPreIntercept, st as validateTeammateOutput, t as handleHook, tt as postEditTypescript, u as cacheQueryOf, ut as validateTailwind, v as detectDuplication, vt as runSessionStartCleanups, w as seoPostToolUseResponse, wt as devContext, x as lifecycleStdout, xt as purgeTtlTree, y as dryGate, yt as sessionStartCore, z as writePluginMap, zt as todayUtc } from "../handle-
|
|
3
|
+
import { A as trackMcpResearch, At as defaultStateDir, B as generateProjectMap, C as seoPostToolUse, Ct as trimLogFile, D as securityAdvisoryForPatch, Dt as claudeMdKey, E as securityAdvisory, Et as projectContext, F as dispatchAipilot, Ft as loadSecurityState, G as countFiles, H as writeTree, I as dispatchLessons, It as saveSecurityState, J as lessonsArchiveFileFor, K as getFileDesc, L as cartoSessionStart, Lt as securityStateDir, M as trackEnrichment, Mt as trackFile, N as dispatchLifecycle, Nt as normalizeEvent, O as postTrackingSideEffects, Ot as promptSubmitContext, P as aipilotPostToolUse, Pt as isoUtc, R as generateEcosystemMap, Rt as securityStatePath, S as postEditContext, St as removeOldFiles, T as dispatchMemory, Tt as gitContext, U as loadEnriched, V as isProject, W as mergeLines, X as lessonsStateFileFor, Y as lessonsFileFor, _ as preCommitGate, _t as readRules, a as recordActivity, at as saveApexState, b as extractSymbols, bt as pruneEmptyDirs, c as MCP_TTL_MS, ct as trackAgentMemory, d as isMcpTool, dt as validateSolidGate, f as queryOf, ft as checkFileSize, g as gate, gt as injectRules, h as TRIVIAL_BUDGET, ht as solidDetectStart, i as respond, it as cleanupSession, j as trackSkillRead, jt as projectHash, k as trackWatchResearch, kt as taskContext, l as WEBFETCH_TTL_MS, lt as subagentCacheContext, m as REQUIRED_AGENTS, mt as detectSolidProfile, n as activityFor, nt as trackSessionChanges, o as mcpPostStore, ot as logToolFailure, p as DEFAULT_WINDOW_MS, pt as countLoc, q as listChildren, r as handlePre, rt as validateRulesLoaded, s as mcpPreIntercept, st as validateTeammateOutput, t as handleHook, tt as postEditTypescript, u as cacheQueryOf, ut as validateTailwind, v as detectDuplication, vt as runSessionStartCleanups, w as seoPostToolUseResponse, wt as devContext, x as lifecycleStdout, xt as purgeTtlTree, y as dryGate, yt as sessionStartCore, z as writePluginMap, zt as todayUtc } from "../handle-B7YufFz9.mjs";
|
|
4
4
|
//#region src/runtime/storage.ts
|
|
5
5
|
/**
|
|
6
6
|
* The project's single state dir (`<root>/.harness`) — neutral + harness-agnostic,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as HOME_DIR } from "./dotenv-
|
|
2
|
-
import { t as detectHarness } from "./harness-
|
|
1
|
+
import { t as HOME_DIR } from "./dotenv-B26N_uGr.mjs";
|
|
2
|
+
import { t as detectHarness } from "./harness-BMuLJ9lm.mjs";
|
|
3
3
|
import { l as PLUGINS_DIR } from "./home-state-BXf38Zi1.mjs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { c as ttlLabel } from "./dotenv-
|
|
1
|
+
import { c as ttlLabel } from "./dotenv-B26N_uGr.mjs";
|
|
2
2
|
import { n as fuseHarnessHome } from "./home-state-BXf38Zi1.mjs";
|
|
3
3
|
import { a as writeJsonFile, i as readJsonFile } from "./json-io-DisYd2fb.mjs";
|
|
4
4
|
import { i as resolveSessions } from "./doc-helpers-CWZegVdR.mjs";
|
|
5
5
|
import { t as routeReferences } from "./router-PKVNBHge.mjs";
|
|
6
|
-
import { t as resolveSkillPath } from "./skill-path-
|
|
6
|
+
import { t as resolveSkillPath } from "./skill-path-CDvR4bbF.mjs";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
9
|
import { createHmac, randomBytes } from "node:crypto";
|
package/dist/tracking/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { c as recordAgent, d as recordRefRead, f as recordTarget, l as recordBrainstormRequired, m as trivialCount, n as saveTrack, o as agentsFresh, p as recordTrivialEdit, s as emptyTrack, t as loadTrack, u as recordDoc } from "../store-
|
|
1
|
+
import { c as recordAgent, d as recordRefRead, f as recordTarget, l as recordBrainstormRequired, m as trivialCount, n as saveTrack, o as agentsFresh, p as recordTrivialEdit, s as emptyTrack, t as loadTrack, u as recordDoc } from "../store-nDZ3eLBQ.mjs";
|
|
2
2
|
export { agentsFresh, emptyTrack, loadTrack, recordAgent, recordBrainstormRequired, recordDoc, recordRefRead, recordTarget, recordTrivialEdit, saveTrack, trivialCount };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { r as resolveMaxLines } from "./limits-CHn8AIL1.mjs";
|
|
2
|
-
import { c as ttlLabel, t as HOME_DIR } from "./dotenv-
|
|
2
|
+
import { c as ttlLabel, t as HOME_DIR } from "./dotenv-B26N_uGr.mjs";
|
|
3
3
|
import { R as DEV_KEYWORDS, g as moduleAwarePath } from "./evaluate-ClfbCDiY.mjs";
|
|
4
4
|
import { d as countFrameworkCodeLines, l as PLUGINS_DIR, u as SOLID_REF } from "./home-state-BXf38Zi1.mjs";
|
|
5
5
|
import { r as isDocConsulted, t as formatDocDeny } from "./doc-helpers-CWZegVdR.mjs";
|
|
6
6
|
import { t as routeReferences } from "./router-PKVNBHge.mjs";
|
|
7
|
-
import { n as findMarketplacePlugins, r as readPluginMeta, t as resolveSkillPath } from "./skill-path-
|
|
7
|
+
import { n as findMarketplacePlugins, r as readPluginMeta, t as resolveSkillPath } from "./skill-path-CDvR4bbF.mjs";
|
|
8
8
|
import { extname, join } from "node:path";
|
|
9
9
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
10
10
|
import { homedir } from "node:os";
|
|
@@ -1208,24 +1208,29 @@ function harnessHomeSegment(id) {
|
|
|
1208
1208
|
return HOME_DIR[id] ?? ".claude";
|
|
1209
1209
|
}
|
|
1210
1210
|
/**
|
|
1211
|
-
* Root instructions doc name for a harness target: "AGENTS.md" for Codex
|
|
1212
|
-
* "CLAUDE.md" for every other
|
|
1211
|
+
* Root instructions doc name for a harness target: "AGENTS.md" for Codex and
|
|
1212
|
+
* Kimi Code CLI (both documented conventions), "CLAUDE.md" for every other
|
|
1213
|
+
* target (including the default/unknown fallback).
|
|
1213
1214
|
* @param id - Harness id.
|
|
1214
1215
|
* @returns The doc file name.
|
|
1215
1216
|
*/
|
|
1216
1217
|
function apexDocName(id) {
|
|
1217
|
-
return id === "codex" ? "AGENTS.md" : "CLAUDE.md";
|
|
1218
|
+
return id === "codex" || id === "kimi" ? "AGENTS.md" : "CLAUDE.md";
|
|
1218
1219
|
}
|
|
1219
1220
|
/**
|
|
1220
|
-
* Native plan/track tool name for a harness target: Codex's `update_plan
|
|
1221
|
-
*
|
|
1222
|
-
* tool references (`TaskList`,
|
|
1223
|
-
*
|
|
1221
|
+
* Native plan/track tool name for a harness target: Codex's `update_plan`,
|
|
1222
|
+
* Kimi Code CLI's `TodoList`, or Claude's `TaskCreate` (default). Covers only
|
|
1223
|
+
* the PLAN-step line — other target-specific tool references (`TaskList`,
|
|
1224
|
+
* `TaskUpdate`) are resolved via `apexTaskSteps`/`apexAgentSteps` in
|
|
1225
|
+
* `apex-target-steps.ts` since their surrounding prose differs structurally,
|
|
1226
|
+
* not just by token.
|
|
1224
1227
|
* @param id - Harness id.
|
|
1225
1228
|
* @returns The plan-tool name.
|
|
1226
1229
|
*/
|
|
1227
1230
|
function apexPlanTool(id) {
|
|
1228
|
-
|
|
1231
|
+
if (id === "codex") return "update_plan";
|
|
1232
|
+
if (id === "kimi") return "TodoList";
|
|
1233
|
+
return "TaskCreate";
|
|
1229
1234
|
}
|
|
1230
1235
|
//#endregion
|
|
1231
1236
|
//#region src/policy/detect-claude-md-project-type.ts
|
|
@@ -1291,6 +1296,61 @@ function buildClaudeMdContext(prompt, cwd, id = "claude-code") {
|
|
|
1291
1296
|
return `${buildApexInstruction(detectClaudeMdProjectType(cwd), resolveMaxLines(), id)}\n\n# ${docName}\n${claudeContent}`;
|
|
1292
1297
|
}
|
|
1293
1298
|
//#endregion
|
|
1299
|
+
//#region src/policy/apex-target-steps.ts
|
|
1300
|
+
/** Codex/Kimi step-trio overrides; any other target falls back to {@link DEFAULT_TASK_STEPS}. */
|
|
1301
|
+
const TASK_STEPS = {
|
|
1302
|
+
codex: {
|
|
1303
|
+
step3: "update_plan → review the current plan",
|
|
1304
|
+
step4: "update_plan → mark the active step in_progress before starting",
|
|
1305
|
+
step7: "update_plan → mark the step completed when done"
|
|
1306
|
+
},
|
|
1307
|
+
kimi: {
|
|
1308
|
+
step3: "TodoList → review the current list (omit todos to query)",
|
|
1309
|
+
step4: "TodoList → mark the active item in_progress before starting",
|
|
1310
|
+
step7: "TodoList → mark the item done when finished"
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
/** Default (Claude Code) step trio — zero-regression fallback for any unlisted target. */
|
|
1314
|
+
const DEFAULT_TASK_STEPS = {
|
|
1315
|
+
step3: "TaskList → see pending tasks",
|
|
1316
|
+
step4: "TaskUpdate(in_progress) → before starting",
|
|
1317
|
+
step7: "TaskUpdate(completed) → triggers auto-commit"
|
|
1318
|
+
};
|
|
1319
|
+
/**
|
|
1320
|
+
* Resolve the step3/step4/step7 phrasing for a harness target (APEX
|
|
1321
|
+
* task-context injection). Replaces the former `isCodex` binary ternary.
|
|
1322
|
+
* @param id - Harness id.
|
|
1323
|
+
* @returns The {@link ApexTaskSteps} for that target.
|
|
1324
|
+
*/
|
|
1325
|
+
function apexTaskSteps(id) {
|
|
1326
|
+
return TASK_STEPS[id] ?? DEFAULT_TASK_STEPS;
|
|
1327
|
+
}
|
|
1328
|
+
/** Codex/Kimi before/done overrides; any other target falls back to {@link DEFAULT_AGENT_STEPS}. */
|
|
1329
|
+
const AGENT_STEPS = {
|
|
1330
|
+
codex: {
|
|
1331
|
+
beforeStart: "Use update_plan → mark the active step in_progress before starting",
|
|
1332
|
+
whenDone: "update_plan → mark the step completed when done"
|
|
1333
|
+
},
|
|
1334
|
+
kimi: {
|
|
1335
|
+
beforeStart: "Use TodoList → mark the active item in_progress before starting",
|
|
1336
|
+
whenDone: "TodoList → mark the item done when finished"
|
|
1337
|
+
}
|
|
1338
|
+
};
|
|
1339
|
+
/** Default (Claude Code) before/done pair — zero-regression fallback for any unlisted target. */
|
|
1340
|
+
const DEFAULT_AGENT_STEPS = {
|
|
1341
|
+
beforeStart: "Use TaskUpdate(taskId, status: in_progress) before starting",
|
|
1342
|
+
whenDone: "TaskUpdate(taskId, status: completed) triggers auto-commit"
|
|
1343
|
+
};
|
|
1344
|
+
/**
|
|
1345
|
+
* Resolve the beforeStart/whenDone phrasing for a harness target (APEX
|
|
1346
|
+
* sub-agent injection). Replaces the former `isCodex` binary ternary.
|
|
1347
|
+
* @param id - Harness id.
|
|
1348
|
+
* @returns The {@link ApexAgentSteps} for that target.
|
|
1349
|
+
*/
|
|
1350
|
+
function apexAgentSteps(id) {
|
|
1351
|
+
return AGENT_STEPS[id] ?? DEFAULT_AGENT_STEPS;
|
|
1352
|
+
}
|
|
1353
|
+
//#endregion
|
|
1294
1354
|
//#region src/policy/apex-task-context.ts
|
|
1295
1355
|
/**
|
|
1296
1356
|
* Read the current task state from `task.json`, reproducing the legacy Python
|
|
@@ -1330,10 +1390,7 @@ function loadApexTaskState(taskFile) {
|
|
|
1330
1390
|
*/
|
|
1331
1391
|
function buildApexTaskContext(state, maxLines, id = "claude-code") {
|
|
1332
1392
|
const seg = harnessHomeSegment(id);
|
|
1333
|
-
const
|
|
1334
|
-
const step3 = isCodex ? "update_plan → review the current plan" : "TaskList → see pending tasks";
|
|
1335
|
-
const step4 = isCodex ? "update_plan → mark the active step in_progress before starting" : "TaskUpdate(in_progress) → before starting";
|
|
1336
|
-
const step7 = isCodex ? "update_plan → mark the step completed when done" : "TaskUpdate(completed) → triggers auto-commit";
|
|
1393
|
+
const { step3, step4, step7 } = apexTaskSteps(id);
|
|
1337
1394
|
return `⚠️ APEX MODE - Read ${seg}/apex/AGENTS.md for rules\n\nCurrent: Task #${state.id} - ${state.subject} (Phase: ${state.phase})\nDocs consulted: ${state.docs}\n\nAgent must:\n1. Read task.json → find last 3 completed tasks\n2. Read their notes in docs/ (task-{ID}-{subject}.md)\n3. ${step3}\n4. ${step4}\n5. Apply SOLID (files < ${maxLines} lines)\n6. Write notes to docs/task-{ID}-{subject}.md\n7. ${step7}`;
|
|
1338
1395
|
}
|
|
1339
1396
|
/**
|
|
@@ -1591,4 +1648,4 @@ function missingSeoElements(html) {
|
|
|
1591
1648
|
return missing;
|
|
1592
1649
|
}
|
|
1593
1650
|
//#endregion
|
|
1594
|
-
export {
|
|
1651
|
+
export { MAX_EXA_RESULTS as A, freshnessGate as B, detectRequiredSkills as C, frameworkSolidGate as D, SKILL_TRIGGERS as E, POST_AUTH_GATES as F, PRE_AUTH_GATES as I, brainstormGate as L, capVerbosity as M, detectCreationIntent as N, isExcludedJsPath as O, APEX_GATES as P, docConsultedGate as R, parseField as S, usesTailwindUtilities as T, solidReadGate as V, detectClaudeMdProjectType as _, firstHeading as a, scanPlugin as b, EXCLUDE_DIRS as c, buildApexTaskInjection as d, loadApexTaskState as f, buildClaudeMdContext as g, buildApexInstruction as h, firstComment as i, MAX_TOKENS as j, isExcludedSwiftPath as k, PROJECT_INDICATORS as l, DEV_VERBS as m, missingSeoElements as n, parseEnrichment as o, apexAgentSteps as p, descFromText as r, parseEntry as s, isHtmlLike as t, buildApexTaskContext as u, apexDocName as v, skillTriggerGate as w, parseBodyDesc as x, harnessHomeSegment as y, evaluateApex 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.80",
|
|
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",
|
|
@@ -113,6 +113,11 @@
|
|
|
113
113
|
"bun": "./src/adapters/hermes/index.ts",
|
|
114
114
|
"types": "./dist/adapters/hermes/index.d.mts",
|
|
115
115
|
"import": "./dist/adapters/hermes/index.mjs"
|
|
116
|
+
},
|
|
117
|
+
"./adapters/kimi": {
|
|
118
|
+
"bun": "./src/adapters/kimi/index.ts",
|
|
119
|
+
"types": "./dist/adapters/kimi/index.d.mts",
|
|
120
|
+
"import": "./dist/adapters/kimi/index.mjs"
|
|
116
121
|
}
|
|
117
122
|
},
|
|
118
123
|
"files": [
|
|
@@ -134,7 +139,7 @@
|
|
|
134
139
|
"sim": "bun test test/sim/",
|
|
135
140
|
"typecheck": "tsc --noEmit",
|
|
136
141
|
"docs:api": "typedoc",
|
|
137
|
-
"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 src/adapters/hermes/index.ts --dts --format esm --clean --out-dir dist",
|
|
142
|
+
"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 src/adapters/hermes/index.ts src/adapters/kimi/index.ts --dts --format esm --clean --out-dir dist",
|
|
138
143
|
"prepublishOnly": "bun test && tsc --noEmit && bun run build"
|
|
139
144
|
},
|
|
140
145
|
"publishConfig": {
|