@fusengine/harness 0.1.44 → 0.1.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- import { a as mcpCacheKey, c as extractText, d as summarizeIndex, f as compactMarkdown, i as cacheStore, l as IndexSummary, m as queryHash, n as cacheLookupSubstring, o as mcpCacheWrite, p as jaccardSimilar, r as cachePath, s as webfetchCacheWrite, t as cacheLookup, u as loadIndex } from "../index-BX-xcvhY.mjs";
2
- export { IndexSummary, cacheLookup, cacheLookupSubstring, cachePath, cacheStore, compactMarkdown, extractText, jaccardSimilar, loadIndex, mcpCacheKey, mcpCacheWrite, queryHash, summarizeIndex, webfetchCacheWrite };
1
+ import { _ as queryHash, a as cacheLookupSubstringMeta, c as mcpCacheKey, d as extractText, f as IndexSummary, g as jaccardSimilar, h as compactMarkdown, i as cacheLookupSubstring, l as mcpCacheWrite, m as summarizeIndex, n as cacheLookup, o as cachePath, p as loadIndex, r as cacheLookupMeta, s as cacheStore, t as CacheHit, u as webfetchCacheWrite } from "../index-BUwEmIK-.mjs";
2
+ export { CacheHit, IndexSummary, cacheLookup, cacheLookupMeta, cacheLookupSubstring, cacheLookupSubstringMeta, cachePath, cacheStore, compactMarkdown, extractText, jaccardSimilar, loadIndex, mcpCacheKey, mcpCacheWrite, queryHash, summarizeIndex, webfetchCacheWrite };
@@ -1,3 +1,3 @@
1
- import { a as cachePath, c as extractText, d as compactMarkdown, f as jaccardSimilar, i as cacheLookupSubstring, l as loadIndex, n as webfetchCacheWrite, o as cacheStore, p as queryHash, r as cacheLookup, s as mcpCacheKey, t as mcpCacheWrite, u as summarizeIndex } from "../mcp-store-CnsW9oFj.mjs";
1
+ import { a as cacheLookupSubstring, c as cacheStore, d as loadIndex, f as summarizeIndex, h as queryHash, i as cacheLookupMeta, l as mcpCacheKey, m as jaccardSimilar, n as webfetchCacheWrite, o as cacheLookupSubstringMeta, p as compactMarkdown, r as cacheLookup, s as cachePath, t as mcpCacheWrite, u as extractText } from "../mcp-store-BkBDmuxN.mjs";
2
2
  import "../cache-la_KkjCS.mjs";
3
- export { cacheLookup, cacheLookupSubstring, cachePath, cacheStore, compactMarkdown, extractText, jaccardSimilar, loadIndex, mcpCacheKey, mcpCacheWrite, queryHash, summarizeIndex, webfetchCacheWrite };
3
+ export { cacheLookup, cacheLookupMeta, cacheLookupSubstring, cacheLookupSubstringMeta, cachePath, cacheStore, compactMarkdown, extractText, jaccardSimilar, loadIndex, mcpCacheKey, mcpCacheWrite, queryHash, summarizeIndex, webfetchCacheWrite };
package/dist/cli/bin.mjs CHANGED
@@ -4,7 +4,7 @@ import { t as detectHarness } from "../harness-C8Nxxyn_.mjs";
4
4
  import { t as claudeHome } from "../home-state-mKZxP4oZ.mjs";
5
5
  import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-DLXtA5DH.mjs";
6
6
  import { n as writeInitFile, t as initFor } from "../run-Do2JltgU.mjs";
7
- import { Mt as todayUtc, t as handleHook } from "../handle-CtMMVoxT.mjs";
7
+ import { Mt as todayUtc, t as handleHook } from "../handle-DJaaD2BT.mjs";
8
8
  import { delimiter, dirname, join } from "node:path";
9
9
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
10
10
  import { homedir } from "node:os";
@@ -1,14 +1,14 @@
1
1
  import { a as parseEnvInt, i as splitTarget, r as resolveMaxLines } from "./limits-CHn8AIL1.mjs";
2
2
  import { r as projectLayout } from "./layout-C0jaaCQC.mjs";
