@fusengine/harness 0.1.86 → 0.1.87

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/bin.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { o as resolveStdinMaxBytes } from "../limits-DG5pl-5s.mjs";
3
- import { r as loadDotenv, s as resolveTtlSec } from "../dotenv-DoqNlALG.mjs";
3
+ import { c as resolveTtlSec, r as loadDotenv } from "../dotenv-BLBkBTww.mjs";
4
4
  import { t as detectHarness } from "../harness-BMuLJ9lm.mjs";
5
5
  import { t as claudeHome } from "../home-state-oUGFB4ds.mjs";
6
6
  import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-B4kqWMA3.mjs";
7
7
  import { n as writeInitFile, t as initFor } from "../run-TAXNRSpD.mjs";
8
- import { $ as runningVersion, Q as runDoctor, Z as notify, et as versionBanner, i as respond, t as handleHook, zt as todayUtc } from "../handle-Dzf4JbrL.mjs";
8
+ import { $ as runningVersion, Q as runDoctor, Z as notify, et as versionBanner, i as respond, t as handleHook, zt as todayUtc } from "../handle-Boj3vnxD.mjs";
9
9
  import { delimiter, join } from "node:path";
10
10
  import { existsSync, mkdirSync, readFileSync, readSync, readdirSync, writeFileSync } from "node:fs";
11
11
  import { homedir } from "node:os";
@@ -1,5 +1,5 @@
1
1
  import { a as resolveMaxLines, c as storeBudget, i as hookBudget, l as parseEnvInt, n as DEFAULT_STDIN_MAX_BYTES, o as resolveStdinMaxBytes, r as MAX_LINES_ENV_KEY, s as splitTarget, t as DEFAULT_MAX_LINES } from "../limits-DG5pl-5s.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-DoqNlALG.mjs";
2
+ import { c as resolveTtlSec, i as parseEnvFile, l as ttlLabel, n as envCandidates, o as DEFAULT_TTL_SEC, r as loadDotenv, s as TTL_ENV_KEY, t as HOME_DIR } from "../dotenv-BLBkBTww.mjs";
3
3
  import { n as STATE_ROOT, r as projectLayout, t as STATE_GITIGNORE } from "../layout-KWoE_Mqn.mjs";
4
4
  import "../config-la_KkjCS.mjs";
5
5
  export { DEFAULT_MAX_LINES, DEFAULT_STDIN_MAX_BYTES, DEFAULT_TTL_SEC, HOME_DIR, MAX_LINES_ENV_KEY, STATE_GITIGNORE, STATE_ROOT, TTL_ENV_KEY, envCandidates, hookBudget, loadDotenv, parseEnvFile, parseEnvInt, projectLayout, resolveMaxLines, resolveStdinMaxBytes, resolveTtlSec, splitTarget, storeBudget, ttlLabel };
@@ -106,4 +106,4 @@ function loadDotenv(id, env = process.env, home = homedir(), cwd = process.cwd()
106
106
  for (const path of envCandidates(id, home, cwd)) for (const [k, v] of Object.entries(parseEnvFile(path))) if (env[k] === void 0) env[k] = v;
107
107
  }
108
108
  //#endregion
109
- export { DEFAULT_TTL_SEC as a, ttlLabel as c, parseEnvFile as i, envCandidates as n, TTL_ENV_KEY as o, loadDotenv as r, resolveTtlSec as s, HOME_DIR as t };
109
+ export { harnessHome as a, resolveTtlSec as c, parseEnvFile as i, ttlLabel as l, envCandidates as n, DEFAULT_TTL_SEC as o, loadDotenv as r, TTL_ENV_KEY as s, HOME_DIR as t };
@@ -1,5 +1,5 @@
1
1
  import { a as resolveMaxLines, l as parseEnvInt, s as splitTarget } from "./limits-DG5pl-5s.mjs";
2
- import { s as resolveTtlSec } from "./dotenv-DoqNlALG.mjs";
2
+ import { a as harnessHome, c as resolveTtlSec } from "./dotenv-BLBkBTww.mjs";
3
3
  import { r as projectLayout } from "./layout-KWoE_Mqn.mjs";