3
3
  import { i as walkUpFor, n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./project-root-3kk7gCOp.mjs";
4
- import { A as detectCreationIntent, B as detectProjectType$1, C as skillTriggerGate, E as frameworkSolidGate, H as requiredArchSkill, L as detectFramework, N as docConsultedGate, P as evaluateApex, _ as scanPlugin, b as findMarketplacePlugins, c as EXCLUDE_DIRS$1, d as buildApexTaskInjection, h as buildClaudeMdContext, k as capVerbosity, l as PROJECT_INDICATORS, n as missingSeoElements, o as parseEnrichment, r as descFromText, s as parseEntry, t as isHtmlLike, x as readPluginMeta, y as parseField, z as detectModularArchitecture } from "./validate-Ca7NSp-r.mjs";
5
- import { F as countFrameworkCodeLines, I as countLines, n as FAIL_CLOSED, t as evaluate } from "./evaluate-d7Pp8XJH.mjs";
4
+ import { A as detectCreationIntent, B as detectProjectType$1, C as findMarketplacePlugins, E as frameworkSolidGate, H as requiredArchSkill, L as detectFramework, N as docConsultedGate, P as evaluateApex, _ as scanPlugin, c as EXCLUDE_DIRS$1, d as buildApexTaskInjection, h as buildClaudeMdContext, k as capVerbosity, l as PROJECT_INDICATORS, n as missingSeoElements, o as parseEnrichment, r as descFromText, s as parseEntry, t as isHtmlLike, w as readPluginMeta, x as skillTriggerGate, y as parseField, z as detectModularArchitecture } from "./validate-DcQaKzau.mjs";
5
+ import { F as countFrameworkCodeLines, I as countLines, N as PLUGINS_DIR, n as FAIL_CLOSED, t as evaluate } from "./evaluate-d7Pp8XJH.mjs";
6
6
  import { a as writeJsonFile, i as readJsonFile, r as hashText, t as atomicWrite } from "./json-io-DisYd2fb.mjs";
7
7
  import { a as sanitizeSessionId, c as sessionsDir, i as loadSessionState, n as fuseHarnessHome, o as saveSessionState, r as fusengineCache, t as claudeHome } from "./home-state-mKZxP4oZ.mjs";
8
8
  import { r as isDocConsulted } from "./doc-helpers-CWZegVdR.mjs";
9
9
  import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
10
10
  import { a as nowStamp, l as throttleMs, n as readRoots, o as readState, s as setStateField, t as addRoot } from "./registry-CymilZiZ.mjs";
11
- import { c as extractText, i as cacheLookupSubstring, l as loadIndex, n as webfetchCacheWrite, r as cacheLookup, t as mcpCacheWrite } from "./mcp-store-CnsW9oFj.mjs";
11
+ import { d as loadIndex, i as cacheLookupMeta, n as webfetchCacheWrite, o as cacheLookupSubstringMeta, t as mcpCacheWrite, u as extractText } from "./mcp-store-BkBDmuxN.mjs";
12
12
  import { t as loadRefs } from "./loader-AGz4nK7d.mjs";
13
13
  import { c as recordDoc, d as trivialCount, i as agentsFresh, l as recordRefRead, n as saveTrack, o as recordAgent, r as verifyTrack, s as recordBrainstormRequired, t as loadTrack, u as recordTrivialEdit } from "./store-CNjFenWe.mjs";
14
14
  import { d as readText, f as sleep, l as collectFiles, m as writeText, n as contextResponse, p as spawnCapture, r as denyResponse, s as systemMessage, t as blockResponse, u as pathExists } from "./claude-DLh0fHWM.mjs";
@@ -3709,7 +3709,8 @@ function dryGate(tool, filePath, content, cwd) {
3709
3709
  const dup = detectDuplication(filePath, content, cwd);
3710
3710
  if (dup.duplicates.length === 0) return null;
3711
3711
  const names = dup.names.slice(0, 5).join(", ");
3712
- const files = dup.duplicates.slice(0, 3).join(", ");
3712
+ let files = dup.duplicates.slice(0, 3).join(", ");
3713
+ if (dup.duplicates.length > 3) files += ` (+${dup.duplicates.length - 3} more)`;
3713
3714
  if (dup.duplicates.length === 1) return {
3714
3715
  kind: "inform",
3715
3716
  title: "Possible duplicate code (DRY)",
@@ -3834,15 +3835,15 @@ const block$1 = (reason) => ({
3834
3835
  function nextModular(filePath, content, cwd) {
3835
3836
  const rel = relative(cwd, filePath);
3836
3837
  const bn = basename(filePath);
3837
- if ((rel.startsWith("app/") || rel.startsWith("src/app/")) && !NEXT_CONVENTION.test(bn) && !NEXT_STATIC.test(bn)) return block$1(`BLOCKED: modular Next.js — '${bn}' is not an app/ convention file. Move business logic to modules/[feature]/.`);
3838
+ if ((rel.startsWith("app/") || rel.startsWith("src/app/")) && !NEXT_CONVENTION.test(bn) && !NEXT_STATIC.test(bn)) return block$1(`BLOCKED: modular Next.js — '${bn}' is not an app/ convention file. Move business logic to modules/[feature]/. Only page.tsx, layout.tsx, route.ts allowed in app/.`);
3838
3839
  const mod = filePath.match(/\/modules\/([^/]+)\//);
3839
3840
  if (!mod) return null;
3840
3841
  const current = mod[1] ?? "";
3841
3842
  for (const m of content.matchAll(/from\s+['"][@.][^'"]*?\/modules\/([^/]+)\//g)) {
3842
3843
  const imported = m[1] ?? "";
3843
3844
  if (current === "cores") {
3844
- if (imported !== "cores" && imported !== "core") return block$1(`BLOCKED: modules/cores/ must not import from modules/${imported}/.`);
3845
- } else if (imported !== current && imported !== "cores" && imported !== "core") return block$1(`BLOCKED: cross-module import — '${current}' imports '${imported}'. Only modules/cores/ is shared.`);
3845
+ if (imported !== "cores" && imported !== "core") return block$1(`BLOCKED: modules/cores/ must not import from modules/${imported}/. Cores must be independent.`);
3846
+ } else if (imported !== current && imported !== "cores" && imported !== "core") return block$1(`BLOCKED: cross-module import — '${current}' imports from '${imported}'. Only modules/cores/ is shared.`);
3846
3847
  }
3847
3848
  return null;
3848
3849
  }
@@ -3929,6 +3930,8 @@ const EXEMPT_RE$1 = /(^|\/)(node_modules|dist|build)\//;
3929
3930
  const SHADCN_PATH_RE = /(components|ui|shadcn|components\.json)/;
3930
3931
  /** A read reference proving one of the two named base skills was consulted (Phase 1). */
3931
3932
  const SHADCN_BASE_SKILL_RE = /skills\/(shadcn-detection|shadcn-components)\//;
3933
+ /** Session `authorizations` source proving a live `mcp__shadcn__*` call was made this session (Phase 1, option 3). */
3934
+ const SHADCN_MCP_SOURCE_RE = /shadcn-mcp/i;
3932
3935
  /**
3933
3936
  * True when this Write/Edit is shadcn-scoped: a `.tsx/.jsx/.css/.scss/.json`
3934
3937
  * file under a `components|ui|shadcn|components.json` path, outside vendored
@@ -3943,6 +3946,11 @@ function isShadcnWrite(tool, filePath) {
3943
3946
  function shadcnBaseSkillRead(refsRead) {
3944
3947
  return refsRead.some((p) => SHADCN_BASE_SKILL_RE.test(p));
3945
3948
  }
3949
+ /** True when a `mcp__shadcn__*` tool call was recorded this session (Phase 1's 3rd unblock option). */
3950
+ function shadcnMcpConsulted(authorizations, sessionId) {
3951
+ if (!authorizations) return false;
3952
+ return Object.values(authorizations).some((a) => a.doc_sessions?.includes(sessionId) && (a.sources ?? (a.source ? [a.source] : [])).some((s) => SHADCN_MCP_SOURCE_RE.test(s)));
3953
+ }
3946
3954
  /**
3947
3955
  * Gate a shadcn-scoped write. Phase 1 requires a base skill read
3948
3956
  * (shadcn-detection or shadcn-components); Phase 2 requires the domain
@@ -3954,11 +3962,11 @@ function shadcnBaseSkillRead(refsRead) {
3954
3962
  */
3955
3963
  function shadcnSkillGate(tool, filePath, content, ev) {
3956
3964
  if (!isShadcnWrite(tool, filePath)) return null;
3957
- if (!shadcnBaseSkillRead(ev.refsRead)) return {
3965
+ if (!shadcnBaseSkillRead(ev.refsRead) && !shadcnMcpConsulted(ev.authorizations, ev.sessionId)) return {
3958
3966
  kind: "block",
3959
3967
  title: "shadcn/ui skill",
3960
- reason: "BLOCKED: shadcn skill not consulted. Read shadcn-detection/SKILL.md or shadcn-components/SKILL.md, then retry.",
3961
- actions: ["Read skills/shadcn-detection/ or skills/shadcn-components/, then retry"]
3968
+ reason: "BLOCKED: shadcn skill not consulted. Read shadcn-detection/SKILL.md or shadcn-components/SKILL.md, or use mcp__shadcn__search_items_in_registries, then retry.",
3969
+ actions: ["Read skills/shadcn-detection/ or skills/shadcn-components/, or call mcp__shadcn__search_items_in_registries, then retry"]
3962
3970
  };
3963
3971
  const domainBlock = skillTriggerGate("shadcn", content, ev.refsRead);
3964
3972
  if (domainBlock) return domainBlock;
@@ -4283,6 +4291,16 @@ function docSourceOf$1(tool) {
4283
4291
  if (/context7/i.test(tool)) return "context7";
4284
4292
  }
4285
4293
  /**
4294
+ * Wrap a cache hit's body in the harness-facing `CACHE HIT` notice — parity with
4295
+ * `webfetch-cache-lookup.py` / `mcp-cache-lookup.py` (KB = body chars/1024 + 1,
4296
+ * age hours = ageMs/3_600_000, both floored).
4297
+ */
4298
+ function cacheHitText(web, hit) {
4299
+ const kb = Math.floor(hit.body.length / 1024) + 1;
4300
+ const hours = Math.floor(hit.ageMs / 36e5);
4301
+ return web ? `CACHE HIT WebFetch (~${kb}KB economise, cached il y a ${hours}h):\n\n${hit.body}\n\nPour forcer un nouveau fetch, modifie l'URL ou la query.` : `CACHE HIT (~${kb}KB economise, cached il y a ${hours}h): ${hit.body}\n\nReformule pour forcer un re-call.`;
4302
+ }
4303
+ /**
4286
4304
  * Pre-event MCP interception: serve a fresh cache hit (deny + cached content),
4287
4305
  * else cap exa verbosity (allow + mutated input), else null to allow normally.
4288
4306
  * Harnesses without input-mutation/cache support fall through to null.
@@ -4292,9 +4310,9 @@ function mcpPreIntercept(id, tool, input, dir, ttlMs, now) {
4292
4310
  const web = isWebFetch(tool);
4293
4311
  const ttl = web ? WEBFETCH_TTL_MS : ttlMs;
4294
4312
  const key = cacheQueryOf(tool, input);
4295
- const cached = cacheLookup(dir, tool, key, ttl, now) ?? (web ? null : cacheLookupSubstring(dir, key, ttl, now));
4296
- if (cached) {
4297
- const served = denyWith(id, cached);
4313
+ const hit = cacheLookupMeta(dir, tool, key, ttl, now) ?? (web ? null : cacheLookupSubstringMeta(dir, key, ttl, now));
4314
+ if (hit) {
4315
+ const served = denyWith(id, cacheHitText(web, hit));
4298
4316
  if (served) return {
4299
4317
  stdout: served,
4300
4318
  docSource: docSourceOf$1(tool)
@@ -4613,17 +4631,6 @@ const FORBIDDEN_FONTS = [
4613
4631
  "Open Sans"
4614
4632
  ];
4615
4633
  const OKLCH_RE = /oklch\(\s*[\d.]+%?\s+0\.0*[1-9]/;
4616
- const KNOWN_DOMAINS = [
4617
- "framer.website",
4618
- "webflow.io",
4619
- "awwwards.com",
4620
- "godly.website",
4621
- "lapa.ninja",
4622
- "onepagelove.com",
4623
- "saasframe.io",
4624
- "bestwebsite.gallery",
4625
- "landingfolio.com"
4626
- ];
4627
4634
  const deny = (reason) => ({
4628
4635
  kind: "block",
4629
4636
  title: "Design pipeline",
@@ -4633,19 +4640,11 @@ const deny = (reason) => ({
4633
4640
  /** Block the design agent from writing anything but .html/.css/.md/.json. */
4634
4641
  function htmlCssOnlyGate(filePath) {
4635
4642
  if (EXEMPT_DIRS.some((d) => filePath.includes(d)) || ALLOWED_WRITE.test(filePath)) return null;
4636
- return deny("BLOCKED: design-expert can only write .html, .css, .md, and .json files.");
4643
+ return deny("BLOCKED: design-expert can only write .html, .css, .md, and .json files. Framework files (.tsx, .astro, .vue, .swift, .php) must be written by the domain expert (astro-expert, react-expert, etc.) AFTER design validation.");
4637
4644
  }
4638
4645
  /** Block edits to the harness-managed `.design-state-*` files (read-only to the agent). */
4639
4646
  function stateFileGate(filePath) {
4640
- return filePath.includes(".design-state-") ? deny("BLOCKED: .design-state files are read-only; the hooks update them as you progress.") : null;
4641
- }
4642
- /** Gate writing design-system.md: requires phase ≥ 2 and the per-mode screenshot quota. */
4643
- function designSystemWriteGate(filePath, state) {
4644
- if (!filePath.endsWith("design-system.md")) return null;
4645
- if (state.currentPhase < 2) return deny(`BLOCKED: cannot write design-system.md at phase ${state.currentPhase}. Read identity + inspiration, then browse & screenshot first.`);
4646
- const needed = MIN_SCREENSHOTS[state.mode];
4647
- if (state.screenshotsCount < needed) return deny(`BLOCKED: ${state.screenshotsCount}/${needed} fuse-browser screenshots for mode '${state.mode}'. Take ${needed - state.screenshotsCount} more (fullPage).`);
4648
- return null;
4647
+ return filePath.includes(".design-state-") ? deny("BLOCKED: .design-state files are READ-ONLY for you. Hooks update them automatically as you progress. Do NOT try to modify them — it will not unblock you. Follow the pipeline: Phase 0→1→2→3→4→5→6 in order.") : null;
4649
4648
  }
4650
4649
  /**
4651
4650
  * Ports browser_helpers.is_exempt: design-system.md is NOT exempt (it must meet
@@ -4675,19 +4674,6 @@ function validateDesignSystem(content) {
4675
4674
  if (FORBIDDEN_FONTS.some((f) => content.includes(f))) missing.push("forbidden font (Inter/Roboto/Arial/Open Sans)");
4676
4675
  return missing;
4677
4676
  }
4678
- /** Gate Gemini create_frontend: requires phase ≥ 3 and a validated design system. */
4679
- function geminiCreateGate(state) {
4680
- if (state.currentPhase < 3) return deny("BLOCKED: cannot call create_frontend before phase 3. Finish screenshots and write a valid design-system.md.");
4681
- if (!state.designSystemValid) return deny("BLOCKED: design-system.md not validated (needs ## Design Reference, OKLCH, typography, reference URL).");
4682
- return null;
4683
- }
4684
- /** Gate fuse-browser navigate: phase ≥ 1, inspiration read, URL in the catalog. */
4685
- function browserNavigateGate(state, url) {
4686
- if (state.currentPhase < 1) return deny("BLOCKED: read identity templates + design-inspiration.md before browsing.");
4687
- if (!state.inspirationRead) return deny("BLOCKED: design-inspiration.md not read yet — read it then pick catalog URLs.");
4688
- if (url && !KNOWN_DOMAINS.some((d) => url.includes(d))) return deny(`BLOCKED: '${url}' is not in the catalog. Use design-inspiration-urls.md domains.`);
4689
- return null;
4690
- }
4691
4677
  /** Gate a screenshot: require a scroll since the last navigate (lazy-load content). */
4692
4678
  function screenshotScrollGate(state) {
4693
4679
  return state.scrolledSinceNav ? null : deny("BLOCKED: scroll the page before a screenshot — browser_scroll to:'end', wait, scroll back, then fullPage screenshot.");
@@ -4698,6 +4684,41 @@ function geminiEnabled() {
4698
4684
  return v === "1" || v === "true";
4699
4685
  }
4700
4686
  //#endregion
4687
+ //#region src/policy/design/gates-pipeline.ts
4688
+ const SKILLS = `${PLUGINS_DIR}/design-expert/skills`;
4689
+ const KNOWN_DOMAINS = [
4690
+ "framer.website",
4691
+ "webflow.io",
4692
+ "awwwards.com",
4693
+ "godly.website",
4694
+ "lapa.ninja",
4695
+ "onepagelove.com",
4696
+ "saasframe.io",
4697
+ "bestwebsite.gallery",
4698
+ "landingfolio.com"
4699
+ ];
4700
+ /** Gate writing design-system.md: requires phase >= 2 and the per-mode screenshot quota. */
4701
+ function designSystemWriteGate(filePath, state) {
4702
+ if (!filePath.endsWith("design-system.md")) return null;
4703
+ if (state.currentPhase < 2) return deny(`BLOCKED: Cannot write design-system.md at phase ${state.currentPhase}. RECOVERY: 1) Read identity templates from skills/0-identity-system/ 2) Read design-inspiration.md 3) Browse and screenshot sites 4) Then write design-system.md`);
4704
+ const needed = MIN_SCREENSHOTS[state.mode];
4705
+ if (state.screenshotsCount < needed) return deny(`BLOCKED: ${state.screenshotsCount}/${needed} screenshots for mode '${state.mode}'. RECOVERY: 1) Take ${needed - state.screenshotsCount} more fuse-browser screenshots 2) Use browser_open + browser_navigate + browser_screenshot fullPage:true 3) Then write design-system.md`);
4706
+ return null;
4707
+ }
4708
+ /** Gate Gemini create_frontend: requires phase >= 3 and a validated design system. */
4709
+ function geminiCreateGate(state) {
4710
+ if (state.currentPhase < 3) return deny("BLOCKED: Cannot call Gemini create_frontend before phase 3. RECOVERY: 1) Complete screenshot browsing phase 2) Write a valid design-system.md 3) Then call mcp__gemini-design__create_frontend");
4711
+ if (!state.designSystemValid) return deny("BLOCKED: design-system.md not validated. RECOVERY: 1) Ensure design-system.md has ## Design Reference, OKLCH tokens, typography pair, reference URL 2) Then retry mcp__gemini-design__create_frontend");
4712
+ return null;
4713
+ }
4714
+ /** Gate fuse-browser navigate: phase >= 1, inspiration read, URL in the catalog. */
4715
+ function browserNavigateGate(state, url) {
4716
+ if (state.currentPhase < 1) return deny(`BLOCKED: Phase 0 not done. READ: ${SKILLS}/0-identity-system/SKILL.md first.`);
4717
+ if (!state.inspirationRead) return deny(`BLOCKED: Read inspiration catalog first. READ: ${SKILLS}/1-designing-systems/references/design-inspiration.md + design-inspiration-urls.md`);
4718
+ if (url && !KNOWN_DOMAINS.some((d) => url.includes(d))) return deny(`BLOCKED: '${url}' not in catalog. Use URLs from design-inspiration-urls.md. Domains: ${KNOWN_DOMAINS.join(", ")}`);
4719
+ return null;
4720
+ }
4721
+ //#endregion
4701
4722
  //#region src/runtime/design.ts
4702
4723
  const NAV = "mcp__fuse-browser__browser_navigate";
4703
4724
  const SHOT = "mcp__fuse-browser__browser_screenshot";
@@ -4878,6 +4899,7 @@ function docSourceOf(tool) {
4878
4899
  if (/browser_(fetch|crawl|serp)/i.test(tool)) return "fuse-browser";
4879
4900
  if (tool === "WebSearch") return "websearch";
4880
4901
  if (tool === "WebFetch") return "webfetch";
4902
+ if (/^mcp__shadcn__/i.test(tool)) return "shadcn-mcp";
4881
4903
  }
4882
4904
  /**
4883
4905
  * Map a live tool-use to the activity to record, or null when nothing is
@@ -57,8 +57,15 @@ declare function webfetchCacheWrite(dir: string, tool: string, key: string, body
57
57
  declare function mcpCacheKey(tool: string, query: string): string;
58
58
  /** Path of a cache entry under `dir`. */
59
59
  declare function cachePath(dir: string, tool: string, query: string): string;
60
+ /** A cache hit's body plus its age in ms (now − mtime), for caller-side reporting. */
61
+ interface CacheHit {
62
+ body: string;
63
+ ageMs: number;
64
+ }
60
65
  /** Read a cached entry if it exists and is fresh (mtime within `ttlMs`), else null. */
61
66
  declare function cacheLookup(dir: string, tool: string, query: string, ttlMs: number, now: number): string | null;
67
+ /** Like {@link cacheLookup}, but also reports the entry's age (for `CACHE HIT` wrapper text). */
68
+ declare function cacheLookupMeta(dir: string, tool: string, query: string, ttlMs: number, now: number): CacheHit | null;
62
69
  /**
63
70
  * First fresh cache file whose body contains the (normalized, case-insensitive)
64
71
  * query substring, else null. Relaxes the exact-key match to lift the hit-rate —
@@ -69,7 +76,9 @@ declare function cacheLookup(dir: string, tool: string, query: string, ttlMs: nu
69
76
  * @param now - Current epoch ms.
70
77
  */
71
78
  declare function cacheLookupSubstring(dir: string, query: string, ttlMs: number, now: number): string | null;
79
+ /** Like {@link cacheLookupSubstring}, but also reports the matched entry's age. */
80
+ declare function cacheLookupSubstringMeta(dir: string, query: string, ttlMs: number, now: number): CacheHit | null;
72
81
  /** Store a cache entry (creates the dir; no-op on empty content). */
73
82
  declare function cacheStore(dir: string, tool: string, query: string, content: string): void;
74
83
  //#endregion
75
- export { mcpCacheKey as a, extractText as c, summarizeIndex as d, compactMarkdown as f, cacheStore as i, IndexSummary as l, queryHash as m, cacheLookupSubstring as n, mcpCacheWrite as o, jaccardSimilar as p, cachePath as r, webfetchCacheWrite as s, cacheLookup as t, loadIndex as u };
84
+ export { queryHash as _, cacheLookupSubstringMeta as a, mcpCacheKey as c, extractText as d, IndexSummary as f, jaccardSimilar as g, compactMarkdown as h, cacheLookupSubstring as i, mcpCacheWrite as l, summarizeIndex as m, cacheLookup as n, cachePath as o, loadIndex as p, cacheLookupMeta as r, cacheStore as s, CacheHit as t, webfetchCacheWrite as u };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as PromptKind, r as formatPrompt, t as Prompt } from "./types-D56jSgD9.mjs";
2
- import { a as mcpCacheKey, c as extractText, d as summarizeIndex, f as compactMarkdown, i as cacheStore, l as IndexSummary, m as queryHash, n as cacheLookupSubstring, o as mcpCacheWrite, p as jaccardSimilar, r as cachePath, s as webfetchCacheWrite, t as cacheLookup, u as loadIndex } from "./index-BX-xcvhY.mjs";
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
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-BXPySPxE.mjs";
4
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-BPPu5CrN.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-BNfYWvYv.mjs";
@@ -11,4 +11,4 @@ import { a as ReminderState, c as readState, d as throttleMs, i as registryFile,
11
11
  import { a as globToRe, i as scoreReferences, n as toRefMeta, o as parseFrontmatter, r as routeReferences, t as loadRefs } from "./index-mmRF3KNp.mjs";
12
12
  import { a as taskStart, c as ensureStateDir, d as stateFilePath, f as acquireLock, i as taskCreate, l as loadState, n as ApexTaskFile, o as ApexState, r as taskComplete, s as apexStateDir, t as ApexTask, u as saveState } from "./index-BA-SqNR7.mjs";
13
13
  import { _ as TIME_INTERVALS, a as formatCost, c as formatTokens, d as colors, f as progressiveColor, g as PROGRESS_CHARS, h as PROGRESS_BAR_DEFAULTS, i as formatBasename, l as ColorFn, m as GRADIENT_BLOCKS, n as generateGradientBar, o as formatPath, p as COLOR_THRESHOLDS, r as generateProgressBar, s as formatTimeLeft, t as ProgressBarOptions, u as Palette } from "./index-BWK8slRi.mjs";
14
- export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, ApexContext, ApexGate, ApexState, ApexTask, ApexTaskFile, ApexTaskState, AuthEntry, CODE_MUTATORS, CODE_REDIRECT, COLOR_THRESHOLDS, CRITICAL_PATTERNS, ColorFn, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, DEV_VERBS, DocSatisfactionStatus, EXCLUDE_DIRS, FAIL_CLOSED, FILE_REDIRECT, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, GO_DECL_RE, GRADIENT_BLOCKS, GUARDS, Guard, GuardContext, HOME_DIR, type HarnessId, type HarnessInfo, type HarnessMode, type HarnessVia, IndexSummary, JAVA_DECL_RE, LabeledPattern, MAX_EXA_RESULTS, MAX_LINES_ENV_KEY, MAX_TOKENS, ModularArchitecture, PHP_DECL_RE, PLUGINS_DIR, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, Palette, type PolicyContext, type PolicyResult, ProgressBarOptions, ProjectLayout, ProjectType, Prompt, PromptKind, RefMeta, ReminderState, RouteResult, SAFE_PREFIXES, SESSION_STATE_FRAGMENT, SKILL_TRIGGERS, SOLID_REF, STATE_GITIGNORE, STATE_ROOT, SWIFT_PROTO_RE, SYSTEM_INSTALL, ScoredRef, TIME_INTERVALS, TS_DECL_RE, TTL_ENV_KEY, TreeEntry, acquireLock, addRoot, apexStateDir, bashWriteGuard, brainstormGate, buildApexInstruction, buildApexTaskContext, buildApexTaskInjection, buildClaudeMdContext, cacheLookup, cacheLookupSubstring, cachePath, cacheStore, capVerbosity, clearUserGuards, colors, compactJson, compactMarkdown, countFrameworkCodeLines, countLines, descFromText, detectClaudeMdProjectType, detectCreationIntent, detectFramework, detectHarness, detectMode, detectModularArchitecture, detectProjectType, detectRequiredSkills, docConsultedGate, ensureMemoryGitignore, ensureStateDir, envCandidates, evaluate, evaluateApex, evaluateFileSize, extractText, firstComment, firstHeading, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, frameworkSolidGate, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, installGuard, interfaceSeparationGuard, isApexCommand, isCodeFile, isDocConsulted, isHtmlLike, jaccardSimilar, lessonsFileFor, loadApexTaskState, loadDotenv, loadIndex, loadRefs, loadState, matchPatterns, mcpCacheKey, mcpCacheWrite, missingSeoElements, modeFor, nowStamp, parseBodyDesc, parseEnrichment, parseEntry, parseEnvFile, parseEnvInt, parseField, parseFrontmatter, progressiveColor, projectLayout, projectRoot, projectRootOrNull, protectedPathGuard, queryHash, readRoots, readState, registerGuard, registryFile, requiredArchSkill, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, runGuards, saveState, scoreReferences, securityGuard, setStateField, skillTriggerGate, solidReadGate, splitTarget, stateFileFor, stateFilePath, summarizeIndex, taskComplete, taskCreate, taskStart, throttleMs, toRefMeta, ttlLabel, usesTailwindUtilities, walkUpFor, webfetchCacheWrite };
14
+ export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, ApexContext, ApexGate, ApexState, ApexTask, ApexTaskFile, ApexTaskState, AuthEntry, CODE_MUTATORS, CODE_REDIRECT, COLOR_THRESHOLDS, CRITICAL_PATTERNS, CacheHit, ColorFn, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, DEV_VERBS, DocSatisfactionStatus, EXCLUDE_DIRS, FAIL_CLOSED, FILE_REDIRECT, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, GO_DECL_RE, GRADIENT_BLOCKS, GUARDS, Guard, GuardContext, HOME_DIR, type HarnessId, type HarnessInfo, type HarnessMode, type HarnessVia, IndexSummary, JAVA_DECL_RE, LabeledPattern, MAX_EXA_RESULTS, MAX_LINES_ENV_KEY, MAX_TOKENS, ModularArchitecture, PHP_DECL_RE, PLUGINS_DIR, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, Palette, type PolicyContext, type PolicyResult, ProgressBarOptions, ProjectLayout, ProjectType, Prompt, PromptKind, RefMeta, ReminderState, RouteResult, SAFE_PREFIXES, SESSION_STATE_FRAGMENT, SKILL_TRIGGERS, SOLID_REF, STATE_GITIGNORE, STATE_ROOT, SWIFT_PROTO_RE, SYSTEM_INSTALL, ScoredRef, TIME_INTERVALS, TS_DECL_RE, TTL_ENV_KEY, TreeEntry, acquireLock, addRoot, apexStateDir, bashWriteGuard, brainstormGate, buildApexInstruction, buildApexTaskContext, buildApexTaskInjection, buildClaudeMdContext, cacheLookup, cacheLookupMeta, cacheLookupSubstring, cacheLookupSubstringMeta, cachePath, cacheStore, capVerbosity, clearUserGuards, colors, compactJson, compactMarkdown, countFrameworkCodeLines, countLines, descFromText, detectClaudeMdProjectType, detectCreationIntent, detectFramework, detectHarness, detectMode, detectModularArchitecture, detectProjectType, detectRequiredSkills, docConsultedGate, ensureMemoryGitignore, ensureStateDir, envCandidates, evaluate, evaluateApex, evaluateFileSize, extractText, firstComment, firstHeading, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, frameworkSolidGate, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, installGuard, interfaceSeparationGuard, isApexCommand, isCodeFile, isDocConsulted, isHtmlLike, jaccardSimilar, lessonsFileFor, loadApexTaskState, loadDotenv, loadIndex, loadRefs, loadState, matchPatterns, mcpCacheKey, mcpCacheWrite, missingSeoElements, modeFor, nowStamp, parseBodyDesc, parseEnrichment, parseEntry, parseEnvFile, parseEnvInt, parseField, parseFrontmatter, progressiveColor, projectLayout, projectRoot, projectRootOrNull, protectedPathGuard, queryHash, readRoots, readState, registerGuard, registryFile, requiredArchSkill, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, runGuards, saveState, scoreReferences, securityGuard, setStateField, skillTriggerGate, solidReadGate, splitTarget, stateFileFor, stateFilePath, summarizeIndex, taskComplete, taskCreate, taskStart, throttleMs, toRefMeta, ttlLabel, usesTailwindUtilities, walkUpFor, webfetchCacheWrite };
package/dist/index.mjs CHANGED
@@ -4,15 +4,15 @@ import { n as STATE_ROOT, r as projectLayout, t as STATE_GITIGNORE } from "./lay
4
4
  import { t as compactJson } from "./compact-json-DK2nX-MK.mjs";
5
5
  import { i as walkUpFor, n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./project-root-3kk7gCOp.mjs";
6
6
  import { n as detectMode, r as modeFor, t as detectHarness } from "./harness-C8Nxxyn_.mjs";
7
- import { A as detectCreationIntent, B as detectProjectType, C as skillTriggerGate, D as MAX_EXA_RESULTS, E as frameworkSolidGate, F as freshnessGate, H as requiredArchSkill, I as solidReadGate, L as detectFramework, M as brainstormGate, N as docConsultedGate, O as MAX_TOKENS, P as evaluateApex, R as DEV_KEYWORDS, S as detectRequiredSkills, T as SKILL_TRIGGERS, V as isApexCommand, a as firstHeading, c as EXCLUDE_DIRS, d as buildApexTaskInjection, f as loadApexTaskState, g as detectClaudeMdProjectType, h as buildClaudeMdContext, i as firstComment, j as APEX_GATES, k as capVerbosity, l as PROJECT_INDICATORS, m as buildApexInstruction, n as missingSeoElements, o as parseEnrichment, p as DEV_VERBS, r as descFromText, s as parseEntry, t as isHtmlLike, u as buildApexTaskContext, v as parseBodyDesc, w as usesTailwindUtilities, y as parseField, z as detectModularArchitecture } from "./validate-Ca7NSp-r.mjs";
7
+ import { A as detectCreationIntent, B as detectProjectType, D as MAX_EXA_RESULTS, E as frameworkSolidGate, F as freshnessGate, H as requiredArchSkill, I as solidReadGate, L as detectFramework, M as brainstormGate, N as docConsultedGate, O as MAX_TOKENS, P as evaluateApex, R as DEV_KEYWORDS, S as usesTailwindUtilities, T as SKILL_TRIGGERS, V as isApexCommand, a as firstHeading, b as detectRequiredSkills, c as EXCLUDE_DIRS, d as buildApexTaskInjection, f as loadApexTaskState, g as detectClaudeMdProjectType, h as buildClaudeMdContext, i as firstComment, j as APEX_GATES, k as capVerbosity, l as PROJECT_INDICATORS, m as buildApexInstruction, n as missingSeoElements, o as parseEnrichment, p as DEV_VERBS, r as descFromText, s as parseEntry, t as isHtmlLike, u as buildApexTaskContext, v as parseBodyDesc, x as skillTriggerGate, y as parseField, z as detectModularArchitecture } from "./validate-DcQaKzau.mjs";
8
8
  import { A as PROJECT_INSTALL, C as PROTECTED_GIT_RE, D as securityGuard, E as CRITICAL_PATTERNS, F as countFrameworkCodeLines, I as countLines, L as evaluateFileSize, M as matchPatterns, N as PLUGINS_DIR, O as GIT_ASK, P as SOLID_REF, S as PROTECTED_FRAGMENTS, T as ASK_PATTERNS, _ as CODE_MUTATORS, a as registerGuard, b as SAFE_PREFIXES, c as GO_DECL_RE, d as PY_MODEL_RE, f as SWIFT_PROTO_RE, g as ASK_WRITERS, h as bashWriteGuard, i as clearUserGuards, j as SYSTEM_INSTALL, k as GIT_BLOCKED, l as JAVA_DECL_RE, m as interfaceSeparationGuard, n as FAIL_CLOSED, o as runGuards, p as TS_DECL_RE, r as GUARDS, s as installGuard, t as evaluate, u as PHP_DECL_RE, v as CODE_REDIRECT, w as protectedPathGuard, x as SESSION_STATE_FRAGMENT, y as FILE_REDIRECT } from "./evaluate-d7Pp8XJH.mjs";
9
9
  import { i as resolveSessions, n as formatDocSatisfactionStatus, r as isDocConsulted, t as formatDocDeny } from "./doc-helpers-CWZegVdR.mjs";
10
10
  import { i as parseFrontmatter, n as scoreReferences, r as globToRe, t as routeReferences } from "./router-PKVNBHge.mjs";
11
11
  import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
12
12
  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";
13
- import { a as cachePath, c as extractText, d as compactMarkdown, f as jaccardSimilar, i as cacheLookupSubstring, l as loadIndex, n as webfetchCacheWrite, o as cacheStore, p as queryHash, r as cacheLookup, s as mcpCacheKey, t as mcpCacheWrite, u as summarizeIndex } from "./mcp-store-CnsW9oFj.mjs";
13
+ import { a as cacheLookupSubstring, c as cacheStore, d as loadIndex, f as summarizeIndex, h as queryHash, i as cacheLookupMeta, l as mcpCacheKey, m as jaccardSimilar, n as webfetchCacheWrite, o as cacheLookupSubstringMeta, p as compactMarkdown, r as cacheLookup, s as cachePath, t as mcpCacheWrite, u as extractText } from "./mcp-store-BkBDmuxN.mjs";
14
14
  import { t as incrementTrivialEditCounter } from "./freshness-BV3PQkDB.mjs";
15
15
  import { n as toRefMeta, t as loadRefs } from "./loader-AGz4nK7d.mjs";
16
16
  import { a as ensureStateDir, c as stateFilePath, i as apexStateDir, l as acquireLock, n as taskCreate, o as loadState, r as taskStart, s as saveState, t as taskComplete } from "./state-9lWvvWk8.mjs";
17
17
  import { a as formatPath, c as colors, d as GRADIENT_BLOCKS, f as PROGRESS_BAR_DEFAULTS, i as formatCost, l as progressiveColor, m as TIME_INTERVALS, n as generateProgressBar, o as formatTimeLeft, p as PROGRESS_CHARS, r as formatBasename, s as formatTokens, t as generateGradientBar, u as COLOR_THRESHOLDS } from "./statusline-D87eUNXl.mjs";
18
- export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, COLOR_THRESHOLDS, CRITICAL_PATTERNS, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, DEV_VERBS, EXCLUDE_DIRS, FAIL_CLOSED, FILE_REDIRECT, GIT_ASK, GIT_BLOCKED, GO_DECL_RE, GRADIENT_BLOCKS, GUARDS, HOME_DIR, JAVA_DECL_RE, MAX_EXA_RESULTS, MAX_LINES_ENV_KEY, MAX_TOKENS, PHP_DECL_RE, PLUGINS_DIR, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, SAFE_PREFIXES, SESSION_STATE_FRAGMENT, SKILL_TRIGGERS, SOLID_REF, STATE_GITIGNORE, STATE_ROOT, SWIFT_PROTO_RE, SYSTEM_INSTALL, TIME_INTERVALS, TS_DECL_RE, TTL_ENV_KEY, acquireLock, addRoot, apexStateDir, bashWriteGuard, brainstormGate, buildApexInstruction, buildApexTaskContext, buildApexTaskInjection, buildClaudeMdContext, cacheLookup, cacheLookupSubstring, cachePath, cacheStore, capVerbosity, clearUserGuards, colors, compactJson, compactMarkdown, countFrameworkCodeLines, countLines, descFromText, detectClaudeMdProjectType, detectCreationIntent, detectFramework, detectHarness, detectMode, detectModularArchitecture, detectProjectType, detectRequiredSkills, docConsultedGate, ensureMemoryGitignore, ensureStateDir, envCandidates, evaluate, evaluateApex, evaluateFileSize, extractText, firstComment, firstHeading, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, frameworkSolidGate, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, installGuard, interfaceSeparationGuard, isApexCommand, isCodeFile, isDocConsulted, isHtmlLike, jaccardSimilar, lessonsFileFor, loadApexTaskState, loadDotenv, loadIndex, loadRefs, loadState, matchPatterns, mcpCacheKey, mcpCacheWrite, missingSeoElements, modeFor, nowStamp, parseBodyDesc, parseEnrichment, parseEntry, parseEnvFile, parseEnvInt, parseField, parseFrontmatter, progressiveColor, projectLayout, projectRoot, projectRootOrNull, protectedPathGuard, queryHash, readRoots, readState, registerGuard, registryFile, requiredArchSkill, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, runGuards, saveState, scoreReferences, securityGuard, setStateField, skillTriggerGate, solidReadGate, splitTarget, stateFileFor, stateFilePath, summarizeIndex, taskComplete, taskCreate, taskStart, throttleMs, toRefMeta, ttlLabel, usesTailwindUtilities, walkUpFor, webfetchCacheWrite };
18
+ export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, COLOR_THRESHOLDS, CRITICAL_PATTERNS, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, DEV_VERBS, EXCLUDE_DIRS, FAIL_CLOSED, FILE_REDIRECT, GIT_ASK, GIT_BLOCKED, GO_DECL_RE, GRADIENT_BLOCKS, GUARDS, HOME_DIR, JAVA_DECL_RE, MAX_EXA_RESULTS, MAX_LINES_ENV_KEY, MAX_TOKENS, PHP_DECL_RE, PLUGINS_DIR, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, SAFE_PREFIXES, SESSION_STATE_FRAGMENT, SKILL_TRIGGERS, SOLID_REF, STATE_GITIGNORE, STATE_ROOT, SWIFT_PROTO_RE, SYSTEM_INSTALL, TIME_INTERVALS, TS_DECL_RE, TTL_ENV_KEY, acquireLock, addRoot, apexStateDir, bashWriteGuard, brainstormGate, buildApexInstruction, buildApexTaskContext, buildApexTaskInjection, buildClaudeMdContext, cacheLookup, cacheLookupMeta, cacheLookupSubstring, cacheLookupSubstringMeta, cachePath, cacheStore, capVerbosity, clearUserGuards, colors, compactJson, compactMarkdown, countFrameworkCodeLines, countLines, descFromText, detectClaudeMdProjectType, detectCreationIntent, detectFramework, detectHarness, detectMode, detectModularArchitecture, detectProjectType, detectRequiredSkills, docConsultedGate, ensureMemoryGitignore, ensureStateDir, envCandidates, evaluate, evaluateApex, evaluateFileSize, extractText, firstComment, firstHeading, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, frameworkSolidGate, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, installGuard, interfaceSeparationGuard, isApexCommand, isCodeFile, isDocConsulted, isHtmlLike, jaccardSimilar, lessonsFileFor, loadApexTaskState, loadDotenv, loadIndex, loadRefs, loadState, matchPatterns, mcpCacheKey, mcpCacheWrite, missingSeoElements, modeFor, nowStamp, parseBodyDesc, parseEnrichment, parseEntry, parseEnvFile, parseEnvInt, parseField, parseFrontmatter, progressiveColor, projectLayout, projectRoot, projectRootOrNull, protectedPathGuard, queryHash, readRoots, readState, registerGuard, registryFile, requiredArchSkill, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, runGuards, saveState, scoreReferences, securityGuard, setStateField, skillTriggerGate, solidReadGate, splitTarget, stateFileFor, stateFilePath, summarizeIndex, taskComplete, taskCreate, taskStart, throttleMs, toRefMeta, ttlLabel, usesTailwindUtilities, walkUpFor, webfetchCacheWrite };
@@ -130,10 +130,19 @@ function cachePath(dir, tool, query) {
130
130
  }
131
131
  /** Read a cached entry if it exists and is fresh (mtime within `ttlMs`), else null. */
132
132
  function cacheLookup(dir, tool, query, ttlMs, now) {
133
+ return cacheLookupMeta(dir, tool, query, ttlMs, now)?.body ?? null;
134
+ }
135
+ /** Like {@link cacheLookup}, but also reports the entry's age (for `CACHE HIT` wrapper text). */
136
+ function cacheLookupMeta(dir, tool, query, ttlMs, now) {
133
137
  const path = cachePath(dir, tool, query);
134
138
  try {
135
- if (!existsSync(path) || now - statSync(path).mtimeMs > ttlMs) return null;
136
- return readFileSync(path, "utf8");
139
+ if (!existsSync(path)) return null;
140
+ const ageMs = now - statSync(path).mtimeMs;
141
+ if (ageMs > ttlMs) return null;
142
+ return {
143
+ body: readFileSync(path, "utf8").slice(0, MAX_BODY),
144
+ ageMs
145
+ };
137
146
  } catch {
138
147
  return null;
139
148
  }
@@ -148,6 +157,10 @@ function cacheLookup(dir, tool, query, ttlMs, now) {
148
157
  * @param now - Current epoch ms.
149
158
  */
150
159
  function cacheLookupSubstring(dir, query, ttlMs, now) {
160
+ return cacheLookupSubstringMeta(dir, query, ttlMs, now)?.body ?? null;
161
+ }
162
+ /** Like {@link cacheLookupSubstring}, but also reports the matched entry's age. */
163
+ function cacheLookupSubstringMeta(dir, query, ttlMs, now) {
151
164
  const needle = query.replace(/[\r\n]+/g, " ").slice(0, NEEDLE_LEN).trim().toLowerCase();
152
165
  if (!needle) return null;
153
166
  let names;
@@ -160,9 +173,13 @@ function cacheLookupSubstring(dir, query, ttlMs, now) {
160
173
  if (!name.endsWith(".md")) continue;
161
174
  const path = join(dir, name);
162
175
  try {
163
- if (now - statSync(path).mtimeMs > ttlMs) continue;
176
+ const ageMs = now - statSync(path).mtimeMs;
177
+ if (ageMs > ttlMs) continue;
164
178
  const body = readFileSync(path, "utf8").slice(0, MAX_BODY);
165
- if (body.toLowerCase().includes(needle)) return body;
179
+ if (body.toLowerCase().includes(needle)) return {
180
+ body,
181
+ ageMs
182
+ };
166
183
  } catch {
167
184
  continue;
168
185
  }
@@ -235,4 +252,4 @@ function webfetchCacheWrite(dir, tool, key, body, now) {
235
252
  cacheStore(dir, tool, key, `---\ntool: ${tool}\nkey: ${JSON.stringify(key)}\nts: ${stamp(now)}\nhash: ${hash}\n---\n\n` + compacted);
236
253
  }
237
254
  //#endregion
238
- export { cachePath as a, extractText as c, compactMarkdown as d, jaccardSimilar as f, cacheLookupSubstring as i, loadIndex as l, webfetchCacheWrite as n, cacheStore as o, queryHash as p, cacheLookup as r, mcpCacheKey as s, mcpCacheWrite as t, summarizeIndex as u };
255
+ export { cacheLookupSubstring as a, cacheStore as c, loadIndex as d, summarizeIndex as f, queryHash as h, cacheLookupMeta as i, mcpCacheKey as l, jaccardSimilar as m, webfetchCacheWrite as n, cacheLookupSubstringMeta as o, compactMarkdown as p, cacheLookup as r, cachePath as s, mcpCacheWrite as t, extractText as u };
@@ -1,4 +1,4 @@
1
- import { A as detectCreationIntent, B as detectProjectType, C as skillTriggerGate, D as MAX_EXA_RESULTS, E as frameworkSolidGate, F as freshnessGate, H as requiredArchSkill, I as solidReadGate, L as detectFramework, M as brainstormGate, N as docConsultedGate, O as MAX_TOKENS, P as evaluateApex, R as DEV_KEYWORDS, S as detectRequiredSkills, T as SKILL_TRIGGERS, V as isApexCommand, a as firstHeading, c as EXCLUDE_DIRS, d as buildApexTaskInjection, f as loadApexTaskState, g as detectClaudeMdProjectType, h as buildClaudeMdContext, i as firstComment, j as APEX_GATES, k as capVerbosity, l as PROJECT_INDICATORS, m as buildApexInstruction, n as missingSeoElements, o as parseEnrichment, p as DEV_VERBS, r as descFromText, s as parseEntry, t as isHtmlLike, u as buildApexTaskContext, v as parseBodyDesc, w as usesTailwindUtilities, y as parseField, z as detectModularArchitecture } from "../validate-Ca7NSp-r.mjs";
1
+ import { A as detectCreationIntent, B as detectProjectType, D as MAX_EXA_RESULTS, E as frameworkSolidGate, F as freshnessGate, H as requiredArchSkill, I as solidReadGate, L as detectFramework, M as brainstormGate, N as docConsultedGate, O as MAX_TOKENS, P as evaluateApex, R as DEV_KEYWORDS, S as usesTailwindUtilities, T as SKILL_TRIGGERS, V as isApexCommand, a as firstHeading, b as detectRequiredSkills, c as EXCLUDE_DIRS, d as buildApexTaskInjection, f as loadApexTaskState, g as detectClaudeMdProjectType, h as buildClaudeMdContext, i as firstComment, j as APEX_GATES, k as capVerbosity, l as PROJECT_INDICATORS, m as buildApexInstruction, n as missingSeoElements, o as parseEnrichment, p as DEV_VERBS, r as descFromText, s as parseEntry, t as isHtmlLike, u as buildApexTaskContext, v as parseBodyDesc, x as skillTriggerGate, y as parseField, z as detectModularArchitecture } from "../validate-DcQaKzau.mjs";
2
2
  import { A as PROJECT_INSTALL, C as PROTECTED_GIT_RE, D as securityGuard, E as CRITICAL_PATTERNS, F as countFrameworkCodeLines, I as countLines, L as evaluateFileSize, M as matchPatterns, N as PLUGINS_DIR, O as GIT_ASK, P as SOLID_REF, S as PROTECTED_FRAGMENTS, T as ASK_PATTERNS, _ as CODE_MUTATORS, a as registerGuard, b as SAFE_PREFIXES, c as GO_DECL_RE, d as PY_MODEL_RE, f as SWIFT_PROTO_RE, g as ASK_WRITERS, h as bashWriteGuard, i as clearUserGuards, j as SYSTEM_INSTALL, k as GIT_BLOCKED, l as JAVA_DECL_RE, m as interfaceSeparationGuard, n as FAIL_CLOSED, o as runGuards, p as TS_DECL_RE, r as GUARDS, s as installGuard, t as evaluate, u as PHP_DECL_RE, v as CODE_REDIRECT, w as protectedPathGuard, x as SESSION_STATE_FRAGMENT, y as FILE_REDIRECT } from "../evaluate-d7Pp8XJH.mjs";
3
3
  import "../policy-la_KkjCS.mjs";
4
4
  export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, CRITICAL_PATTERNS, DEV_KEYWORDS, DEV_VERBS, EXCLUDE_DIRS, FAIL_CLOSED, FILE_REDIRECT, GIT_ASK, GIT_BLOCKED, GO_DECL_RE, GUARDS, JAVA_DECL_RE, MAX_EXA_RESULTS, MAX_TOKENS, PHP_DECL_RE, PLUGINS_DIR, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, SAFE_PREFIXES, SESSION_STATE_FRAGMENT, SKILL_TRIGGERS, SOLID_REF, SWIFT_PROTO_RE, SYSTEM_INSTALL, TS_DECL_RE, bashWriteGuard, brainstormGate, buildApexInstruction, buildApexTaskContext, buildApexTaskInjection, buildClaudeMdContext, capVerbosity, clearUserGuards, countFrameworkCodeLines, countLines, descFromText, detectClaudeMdProjectType, detectCreationIntent, detectFramework, detectModularArchitecture, detectProjectType, detectRequiredSkills, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, firstComment, firstHeading, frameworkSolidGate, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, isHtmlLike, loadApexTaskState, matchPatterns, missingSeoElements, parseBodyDesc, parseEnrichment, parseEntry, parseField, protectedPathGuard, registerGuard, requiredArchSkill, runGuards, securityGuard, skillTriggerGate, solidReadGate, usesTailwindUtilities };
@@ -1,6 +1,6 @@
1
1
  import { r as projectLayout } from "../layout-C0jaaCQC.mjs";
2
2
  import { a as sanitizeSessionId, c as sessionsDir, i as loadSessionState, n as fuseHarnessHome, o as saveSessionState, r as fusengineCache, s as sessionStatePath, t as claudeHome } from "../home-state-mKZxP4oZ.mjs";
3
- import { $ as saveApexState, A as trackSkillRead, At as securityStateDir, B as writePluginMap, C as seoPostToolUse, Ct as defaultStateDir, D as postTrackingSideEffects, Dt as isoUtc, E as securityAdvisory, Et as normalizeEvent, F as dispatchLessons, G as mergeLines, H as isProject, I as lessonsFileFor, J as listChildren, K as countFiles, L as lessonsStateFileFor, M as dispatchLifecycle, Mt as todayUtc, N as aipilotPostToolUse, O as trackWatchResearch, Ot as loadSecurityState, P as dispatchAipilot, Q as cleanupSession, R as cartoSessionStart, S as postEditContext, St as taskContext, T as dispatchMemory, Tt as trackFile, U as writeTree, V as generateProjectMap, W as loadEnriched, X as trackSessionChanges, Y as postEditTypescript, Z as validateRulesLoaded, _ as preCommitGate, _t as trimLogFile, a as recordActivity, at as validateSolidGate, b as extractSymbols, bt as projectContext, c as MCP_TTL_MS, ct as detectSolidProfile, d as isMcpTool, dt as readRules, et as logToolFailure, f as queryOf, ft as runSessionStartCleanups, g as gate, gt as removeOldFiles, h as TRIVIAL_BUDGET, ht as purgeTtlTree, i as respond, it as validateTailwind, j as trackEnrichment, jt as securityStatePath, k as trackMcpResearch, kt as saveSecurityState, l as WEBFETCH_TTL_MS, lt as solidDetectStart, m as REQUIRED_AGENTS, mt as pruneEmptyDirs, n as activityFor, nt as trackAgentMemory, o as mcpPostStore, ot as checkFileSize, p as DEFAULT_WINDOW_MS, pt as sessionStartCore, q as getFileDesc, r as handlePre, rt as subagentCacheContext, s as mcpPreIntercept, st as countLoc, t as handleHook, tt as validateTeammateOutput, u as cacheQueryOf, ut as injectRules, v as detectDuplication, vt as devContext, w as seoPostToolUseResponse, wt as projectHash, x as lifecycleStdout, xt as promptSubmitContext, y as dryGate, yt as gitContext, z as generateEcosystemMap } from "../handle-CtMMVoxT.mjs";
3
+ import { $ as saveApexState, A as trackSkillRead, At as securityStateDir, B as writePluginMap, C as seoPostToolUse, Ct as defaultStateDir, D as postTrackingSideEffects, Dt as isoUtc, E as securityAdvisory, Et as normalizeEvent, F as dispatchLessons, G as mergeLines, H as isProject, I as lessonsFileFor, J as listChildren, K as countFiles, L as lessonsStateFileFor, M as dispatchLifecycle, Mt as todayUtc, N as aipilotPostToolUse, O as trackWatchResearch, Ot as loadSecurityState, P as dispatchAipilot, Q as cleanupSession, R as cartoSessionStart, S as postEditContext, St as taskContext, T as dispatchMemory, Tt as trackFile, U as writeTree, V as generateProjectMap, W as loadEnriched, X as trackSessionChanges, Y as postEditTypescript, Z as validateRulesLoaded, _ as preCommitGate, _t as trimLogFile, a as recordActivity, at as validateSolidGate, b as extractSymbols, bt as projectContext, c as MCP_TTL_MS, ct as detectSolidProfile, d as isMcpTool, dt as readRules, et as logToolFailure, f as queryOf, ft as runSessionStartCleanups, g as gate, gt as removeOldFiles, h as TRIVIAL_BUDGET, ht as purgeTtlTree, i as respond, it as validateTailwind, j as trackEnrichment, jt as securityStatePath, k as trackMcpResearch, kt as saveSecurityState, l as WEBFETCH_TTL_MS, lt as solidDetectStart, m as REQUIRED_AGENTS, mt as pruneEmptyDirs, n as activityFor, nt as trackAgentMemory, o as mcpPostStore, ot as checkFileSize, p as DEFAULT_WINDOW_MS, pt as sessionStartCore, q as getFileDesc, r as handlePre, rt as subagentCacheContext, s as mcpPreIntercept, st as countLoc, t as handleHook, tt as validateTeammateOutput, u as cacheQueryOf, ut as injectRules, v as detectDuplication, vt as devContext, w as seoPostToolUseResponse, wt as projectHash, x as lifecycleStdout, xt as promptSubmitContext, y as dryGate, yt as gitContext, z as generateEcosystemMap } from "../handle-DJaaD2BT.mjs";
4
4
  //#region src/runtime/storage.ts
5
5
  /**
6
6
  * The project's single state dir (`<root>/.harness`) — neutral + harness-agnostic,
@@ -804,6 +804,94 @@ function isShadcnProject(cwd) {
804
804
  return UI_DIRS.some((dir) => existsSync(join(cwd, dir)));
805
805
  }
806
806
  //#endregion
807
+ //#region src/runtime/lifecycle/cartographer/detect.ts
808
+ /**
809
+ * Plugin discovery (fs). Ports `detect_plugins.py`: marketplace `plugins` dir
810
+ * resolution + `plugin.json` meta reading.
811
+ */
812
+ /** Sorted entry names of `dir` (alpha, byte-order), or `[]` on error. */
813
+ function sortedNames$1(dir) {
814
+ try {
815
+ return readdirSync(dir).sort((a, b) => a.localeCompare(b, "en"));
816
+ } catch {
817
+ return [];
818
+ }
819
+ }
820
+ /**
821
+ * Read `[version, name]` from `<pluginPath>/.claude-plugin/plugin.json`.
822
+ * @param pluginPath - Absolute plugin directory.
823
+ * @returns The `[version, name]` pair (both "" when absent/unreadable).
824
+ */
825
+ function readPluginMeta(pluginPath) {
826
+ const pj = join(pluginPath, ".claude-plugin", "plugin.json");
827
+ if (!existsSync(pj)) return ["", ""];
828
+ try {
829
+ const meta = JSON.parse(readFileSync(pj, "utf-8"));
830
+ return [meta.version ?? "", meta.name ?? ""];
831
+ } catch {
832
+ return ["", ""];
833
+ }
834
+ }
835
+ /**
836
+ * Auto-detect the marketplace `plugins` dir that contains `cartographer`,
837
+ * falling back to the first marketplace with a `plugins` dir, else `cwd`.
838
+ * Ports `find_marketplace_plugins`, but harness-agnostic: the config dir is
839
+ * derived from the detected harness (`.claude`, `.codex`, `.cursor`, …) via the
840
+ * shared `HOME_DIR` mapping instead of a hardcoded `.claude`.
841
+ * @param home - Home directory (defaults to `~`).
842
+ * @param id - Detected harness id (defaults to runtime detection).
843
+ * @returns The resolved plugins directory.
844
+ */
845
+ function findMarketplacePlugins(home = homedir(), id = detectHarness().id) {
846
+ const mp = join(home, HOME_DIR[id] ?? ".claude", "plugins", "marketplaces");
847
+ const markets = sortedNames$1(mp);
848
+ for (const m of markets) if (existsSync(join(mp, m, "plugins", "cartographer"))) return join(mp, m, "plugins");
849
+ for (const m of markets) if (existsSync(join(mp, m, "plugins"))) return join(mp, m, "plugins");
850
+ return process.cwd();
851
+ }
852
+ //#endregion
853
+ //#region src/policy/skill-path.ts
854
+ /**
855
+ * Dynamic skill-path resolution (fs). Replaces a hardcoded `PLUGIN_DIR`
856
+ * table (framework -> plugin dir name), which can drift from what's actually
857
+ * installed: scans installed marketplace plugins for a `skills/<name>/SKILL.md`
858
+ * that exists ON DISK, returning the first real absolute path found — never a
859
+ * guessed one.
860
+ *
861
+ * Deliberately lighter than the cartographer's own `scanSkills` (which
862
+ * frontmatter-parses every skill in a plugin): this runs once per missing
863
+ * sub-skill on every gated Write/Edit, so it does a single flat `readdirSync`
864
+ * of the plugins dir + one `existsSync` per candidate — no file reads, no
865
+ * recursion, no `statSync`.
866
+ */
867
+ /** Sorted entry names of `dir` (alpha, byte-order), or `[]` on error. */
868
+ function pluginDirNames(dir) {
869
+ try {
870
+ return readdirSync(dir).sort((a, b) => a.localeCompare(b, "en"));
871
+ } catch {
872
+ return [];
873
+ }
874
+ }
875
+ /**
876
+ * Resolve the real, installed `SKILL.md` path for `skillName`.
877
+ * @param skillName - the sub-skill directory name (e.g. "solid-react",
878
+ * "shadcn-detection", "react-shadcn").
879
+ * @param pluginsDirOverride - override for the marketplace plugins dir
880
+ * (tests); defaults to the auto-detected marketplace root.
881
+ * @returns the absolute `<pluginDir>/skills/<skillName>/SKILL.md` of the
882
+ * first installed plugin that actually has it, or the generic
883
+ * `${PLUGINS_DIR}/<skillName>` fallback when none is found (CI/test
884
+ * environments without a marketplace).
885
+ */
886
+ function resolveSkillPath(skillName, pluginsDirOverride) {
887
+ const pluginsDir = pluginsDirOverride ?? findMarketplacePlugins();
888
+ for (const dir of pluginDirNames(pluginsDir)) {
889
+ const candidate = join(pluginsDir, dir, "skills", skillName, "SKILL.md");
890
+ if (existsSync(candidate)) return candidate;
891
+ }
892
+ return `${PLUGINS_DIR}/${skillName}`;
893
+ }
894
+ //#endregion
807
895
  //#region src/policy/skill-triggers.ts
808
896
  /**
809
897
  * Detect which sub-skills the written `content` requires for a `framework`.
@@ -867,56 +955,10 @@ function skillTriggerGate(framework, content, refsRead, forcedSkill, cwd, filePa
867
955
  kind: "block",
868
956
  title: "Required sub-skill not consulted",
869
957
  reason: `${framework}: code uses APIs covered by ${missing.join(", ")} but its skill reference was not read this session.`,
870
- actions: missing.map((s) => `Read skills/${s}/ before writing this code`)
958
+ actions: missing.map((s) => `Read ${resolveSkillPath(s)}`)
871
959
  };
872
960
  }
873
961
  //#endregion
874
- //#region src/runtime/lifecycle/cartographer/detect.ts
875
- /**
876
- * Plugin discovery (fs). Ports `detect_plugins.py`: marketplace `plugins` dir
877
- * resolution + `plugin.json` meta reading.
878
- */
879
- /** Sorted entry names of `dir` (alpha, byte-order), or `[]` on error. */
880
- function sortedNames$1(dir) {
881
- try {
882
- return readdirSync(dir).sort((a, b) => a.localeCompare(b, "en"));
883
- } catch {
884
- return [];
885
- }
886
- }
887
- /**
888
- * Read `[version, name]` from `<pluginPath>/.claude-plugin/plugin.json`.
889
- * @param pluginPath - Absolute plugin directory.
890
- * @returns The `[version, name]` pair (both "" when absent/unreadable).
891
- */
892
- function readPluginMeta(pluginPath) {
893
- const pj = join(pluginPath, ".claude-plugin", "plugin.json");
894
- if (!existsSync(pj)) return ["", ""];
895
- try {
896
- const meta = JSON.parse(readFileSync(pj, "utf-8"));
897
- return [meta.version ?? "", meta.name ?? ""];
898
- } catch {
899
- return ["", ""];
900
- }
901
- }
902
- /**
903
- * Auto-detect the marketplace `plugins` dir that contains `cartographer`,
904
- * falling back to the first marketplace with a `plugins` dir, else `cwd`.
905
- * Ports `find_marketplace_plugins`, but harness-agnostic: the config dir is
906
- * derived from the detected harness (`.claude`, `.codex`, `.cursor`, …) via the
907
- * shared `HOME_DIR` mapping instead of a hardcoded `.claude`.
908
- * @param home - Home directory (defaults to `~`).
909
- * @param id - Detected harness id (defaults to runtime detection).
910
- * @returns The resolved plugins directory.
911
- */
912
- function findMarketplacePlugins(home = homedir(), id = detectHarness().id) {
913
- const mp = join(home, HOME_DIR[id] ?? ".claude", "plugins", "marketplaces");
914
- const markets = sortedNames$1(mp);
915
- for (const m of markets) if (existsSync(join(mp, m, "plugins", "cartographer"))) return join(mp, m, "plugins");
916
- for (const m of markets) if (existsSync(join(mp, m, "plugins"))) return join(mp, m, "plugins");
917
- return process.cwd();
918
- }
919
- //#endregion
920
962
  //#region src/policy/cartographer/frontmatter.ts
921
963
  /**
922
964
  * Frontmatter parsing — pure text helpers (no fs). Ports `parse_frontmatter.py`.
@@ -1497,4 +1539,4 @@ function missingSeoElements(html) {
1497
1539
  return missing;
1498
1540
  }
1499
1541
  //#endregion
1500
- export { detectCreationIntent as A, detectProjectType as B, skillTriggerGate as C, MAX_EXA_RESULTS as D, frameworkSolidGate as E, freshnessGate as F, requiredArchSkill as H, solidReadGate as I, detectFramework as L, brainstormGate as M, docConsultedGate as N, MAX_TOKENS as O, evaluateApex as P, DEV_KEYWORDS as R, detectRequiredSkills as S, SKILL_TRIGGERS as T, isApexCommand as V, scanPlugin as _, firstHeading as a, findMarketplacePlugins as b, EXCLUDE_DIRS as c, buildApexTaskInjection as d, loadApexTaskState as f, detectClaudeMdProjectType as g, buildClaudeMdContext as h, firstComment as i, APEX_GATES as j, capVerbosity as k, PROJECT_INDICATORS as l, buildApexInstruction as m, missingSeoElements as n, parseEnrichment as o, DEV_VERBS as p, descFromText as r, parseEntry as s, isHtmlLike as t, buildApexTaskContext as u, parseBodyDesc as v, usesTailwindUtilities as w, readPluginMeta as x, parseField as y, detectModularArchitecture as z };
1542
+ export { detectCreationIntent as A, detectProjectType as B, findMarketplacePlugins as C, MAX_EXA_RESULTS as D, frameworkSolidGate as E, freshnessGate as F, requiredArchSkill as H, solidReadGate as I, detectFramework as L, brainstormGate as M, docConsultedGate as N, MAX_TOKENS as O, evaluateApex as P, DEV_KEYWORDS as R, usesTailwindUtilities as S, SKILL_TRIGGERS as T, isApexCommand as V, scanPlugin as _, firstHeading as a, detectRequiredSkills as b, EXCLUDE_DIRS as c, buildApexTaskInjection as d, loadApexTaskState as f, detectClaudeMdProjectType as g, buildClaudeMdContext as h, firstComment as i, APEX_GATES as j, capVerbosity as k, PROJECT_INDICATORS as l, buildApexInstruction as m, missingSeoElements as n, parseEnrichment as o, DEV_VERBS as p, descFromText as r, parseEntry as s, isHtmlLike as t, buildApexTaskContext as u, parseBodyDesc as v, readPluginMeta as w, skillTriggerGate as x, parseField as y, detectModularArchitecture as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusengine/harness",
3
- "version": "0.1.44",
3
+ "version": "0.1.45",
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",