4
4
  import { i as walkUpFor, n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./project-root-3kk7gCOp.mjs";
5
5
  import { t as detectHarness } from "./harness-BMuLJ9lm.mjs";
@@ -7,13 +7,13 @@ import { D as isVendorPath, G as isFileSizeScoped, K as resolveSolidRefFramework
7
7
  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, p as evaluateFileSize, r as fusengineCache, t as claudeHome } from "./home-state-oUGFB4ds.mjs";
8
8
  import { a as writeJsonFile, i as readJsonFile, r as hashText, t as atomicWrite } from "./json-io-DisYd2fb.mjs";
9
9
  import { r as isDocConsulted } from "./doc-helpers-CWZegVdR.mjs";
10
- import { D as frameworkSolidGate, F as isExcludedSwiftPath, G as evaluateApex, H as PRE_AUTH_GATES, P as isExcludedJsPath, R as capVerbosity, S as parseField, T as usesTailwindUtilities, V as POST_AUTH_GATES, W as docConsultedGate, b as scanPlugin, c as EXCLUDE_DIRS$1, d as buildApexTaskInjection, g as buildClaudeMdContext, 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 detectCreationIntent } from "./validate-C-TB06Mw.mjs";
11
- import { n as findMarketplacePlugins, r as readPluginMeta, t as resolveSkillPath } from "./skill-path-DhBGlQe3.mjs";
10
+ import { D as frameworkSolidGate, F as isExcludedSwiftPath, G as evaluateApex, H as PRE_AUTH_GATES, P as isExcludedJsPath, R as capVerbosity, S as parseField, T as usesTailwindUtilities, V as POST_AUTH_GATES, W as docConsultedGate, b as scanPlugin, c as EXCLUDE_DIRS$1, d as buildApexTaskInjection, g as buildClaudeMdContext, 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 detectCreationIntent } from "./validate-DYPT8NY-.mjs";
11
+ import { n as findMarketplacePlugins, r as readPluginMeta, t as resolveSkillPath } from "./skill-path-DVML3zfp.mjs";
12
12
  import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
13
13
  import { a as nowStamp, l as throttleMs, n as readRoots, o as readState, s as setStateField, t as addRoot } from "./registry-IhHk2KlT.mjs";
14
14
  import { d as loadIndex, i as cacheLookupMeta, n as webfetchCacheWrite, o as cacheLookupSubstringMeta, t as mcpCacheWrite, u as extractText } from "./mcp-store-BkBDmuxN.mjs";
15
15
  import { t as loadRefs } from "./loader-AGz4nK7d.mjs";
16
- import { C as apexAuthorizationGate, S as trivialCount, _ as recordBrainstormRequired, a as journalLogPath, b as recordTarget, c as appendEvent, d as signTrack, f as verifyTrack, g as recordAgent, h as emptyTrack, i as withTrack, l as withTrackLockSync, m as agentsFresh, o as readTrackSync, p as writeLastNonce, r as trackJournalEnabled, s as diffTrackEvents, t as loadTrack, u as LOCK_FAILED, v as recordDoc, x as recordTrivialEdit, y as recordRefRead } from "./store-Dw08Amqh.mjs";
16
+ import { C as apexAuthorizationGate, S as trivialCount, _ as recordBrainstormRequired, a as journalLogPath, b as recordTarget, c as appendEvent, d as signTrack, f as verifyTrack, g as recordAgent, h as emptyTrack, i as withTrack, l as withTrackLockSync, m as agentsFresh, o as readTrackSync, p as writeLastNonce, r as trackJournalEnabled, s as diffTrackEvents, t as loadTrack, u as LOCK_FAILED, v as recordDoc, x as recordTrivialEdit, y as recordRefRead } from "./store-5-ZPKb0u.mjs";
17
17
  import { n as parseApplyPatch, t as isBypassPermissions } from "./permission-mode-BN3MNgbm.mjs";
18
18
  import { _ as commandToString, d as collectFiles, f as pathExists, g as writeText, h as spawnCapture, i as denyResponse, l as systemMessage, m as sleep, n as blockResponse, p as readText$1, r as contextResponse, s as informResponse, t as attachSystemMessage } from "./claude-CEWX-8m9.mjs";
19
19
  import { i as toKimiResponse, n as kimiDenyResponse } from "./kimi-CqLkpg1A.mjs";
@@ -893,12 +893,35 @@ function claudeMdKey(prompt, ctx) {
893
893
  return `claude-md:${hashText(prompt)}:${hashText(ctx)}`;
894
894
  }
895
895
  /**
896
+ * Extract the APEX preamble portion of a `buildClaudeMdContext` result, i.e.
897
+ * everything BEFORE the `# <docName>\n` root-doc block it prepends on dev
898
+ * prompts. Returns "" when `ctx` has no preamble (plain prompt: `ctx` starts
899
+ * directly with the root-doc heading).
900
+ * @param ctx - The full `buildClaudeMdContext` return value.
901
+ * @param docName - Root doc file name (e.g. "AGENTS.md").
902
+ * @returns The preamble text, or "" when absent.
903
+ */
904
+ function extractApexPreamble(ctx, docName) {
905
+ const heading = `# ${docName}\n`;
906
+ if (ctx.startsWith(heading)) return "";
907
+ const sep = `\n\n${heading}`;
908
+ const idx = ctx.indexOf(sep);
909
+ return idx === -1 ? "" : ctx.slice(0, idx);
910
+ }
911
+ /**
896
912
  * UserPromptSubmit context injection: render the CLAUDE.md (+ optional APEX)
897
913
  * preamble as a Claude `additionalContext` response, or "" when nothing to emit.
898
914
  * Guarded by {@link oncePerWindow} via {@link claudeMdKey}: only a
899
915
  * near-simultaneous double-fire of the SAME turn (identical prompt AND identical
900
916
  * block, within {@link DEDUP_WINDOW_MS}) is suppressed. The invariant "CLAUDE.md
901
917
  * is emitted on EVERY message" is thus preserved.
918
+ *
919
+ * On kimi specifically, the root doc body is dropped from the emitted text:
920
+ * Kimi loads `<kimiHome>/AGENTS.md` natively at session start (`apex-target.ts`),
921
+ * so re-injecting its full body on every prompt is redundant terminal noise
922
+ * (kimi has no model-only hook channel — `runtime/inform.ts`). The APEX
923
+ * preamble, which is NOT part of `AGENTS.md`, is still emitted in full when the
924
+ * prompt is dev-shaped; a plain prompt leaves only the notice.
902
925
  * @param prompt - The raw user prompt.
903
926
  * @param cwd - Project root (for project-type detection).
904
927
  * @param id - Harness target id (defaults to "claude-code" — zero-regression default).
@@ -908,7 +931,12 @@ function promptSubmitContext(prompt, cwd, id = "claude-code") {
908
931
  const ctx = buildClaudeMdContext(prompt, cwd, id);
909
932
  if (!ctx) return "";
910
933
  if (!oncePerWindow(claudeMdKey(prompt, ctx), 3e3)) return "";
911
- return renderInform(id, "UserPromptSubmit", ctx, `${apexDocName(id)} injected`);
934
+ const notice = `${apexDocName(id)} injected`;
935
+ if (id === "kimi") {
936
+ const preamble = extractApexPreamble(ctx, apexDocName(id));
937
+ return preamble ? renderInform(id, "UserPromptSubmit", preamble, notice) : notice;
938
+ }
939
+ return renderInform(id, "UserPromptSubmit", ctx, notice);
912
940
  }
913
941
  /**
914
942
  * PreToolUse Task context injection: render the APEX sub-agent context as a
@@ -1092,6 +1120,45 @@ function sessionStartCore(cwd, home = homedir(), now = Date.now(), id = "claude-
1092
1120
  return ctx ? attachSystemMessage(contextResponse("SessionStart", ctx), `${apexDocName(id)} injected`) : "";
1093
1121
  }
1094
1122
  //#endregion
1123
+ //#region src/runtime/lifecycle/kimi-rules-native.ts
1124
+ /**
1125
+ * Guard for the kimi notice-only rules injection. Kimi Code CLI has no
1126
+ * model-only hook channel (`src/runtime/inform.ts`) — every `UserPromptSubmit`
1127
+ * stdout is BOTH appended to the model's context AND rendered raw in the
1128
+ * user's terminal. `injectRules` (`inject-rules.ts`) normally re-injects the
1129
+ * full ~18 Ko rules corpus on every prompt; when the installer has already
1130
+ * merged that same corpus into `<kimiHome>/AGENTS.md` — which Kimi loads
1131
+ * natively at session start via its documented `${agents_md}` mechanism,
1132
+ * independent of hooks — the re-injection is pure noise the user has to
1133
+ * scroll past. Confirmed present, fenced, in the local install:
1134
+ * `~/.kimi-code/AGENTS.md:87` / `:335`.
1135
+ *
1136
+ * Fail-safe semantics: fences absent, file absent, or any read error ⟹
1137
+ * `false` — the caller then falls back to the full corpus. A dump is
1138
+ * verbose but never wrong; a false `true` would silently drop the rules.
1139
+ */
1140
+ /** Opening fence the fusengine installer writes around the merged corpus. */
1141
+ const FENCE_START = "<!-- fusengine:kimi-rules:start -->";
1142
+ /** Closing fence the fusengine installer writes around the merged corpus. */
1143
+ const FENCE_END = "<!-- fusengine:kimi-rules:end -->";
1144
+ /**
1145
+ * Check whether the kimi rules corpus is already present, fenced, inside
1146
+ * `<kimiHome>/AGENTS.md`.
1147
+ * @param home - Kimi home dir override (defaults to `harnessHome("kimi")`,
1148
+ * which honors `KIMI_CODE_HOME`).
1149
+ * @returns `true` only when both fences are found in a readable file.
1150
+ */
1151
+ function rulesInAgentsMd(home) {
1152
+ try {
1153
+ const agentsMd = join(home ?? harnessHome("kimi"), "AGENTS.md");
1154
+ if (!existsSync(agentsMd)) return false;
1155
+ const content = readFileSync(agentsMd, "utf-8");
1156
+ return content.includes(FENCE_START) && content.includes(FENCE_END);
1157
+ } catch {
1158
+ return false;
1159
+ }
1160
+ }
1161
+ //#endregion
1095
1162
  //#region src/runtime/lifecycle/inject-rules.ts
1096
1163
  /** Read & concatenate all `*.md` files (sorted) under `rulesDir`. */
1097
1164
  function readRules(rulesDir) {
@@ -1115,6 +1182,13 @@ function readRules(rulesDir) {
1115
1182
  * with the *actual* `hookEventName` — the spec requires it to match the firing
1116
1183
  * event (a hardcoded "SessionStart" is non-conforming and may be dropped on
1117
1184
  * UserPromptSubmit/SubagentStart).
1185
+ *
1186
+ * On kimi's `UserPromptSubmit` specifically, when the corpus is already fenced
1187
+ * inside `<kimiHome>/AGENTS.md` (native load, {@link rulesInAgentsMd}), only the
1188
+ * notice is emitted — the re-injected corpus would otherwise dump into the
1189
+ * user's terminal on every single prompt for no model-context benefit.
1190
+ * `SessionStart`/`SubagentStart` always keep the full corpus (session bootstrap
1191
+ * / sub-agent contexts need it regardless of the native AGENTS.md load).
1118
1192
  * @param pluginRoot - `CLAUDE_PLUGIN_ROOT` of the claude-rules plugin.
1119
1193
  * @param event - The firing hook event name (e.g. "SessionStart").
1120
1194
  * @param id - Harness target id (defaults to "claude-code" — zero-regression default).
@@ -1122,7 +1196,9 @@ function readRules(rulesDir) {
1122
1196
  */
1123
1197
  function injectRules(pluginRoot, event, id = "claude-code") {
1124
1198
  const content = readRules(join(pluginRoot, "rules"));
1125
- return content ? renderInform(id, event, content, "rules 00-08 injected") : "";
1199
+ if (!content) return "";
1200
+ if (id === "kimi" && event === "UserPromptSubmit" && rulesInAgentsMd()) return "rules 00-08 injected";
1201
+ return renderInform(id, event, content, "rules 00-08 injected");
1126
1202
  }
1127
1203
  //#endregion
1128
1204
  //#region src/runtime/lifecycle/solid-detect.ts
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as resolveMaxLines, c as storeBudget, i as hookBudget, l as parseEnvInt, n as DEFAULT_STDIN_MAX_BYTES, o as resolveStdinMaxBytes, r as MAX_LINES_ENV_KEY, s as splitTarget, t as DEFAULT_MAX_LINES } from "./limits-DG5pl-5s.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-DoqNlALG.mjs";
2
+ import { c as resolveTtlSec, i as parseEnvFile, l as ttlLabel, n as envCandidates, o as DEFAULT_TTL_SEC, r as loadDotenv, s as TTL_ENV_KEY, t as HOME_DIR } from "./dotenv-BLBkBTww.mjs";
3
3
  import { n as STATE_ROOT, r as projectLayout, t as STATE_GITIGNORE } from "./layout-KWoE_Mqn.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";
@@ -8,7 +8,7 @@ import { $ as matchPatterns, B as protectedPathGuard, C as isInterfacesPath, D a
8
8
  import { d as countFrameworkCodeLines, f as countLines, l as PLUGINS_DIR, p as evaluateFileSize, u as SOLID_REF } from "./home-state-oUGFB4ds.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 importsTanstackQuery, B as APEX_GATES, C as detectRequiredSkills, D as frameworkSolidGate, E as SKILL_TRIGGERS, G as evaluateApex, H as PRE_AUTH_GATES, I as MAX_EXA_RESULTS, K as freshnessGate, L as MAX_TOKENS, M as countStores, N as declaresStore, O as declaresCustomHook, R as capVerbosity, S as parseField, T as usesTailwindUtilities, U as brainstormGate, V as POST_AUTH_GATES, W as docConsultedGate, _ 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 queryCapActive, k as declaresQueryHook, l as PROJECT_INDICATORS, m as DEV_VERBS, n as missingSeoElements, o as parseEnrichment, q as solidReadGate, r as descFromText, s as parseEntry, t as isHtmlLike, u as buildApexTaskContext, w as skillTriggerGate, x as parseBodyDesc, z as detectCreationIntent } from "./validate-C-TB06Mw.mjs";
11
+ import { A as importsTanstackQuery, B as APEX_GATES, C as detectRequiredSkills, D as frameworkSolidGate, E as SKILL_TRIGGERS, G as evaluateApex, H as PRE_AUTH_GATES, I as MAX_EXA_RESULTS, K as freshnessGate, L as MAX_TOKENS, M as countStores, N as declaresStore, O as declaresCustomHook, R as capVerbosity, S as parseField, T as usesTailwindUtilities, U as brainstormGate, V as POST_AUTH_GATES, W as docConsultedGate, _ 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 queryCapActive, k as declaresQueryHook, l as PROJECT_INDICATORS, m as DEV_VERBS, n as missingSeoElements, o as parseEnrichment, q as solidReadGate, r as descFromText, s as parseEntry, t as isHtmlLike, u as buildApexTaskContext, w as skillTriggerGate, x as parseBodyDesc, z as detectCreationIntent } from "./validate-DYPT8NY-.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-IhHk2KlT.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";
@@ -1,5 +1,5 @@
1
1
  import { $ as matchPatterns, B as protectedPathGuard, C as isInterfacesPath, D as isVendorPath, E as isTypesPath, F as FILE_REDIRECT, H as CRITICAL_PATTERNS, I as SAFE_PREFIXES, J as GIT_BLOCKED, L as SESSION_STATE_FRAGMENT, M as ASK_WRITERS, N as CODE_MUTATORS, O as langOfPath, P as CODE_REDIRECT, Q as isRalphMode, R as PROTECTED_FRAGMENTS, S as isHooksPath, T as isStoresPath, U as securityGuard, V as ASK_PATTERNS, X as RALPH_SAFE, Y as PROJECT_INSTALL, Z as SYSTEM_INSTALL, a as registerGuard, at as DEV_KEYWORDS, b as interfaceDeclLevel, c as GO_DECL_RE, ct as isApexCommand, d as PY_MODEL_RE, et as detectFramework, f as RUST_DECL_RE, h as interfaceSeparationGuard, i as clearUserGuards, it as maskCommentsOnly, j as bashWriteGuard, k as lexProfileOf, l as JAVA_DECL_RE, lt as requiredArchSkill, m as TS_DECL_RE, n as FAIL_CLOSED, o as runGuards, ot as detectModularArchitecture, p as SWIFT_PROTO_RE, q as GIT_ASK, r as GUARDS, rt as maskCommentsAndStrings, s as installGuard, st as detectProjectType, t as evaluate, u as PHP_DECL_RE, v as declaresExportedTypeAlias, w as isQueryPath, x as isComponentsPath, y as declaresInterface, z as PROTECTED_GIT_RE } from "../evaluate-9bUzRquQ.mjs";
2
2
  import { d as countFrameworkCodeLines, f as countLines, l as PLUGINS_DIR, p as evaluateFileSize, u as SOLID_REF } from "../home-state-oUGFB4ds.mjs";
3
- import { A as importsTanstackQuery, B as APEX_GATES, C as detectRequiredSkills, D as frameworkSolidGate, E as SKILL_TRIGGERS, G as evaluateApex, H as PRE_AUTH_GATES, I as MAX_EXA_RESULTS, K as freshnessGate, L as MAX_TOKENS, M as countStores, N as declaresStore, O as declaresCustomHook, R as capVerbosity, S as parseField, T as usesTailwindUtilities, U as brainstormGate, V as POST_AUTH_GATES, W as docConsultedGate, _ 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 queryCapActive, k as declaresQueryHook, l as PROJECT_INDICATORS, m as DEV_VERBS, n as missingSeoElements, o as parseEnrichment, q as solidReadGate, r as descFromText, s as parseEntry, t as isHtmlLike, u as buildApexTaskContext, w as skillTriggerGate, x as parseBodyDesc, z as detectCreationIntent } from "../validate-C-TB06Mw.mjs";
3
+ import { A as importsTanstackQuery, B as APEX_GATES, C as detectRequiredSkills, D as frameworkSolidGate, E as SKILL_TRIGGERS, G as evaluateApex, H as PRE_AUTH_GATES, I as MAX_EXA_RESULTS, K as freshnessGate, L as MAX_TOKENS, M as countStores, N as declaresStore, O as declaresCustomHook, R as capVerbosity, S as parseField, T as usesTailwindUtilities, U as brainstormGate, V as POST_AUTH_GATES, W as docConsultedGate, _ 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 queryCapActive, k as declaresQueryHook, l as PROJECT_INDICATORS, m as DEV_VERBS, n as missingSeoElements, o as parseEnrichment, q as solidReadGate, r as descFromText, s as parseEntry, t as isHtmlLike, u as buildApexTaskContext, w as skillTriggerGate, x as parseBodyDesc, z as detectCreationIntent } from "../validate-DYPT8NY-.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, countStores, declaresCustomHook, declaresExportedTypeAlias, declaresInterface, declaresQueryHook, declaresStore, descFromText, detectClaudeMdProjectType, detectCreationIntent, detectFramework, detectModularArchitecture, detectProjectType, detectRequiredSkills, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, firstComment, firstHeading, frameworkSolidGate, freshnessGate, importsTanstackQuery, installGuard, interfaceDeclLevel, interfaceSeparationGuard, isApexCommand, isComponentsPath, isHooksPath, isHtmlLike, isInterfacesPath, isQueryPath, isRalphMode, isStoresPath, isTypesPath, isVendorPath, langOfPath, lexProfileOf, loadApexTaskState, maskCommentsAndStrings, maskCommentsOnly, matchPatterns, missingSeoElements, parseBodyDesc, parseEnrichment, parseEntry, parseField, protectedPathGuard, queryCapActive, registerGuard, requiredArchSkill, runGuards, securityGuard, skillTriggerGate, solidReadGate, usesTailwindUtilities };
@@ -290,6 +290,13 @@ declare function claudeMdKey(prompt: string, ctx: string): string;
290
290
  * near-simultaneous double-fire of the SAME turn (identical prompt AND identical
291
291
  * block, within {@link DEDUP_WINDOW_MS}) is suppressed. The invariant "CLAUDE.md
292
292
  * is emitted on EVERY message" is thus preserved.
293
+ *
294
+ * On kimi specifically, the root doc body is dropped from the emitted text:
295
+ * Kimi loads `<kimiHome>/AGENTS.md` natively at session start (`apex-target.ts`),
296
+ * so re-injecting its full body on every prompt is redundant terminal noise
297
+ * (kimi has no model-only hook channel — `runtime/inform.ts`). The APEX
298
+ * preamble, which is NOT part of `AGENTS.md`, is still emitted in full when the
299
+ * prompt is dev-shaped; a plain prompt leaves only the notice.
293
300
  * @param prompt - The raw user prompt.
294
301
  * @param cwd - Project root (for project-type detection).
295
302
  * @param id - Harness target id (defaults to "claude-code" — zero-regression default).
@@ -373,6 +380,13 @@ declare function readRules(rulesDir: string): string;
373
380
  * with the *actual* `hookEventName` — the spec requires it to match the firing
374
381
  * event (a hardcoded "SessionStart" is non-conforming and may be dropped on
375
382
  * UserPromptSubmit/SubagentStart).
383
+ *
384
+ * On kimi's `UserPromptSubmit` specifically, when the corpus is already fenced
385
+ * inside `<kimiHome>/AGENTS.md` (native load, {@link rulesInAgentsMd}), only the
386
+ * notice is emitted — the re-injected corpus would otherwise dump into the
387
+ * user's terminal on every single prompt for no model-context benefit.
388
+ * `SessionStart`/`SubagentStart` always keep the full corpus (session bootstrap
389
+ * / sub-agent contexts need it regardless of the native AGENTS.md load).
376
390
  * @param pluginRoot - `CLAUDE_PLUGIN_ROOT` of the claude-rules plugin.
377
391
  * @param event - The firing hook event name (e.g. "SessionStart").
378
392
  * @param id - Harness target id (defaults to "claude-code" — zero-regression default).
@@ -1,6 +1,6 @@
1
1
  import { r as projectLayout } from "../layout-KWoE_Mqn.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-oUGFB4ds.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-Dzf4JbrL.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-Boj3vnxD.mjs";
4
4
  //#region src/runtime/storage.ts
5
5
  /**
6
6
  * The project's single state dir (`<root>/.harness`) — neutral + harness-agnostic,
@@ -1,4 +1,4 @@
1
- import { t as HOME_DIR } from "./dotenv-DoqNlALG.mjs";
1
+ import { t as HOME_DIR } from "./dotenv-BLBkBTww.mjs";
2
2
  import { t as detectHarness } from "./harness-BMuLJ9lm.mjs";
3
3
  import { l as PLUGINS_DIR } from "./home-state-oUGFB4ds.mjs";
4
4
  import { join } from "node:path";
@@ -1,9 +1,9 @@
1
- import { c as ttlLabel } from "./dotenv-DoqNlALG.mjs";
1
+ import { l as ttlLabel } from "./dotenv-BLBkBTww.mjs";
2
2
  import { n as fuseHarnessHome } from "./home-state-oUGFB4ds.mjs";
3
3
  import { a as writeJsonFile, i as readJsonFile, t as atomicWrite } 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-DhBGlQe3.mjs";
6
+ import { t as resolveSkillPath } from "./skill-path-DVML3zfp.mjs";
7
7
  import { dirname, join } from "node:path";
8
8
  import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
9
9
  import { createHmac, randomBytes } from "node:crypto";
@@ -1,2 +1,2 @@
1
- import { S as trivialCount, _ as recordBrainstormRequired, b as recordTarget, g as recordAgent, h as emptyTrack, i as withTrack, m as agentsFresh, n as saveTrack, r as trackJournalEnabled, t as loadTrack, v as recordDoc, x as recordTrivialEdit, y as recordRefRead } from "../store-Dw08Amqh.mjs";
1
+ import { S as trivialCount, _ as recordBrainstormRequired, b as recordTarget, g as recordAgent, h as emptyTrack, i as withTrack, m as agentsFresh, n as saveTrack, r as trackJournalEnabled, t as loadTrack, v as recordDoc, x as recordTrivialEdit, y as recordRefRead } from "../store-5-ZPKb0u.mjs";
2
2
  export { agentsFresh, emptyTrack, loadTrack, recordAgent, recordBrainstormRequired, recordDoc, recordRefRead, recordTarget, recordTrivialEdit, saveTrack, trackJournalEnabled, trivialCount, withTrack };
@@ -1,10 +1,10 @@
1
1
  import { a as resolveMaxLines, c as storeBudget, i as hookBudget } from "./limits-DG5pl-5s.mjs";
2
- import { c as ttlLabel, t as HOME_DIR } from "./dotenv-DoqNlALG.mjs";
2
+ import { l as ttlLabel, t as HOME_DIR } from "./dotenv-BLBkBTww.mjs";
3
3
  import { A as moduleAwarePath, S as isHooksPath, T as isStoresPath, _ as rolloutVerdict, at as DEV_KEYWORDS, it as maskCommentsOnly, nt as projectCaps, rt as maskCommentsAndStrings, tt as nearestManifestDir, x as isComponentsPath, y as declaresInterface } from "./evaluate-9bUzRquQ.mjs";
4
4
  import { d as countFrameworkCodeLines, l as PLUGINS_DIR, u as SOLID_REF } from "./home-state-oUGFB4ds.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-DhBGlQe3.mjs";
7
+ import { n as findMarketplacePlugins, r as readPluginMeta, t as resolveSkillPath } from "./skill-path-DVML3zfp.mjs";
8
8
  import { dirname, extname, join, resolve } from "node:path";
9
9
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
10
10
  import { homedir } from "node:os";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusengine/harness",
3
- "version": "0.1.86",
3
+ "version": "0.1.87",
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",