@fusengine/harness 0.1.2 → 0.1.4

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,5 +1,3 @@
1
- import { n as readJsonFile, r as writeJsonFile, t as ensureDir } from "./json-io-RH82El2J.mjs";
2
- import { dirname } from "node:path";
3
1
  //#region src/freshness/doc-helpers.ts
4
2
  /** Resolve the sessions array from an auth entry (legacy fallback). */
5
3
  function resolveSessions(auth) {
@@ -41,24 +39,4 @@ function formatDocDeny(framework) {
41
39
  ].join("\n");
42
40
  }
43
41
  //#endregion
44
- //#region src/freshness/trivial-edit-counter.ts
45
- /**
46
- * Increment a session's trivial-edit counter, evicting timestamps older than
47
- * `windowMs`. Decoupled + injectable `now` for testability.
48
- * @param filePath - session state file path
49
- * @param windowMs - sliding window in ms
50
- * @param now - current epoch ms (defaults to `Date.now()`)
51
- * @returns number of trivial edits within the window (including this one)
52
- */
53
- async function incrementTrivialEditCounter(filePath, windowMs, now = Date.now()) {
54
- await ensureDir(dirname(filePath));
55
- const state = await readJsonFile(filePath) ?? {};
56
- const cutoff = now - windowMs;
57
- const edits = (state.trivial_edits ?? []).filter((ts) => ts > cutoff);
58
- edits.push(now);
59
- state.trivial_edits = edits;
60
- await writeJsonFile(filePath, state);
61
- return edits.length;
62
- }
63
- //#endregion
64
- export { resolveSessions as a, isDocConsulted as i, formatDocDeny as n, formatDocSatisfactionStatus as r, incrementTrivialEditCounter as t };
42
+ export { resolveSessions as i, formatDocSatisfactionStatus as n, isDocConsulted as r, formatDocDeny as t };
@@ -1,2 +1,3 @@
1
- import { a as resolveSessions, i as isDocConsulted, n as formatDocDeny, r as formatDocSatisfactionStatus, t as incrementTrivialEditCounter } from "../freshness-DzGoBKKj.mjs";
1
+ import { i as resolveSessions, n as formatDocSatisfactionStatus, r as isDocConsulted, t as formatDocDeny } from "../doc-helpers-Dd_x1-tZ.mjs";
2
+ import { t as incrementTrivialEditCounter } from "../freshness-BK9Xg6oG.mjs";
2
3
  export { formatDocDeny, formatDocSatisfactionStatus, incrementTrivialEditCounter, isDocConsulted, resolveSessions };
@@ -0,0 +1,23 @@
1
+ import { n as readJsonFile, r as writeJsonFile, t as ensureDir } from "./json-io-RH82El2J.mjs";
2
+ import { dirname } from "node:path";
3
+ //#region src/freshness/trivial-edit-counter.ts
4
+ /**
5
+ * Increment a session's trivial-edit counter, evicting timestamps older than
6
+ * `windowMs`. Decoupled + injectable `now` for testability.
7
+ * @param filePath - session state file path
8
+ * @param windowMs - sliding window in ms
9
+ * @param now - current epoch ms (defaults to `Date.now()`)
10
+ * @returns number of trivial edits within the window (including this one)
11
+ */
12
+ async function incrementTrivialEditCounter(filePath, windowMs, now = Date.now()) {
13
+ await ensureDir(dirname(filePath));
14
+ const state = await readJsonFile(filePath) ?? {};
15
+ const cutoff = now - windowMs;
16
+ const edits = (state.trivial_edits ?? []).filter((ts) => ts > cutoff);
17
+ edits.push(now);
18
+ state.trivial_edits = edits;
19
+ await writeJsonFile(filePath, state);
20
+ return edits.length;
21
+ }
22
+ //#endregion
23
+ export { incrementTrivialEditCounter as t };
@@ -1,4 +1,6 @@
1
1
  import { t as Prompt } from "./types-D56jSgD9.mjs";
2
+ import { t as AuthEntry } from "./doc-helpers-CG1nuf-c.mjs";
3
+ import { t as RefMeta } from "./types-CY5qT2X1.mjs";
2
4
 
3
5
  //#region src/policy/detect-project.d.ts
4
6
  /** Project types detected from filesystem indicators. */
@@ -77,4 +79,40 @@ interface PolicyResult {
77
79
  */
78
80
  declare function evaluate(ctx: PolicyContext): PolicyResult;
79
81
  //#endregion
80
- export { GIT_BLOCKED as a, matchPatterns as c, evaluateFileSize as d, detectFramework as f, isApexCommand as g, detectProjectType as h, GIT_ASK as i, FileSizeVerdict as l, ProjectType as m, PolicyResult as n, PROJECT_INSTALL as o, DEV_KEYWORDS as p, evaluate as r, SYSTEM_INSTALL as s, PolicyContext as t, countLines as u };
82
+ //#region src/policy/apex.d.ts
83
+ /**
84
+ * Session context for the stateful APEX gates. The harness adapter supplies this
85
+ * (the package owns the gate LOGIC; recording the session activity is the
86
+ * adapter's tracking layer).
87
+ */
88
+ interface ApexContext {
89
+ sessionId: string;
90
+ framework: string;
91
+ filePath: string;
92
+ content: string;
93
+ /** Doc-consultation authorizations from session state (Context7/Exa). */
94
+ authorizations?: Record<string, AuthEntry>;
95
+ /** Available SOLID references for the framework's skill. */
96
+ refs?: RefMeta[];
97
+ /** Absolute paths of SOLID refs already read this session. */
98
+ refsRead?: string[];
99
+ /** Whether the required prior agents (explore + research) ran within the freshness window. */
100
+ agentsFresh?: boolean;
101
+ }
102
+ /** A single APEX gate: returns a blocking {@link Prompt}, or null to pass. */
103
+ type ApexGate = (ctx: ApexContext) => Prompt | null;
104
+ /** Gate: Context7 + Exa must have been consulted this session. */
105
+ declare const docConsultedGate: ApexGate;
106
+ /** Gate: the routed SOLID references for this edit must have been read. */
107
+ declare const solidReadGate: ApexGate;
108
+ /** Gate: the required prior agents (explore + research) must have run within the window. */
109
+ declare const freshnessGate: ApexGate;
110
+ /** Default APEX gate chain (freshness, then docs, then SOLID refs). */
111
+ declare const APEX_GATES: ReadonlyArray<ApexGate>;
112
+ /**
113
+ * Run the APEX gates (chain-of-responsibility): the first failing gate's prompt
114
+ * wins; null means every gate passed (allow).
115
+ */
116
+ declare function evaluateApex(ctx: ApexContext, gates?: ReadonlyArray<ApexGate>): Prompt | null;
117
+ //#endregion
118
+ export { isApexCommand as C, detectProjectType as S, countLines as _, evaluateApex as a, DEV_KEYWORDS as b, PolicyContext as c, GIT_ASK as d, GIT_BLOCKED as f, FileSizeVerdict as g, matchPatterns as h, docConsultedGate as i, PolicyResult as l, SYSTEM_INSTALL as m, ApexContext as n, freshnessGate as o, PROJECT_INSTALL as p, ApexGate as r, solidReadGate as s, APEX_GATES as t, evaluate as u, evaluateFileSize as v, ProjectType as x, detectFramework as y };
@@ -1,28 +1,5 @@
1
- //#region src/refs/types.d.ts
2
- /** Parsed frontmatter metadata from a reference .md file. */
3
- interface RefMeta {
4
- name: string;
5
- description: string;
6
- keywords: string;
7
- priority: string;
8
- related: string;
9
- appliesTo: string;
10
- triggerOnEdit: string;
11
- level: string;
12
- filePath: string;
13
- }
14
- /** A reference with its computed routing score. */
15
- interface ScoredRef {
16
- meta: RefMeta;
17
- score: number;
18
- }
19
- /** Routing result with categorized references. */
20
- interface RouteResult {
21
- required: ScoredRef[];
22
- optional: ScoredRef[];
23
- skillPath: string;
24
- }
25
- //#endregion
1
+ import { n as RouteResult, r as ScoredRef, t as RefMeta } from "./types-CY5qT2X1.mjs";
2
+
26
3
  //#region src/refs/frontmatter.d.ts
27
4
  /** Extract frontmatter key/value pairs from markdown content (quotes stripped). */
28
5
  declare function parseFrontmatter(content: string): Record<string, string>;
@@ -41,4 +18,4 @@ declare function scoreReferences(refs: RefMeta[], filePath: string, content: str
41
18
  */
42
19
  declare function routeReferences(refs: RefMeta[], filePath: string, content: string, skillPath?: string): RouteResult | null;
43
20
  //#endregion
44
- export { RefMeta as a, parseFrontmatter as i, scoreReferences as n, RouteResult as o, globToRe as r, ScoredRef as s, routeReferences as t };
21
+ export { parseFrontmatter as i, scoreReferences as n, globToRe as r, routeReferences as t };
package/dist/index.d.mts CHANGED
@@ -5,9 +5,10 @@ import { a as detectHarness, i as HarnessVia, n as HarnessInfo, o as detectMode,
5
5
  import { a as isDocConsulted, i as formatDocSatisfactionStatus, n as DocSatisfactionStatus, o as resolveSessions, r as formatDocDeny, t as AuthEntry } from "./doc-helpers-CG1nuf-c.mjs";
6
6
  import { t as incrementTrivialEditCounter } from "./index-BOBXQ91y.mjs";
7
7
  import { i as compactJson, n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./index-C1vLIMwN.mjs";
8
- import { a as GIT_BLOCKED, c as matchPatterns, d as evaluateFileSize, f as detectFramework, g as isApexCommand, h as detectProjectType, i as GIT_ASK, l as FileSizeVerdict, m as ProjectType, n as PolicyResult, o as PROJECT_INSTALL, p as DEV_KEYWORDS, r as evaluate, s as SYSTEM_INSTALL, t as PolicyContext, u as countLines } from "./index-D7MkQNdc.mjs";
8
+ import { C as isApexCommand, S as detectProjectType, _ as countLines, a as evaluateApex, b as DEV_KEYWORDS, c as PolicyContext, d as GIT_ASK, f as GIT_BLOCKED, g as FileSizeVerdict, h as matchPatterns, i as docConsultedGate, l as PolicyResult, m as SYSTEM_INSTALL, n as ApexContext, o as freshnessGate, p as PROJECT_INSTALL, r as ApexGate, s as solidReadGate, t as APEX_GATES, u as evaluate, v as evaluateFileSize, x as ProjectType, y as detectFramework } from "./index-BbJucvaG.mjs";
9
+ import { n as RouteResult, r as ScoredRef, t as RefMeta } from "./types-CY5qT2X1.mjs";
9
10
  import { a as ReminderState, c as setStateField, i as registryFile, l as stateFileFor, n as addRoot, o as nowStamp, r as readRoots, s as readState, t as ensureMemoryGitignore, u as throttleMs } from "./index-BEM-mQMC.mjs";
10
- import { a as RefMeta, i as parseFrontmatter, n as scoreReferences, o as RouteResult, r as globToRe, s as ScoredRef, t as routeReferences } from "./index-DqNKW8Ki.mjs";
11
+ import { i as parseFrontmatter, n as scoreReferences, r as globToRe, t as routeReferences } from "./index-C2Lz-cwJ.mjs";
11
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-CPoF_hLP.mjs";
12
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";
13
- export { ApexState, ApexTask, ApexTaskFile, AuthEntry, COLOR_THRESHOLDS, ColorFn, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, DocSatisfactionStatus, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, GRADIENT_BLOCKS, HarnessId, HarnessInfo, HarnessMode, HarnessVia, IndexSummary, MAX_LINES_ENV_KEY, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INSTALL, Palette, PolicyContext, PolicyResult, ProgressBarOptions, ProjectType, Prompt, PromptKind, RefMeta, ReminderState, RouteResult, SYSTEM_INSTALL, ScoredRef, TIME_INTERVALS, TTL_ENV_KEY, acquireLock, addRoot, apexStateDir, colors, compactJson, compactMarkdown, countLines, detectFramework, detectHarness, detectMode, detectProjectType, ensureMemoryGitignore, ensureStateDir, evaluate, evaluateFileSize, extractText, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, isApexCommand, isCodeFile, isDocConsulted, jaccardSimilar, loadIndex, loadState, matchPatterns, modeFor, nowStamp, parseEnvInt, parseFrontmatter, progressiveColor, projectRoot, projectRootOrNull, queryHash, readRoots, readState, registryFile, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, saveState, scoreReferences, setStateField, splitTarget, stateFileFor, stateFilePath, summarizeIndex, taskComplete, taskCreate, taskStart, throttleMs, ttlLabel };
14
+ export { APEX_GATES, ApexContext, ApexGate, ApexState, ApexTask, ApexTaskFile, AuthEntry, COLOR_THRESHOLDS, ColorFn, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, DocSatisfactionStatus, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, GRADIENT_BLOCKS, HarnessId, HarnessInfo, HarnessMode, HarnessVia, IndexSummary, MAX_LINES_ENV_KEY, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INSTALL, Palette, PolicyContext, PolicyResult, ProgressBarOptions, ProjectType, Prompt, PromptKind, RefMeta, ReminderState, RouteResult, SYSTEM_INSTALL, ScoredRef, TIME_INTERVALS, TTL_ENV_KEY, acquireLock, addRoot, apexStateDir, colors, compactJson, compactMarkdown, countLines, detectFramework, detectHarness, detectMode, detectProjectType, docConsultedGate, ensureMemoryGitignore, ensureStateDir, evaluate, evaluateApex, evaluateFileSize, extractText, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, isApexCommand, isCodeFile, isDocConsulted, jaccardSimilar, loadIndex, loadState, matchPatterns, modeFor, nowStamp, parseEnvInt, parseFrontmatter, progressiveColor, projectRoot, projectRootOrNull, queryHash, readRoots, readState, registryFile, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, saveState, scoreReferences, setStateField, solidReadGate, splitTarget, stateFileFor, stateFilePath, summarizeIndex, taskComplete, taskCreate, taskStart, throttleMs, ttlLabel };
package/dist/index.mjs CHANGED
@@ -3,13 +3,14 @@ import { i as ttlLabel, n as TTL_ENV_KEY, r as resolveTtlSec, t as DEFAULT_TTL_S
3
3
  import { t as compactJson } from "./compact-json-DK2nX-MK.mjs";
4
4
  import { n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./project-root-C4ks_q1G.mjs";
5
5
  import { n as detectMode, r as modeFor, t as detectHarness } from "./harness-C8Nxxyn_.mjs";
6
- import { n as detectProjectType, r as isApexCommand, t as DEV_KEYWORDS } from "./policy-C_pXmeNB.mjs";
6
+ import { a as solidReadGate, c as isApexCommand, i as freshnessGate, n as docConsultedGate, o as DEV_KEYWORDS, r as evaluateApex, s as detectProjectType, t as APEX_GATES } from "./policy-C4zmyZR-.mjs";
7
7
  import { a as SYSTEM_INSTALL, c as evaluateFileSize, i as PROJECT_INSTALL, l as detectFramework, n as GIT_ASK, o as matchPatterns, r as GIT_BLOCKED, s as countLines, t as evaluate } from "./evaluate-CsYyUucy.mjs";
8
+ import { i as resolveSessions, n as formatDocSatisfactionStatus, r as isDocConsulted, t as formatDocDeny } from "./doc-helpers-Dd_x1-tZ.mjs";
9
+ import { i as parseFrontmatter, n as scoreReferences, r as globToRe, t as routeReferences } from "./router-Dj3AfgBE.mjs";
8
10
  import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
9
11
  import { a as readState, c as throttleMs, i as nowStamp, l as ensureMemoryGitignore, n as readRoots, o as setStateField, r as registryFile, s as stateFileFor, t as addRoot } from "./memory-BVNt4Ary.mjs";
10
12
  import { a as jaccardSimilar, i as compactMarkdown, n as loadIndex, o as queryHash, r as summarizeIndex, t as extractText } from "./cache-DbPSJ9bC.mjs";
11
- import { a as resolveSessions, i as isDocConsulted, n as formatDocDeny, r as formatDocSatisfactionStatus, t as incrementTrivialEditCounter } from "./freshness-DzGoBKKj.mjs";
12
- import { i as parseFrontmatter, n as scoreReferences, r as globToRe, t as routeReferences } from "./refs-Dj3AfgBE.mjs";
13
+ import { t as incrementTrivialEditCounter } from "./freshness-BK9Xg6oG.mjs";
13
14
  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-Bc4wdnCG.mjs";
14
15
  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";
15
- export { COLOR_THRESHOLDS, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, GIT_ASK, GIT_BLOCKED, GRADIENT_BLOCKS, MAX_LINES_ENV_KEY, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INSTALL, SYSTEM_INSTALL, TIME_INTERVALS, TTL_ENV_KEY, acquireLock, addRoot, apexStateDir, colors, compactJson, compactMarkdown, countLines, detectFramework, detectHarness, detectMode, detectProjectType, ensureMemoryGitignore, ensureStateDir, evaluate, evaluateFileSize, extractText, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, isApexCommand, isCodeFile, isDocConsulted, jaccardSimilar, loadIndex, loadState, matchPatterns, modeFor, nowStamp, parseEnvInt, parseFrontmatter, progressiveColor, projectRoot, projectRootOrNull, queryHash, readRoots, readState, registryFile, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, saveState, scoreReferences, setStateField, splitTarget, stateFileFor, stateFilePath, summarizeIndex, taskComplete, taskCreate, taskStart, throttleMs, ttlLabel };
16
+ export { APEX_GATES, COLOR_THRESHOLDS, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, GIT_ASK, GIT_BLOCKED, GRADIENT_BLOCKS, MAX_LINES_ENV_KEY, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INSTALL, SYSTEM_INSTALL, TIME_INTERVALS, TTL_ENV_KEY, acquireLock, addRoot, apexStateDir, colors, compactJson, compactMarkdown, countLines, detectFramework, detectHarness, detectMode, detectProjectType, docConsultedGate, ensureMemoryGitignore, ensureStateDir, evaluate, evaluateApex, evaluateFileSize, extractText, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, isApexCommand, isCodeFile, isDocConsulted, jaccardSimilar, loadIndex, loadState, matchPatterns, modeFor, nowStamp, parseEnvInt, parseFrontmatter, progressiveColor, projectRoot, projectRootOrNull, queryHash, readRoots, readState, registryFile, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, saveState, scoreReferences, setStateField, solidReadGate, splitTarget, stateFileFor, stateFilePath, summarizeIndex, taskComplete, taskCreate, taskStart, throttleMs, ttlLabel };
@@ -1,2 +1,2 @@
1
- import { a as GIT_BLOCKED, c as matchPatterns, d as evaluateFileSize, f as detectFramework, g as isApexCommand, h as detectProjectType, i as GIT_ASK, l as FileSizeVerdict, m as ProjectType, n as PolicyResult, o as PROJECT_INSTALL, p as DEV_KEYWORDS, r as evaluate, s as SYSTEM_INSTALL, t as PolicyContext, u as countLines } from "../index-D7MkQNdc.mjs";
2
- export { DEV_KEYWORDS, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, PROJECT_INSTALL, PolicyContext, PolicyResult, ProjectType, SYSTEM_INSTALL, countLines, detectFramework, detectProjectType, evaluate, evaluateFileSize, isApexCommand, matchPatterns };
1
+ import { C as isApexCommand, S as detectProjectType, _ as countLines, a as evaluateApex, b as DEV_KEYWORDS, c as PolicyContext, d as GIT_ASK, f as GIT_BLOCKED, g as FileSizeVerdict, h as matchPatterns, i as docConsultedGate, l as PolicyResult, m as SYSTEM_INSTALL, n as ApexContext, o as freshnessGate, p as PROJECT_INSTALL, r as ApexGate, s as solidReadGate, t as APEX_GATES, u as evaluate, v as evaluateFileSize, x as ProjectType, y as detectFramework } from "../index-BbJucvaG.mjs";
2
+ export { APEX_GATES, ApexContext, ApexGate, DEV_KEYWORDS, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, PROJECT_INSTALL, PolicyContext, PolicyResult, ProjectType, SYSTEM_INSTALL, countLines, detectFramework, detectProjectType, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, freshnessGate, isApexCommand, matchPatterns, solidReadGate };
@@ -1,3 +1,3 @@
1
- import { n as detectProjectType, r as isApexCommand, t as DEV_KEYWORDS } from "../policy-C_pXmeNB.mjs";
1
+ import { a as solidReadGate, c as isApexCommand, i as freshnessGate, n as docConsultedGate, o as DEV_KEYWORDS, r as evaluateApex, s as detectProjectType, t as APEX_GATES } from "../policy-C4zmyZR-.mjs";
2
2
  import { a as SYSTEM_INSTALL, c as evaluateFileSize, i as PROJECT_INSTALL, l as detectFramework, n as GIT_ASK, o as matchPatterns, r as GIT_BLOCKED, s as countLines, t as evaluate } from "../evaluate-CsYyUucy.mjs";
3
- export { DEV_KEYWORDS, GIT_ASK, GIT_BLOCKED, PROJECT_INSTALL, SYSTEM_INSTALL, countLines, detectFramework, detectProjectType, evaluate, evaluateFileSize, isApexCommand, matchPatterns };
3
+ export { APEX_GATES, DEV_KEYWORDS, GIT_ASK, GIT_BLOCKED, PROJECT_INSTALL, SYSTEM_INSTALL, countLines, detectFramework, detectProjectType, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, freshnessGate, isApexCommand, matchPatterns, solidReadGate };
@@ -0,0 +1,79 @@
1
+ import { r as isDocConsulted, t as formatDocDeny } from "./doc-helpers-Dd_x1-tZ.mjs";
2
+ import { t as routeReferences } from "./router-Dj3AfgBE.mjs";
3
+ import { existsSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ //#region src/policy/detect-project.ts
6
+ /** Keywords that signal a development task (APEX trigger). */
7
+ const DEV_KEYWORDS = /\b(implement|create|build|fix|add|refactor|develop|feature|bug|update|modify|change|write|code)\b/i;
8
+ /** True when the prompt invokes the /apex command. */
9
+ function isApexCommand(prompt) {
10
+ return /(?:^|\s)\/apex|\/fuse-ai-pilot:apex/i.test(prompt);
11
+ }
12
+ /** Detect the project type by scanning config files in `dir`. */
13
+ function detectProjectType(dir) {
14
+ const has = (f) => existsSync(join(dir, f));
15
+ if (has("next.config.js") || has("next.config.ts") || has("next.config.mjs")) return "nextjs";
16
+ if (has("nuxt.config.ts") || has("nuxt.config.js")) return "nuxt";
17
+ if (has("angular.json")) return "angular";
18
+ if (has("svelte.config.js") || has("svelte.config.ts")) return "svelte";
19
+ if (has("vite.config.ts") && has("src/App.vue")) return "vue";
20
+ if (has("vite.config.ts") || has("vite.config.js")) return "react";
21
+ if (has("tailwind.config.js") || has("tailwind.config.ts")) return "tailwind";
22
+ if (has("composer.json") && has("artisan")) return "laravel";
23
+ if (has("Gemfile") && has("config/routes.rb")) return "rails";
24
+ if (has("requirements.txt") || has("pyproject.toml") || has("setup.py")) return has("manage.py") ? "django" : "python";
25
+ if (has("go.mod")) return "go";
26
+ if (has("Cargo.toml")) return "rust";
27
+ if (has("Package.swift")) return "swift";
28
+ if (has("pom.xml") || has("build.gradle") || has("build.gradle.kts")) return "java";
29
+ if (has("build.sbt")) return "scala";
30
+ if (has("mix.exs")) return "elixir";
31
+ if (has("Gemfile")) return "ruby";
32
+ return "generic";
33
+ }
34
+ //#endregion
35
+ //#region src/policy/apex.ts
36
+ /** Gate: Context7 + Exa must have been consulted this session. */
37
+ const docConsultedGate = (ctx) => isDocConsulted(ctx.authorizations, ctx.sessionId) ? null : {
38
+ kind: "block",
39
+ title: "APEX: documentation not consulted",
40
+ reason: formatDocDeny(ctx.framework),
41
+ actions: ["Call mcp__context7__query-docs", "Call mcp__exa__web_search_exa"]
42
+ };
43
+ /** Gate: the routed SOLID references for this edit must have been read. */
44
+ const solidReadGate = (ctx) => {
45
+ if (!ctx.refs?.length) return null;
46
+ const routed = routeReferences(ctx.refs, ctx.filePath, ctx.content);
47
+ if (!routed) return null;
48
+ const read = new Set(ctx.refsRead ?? []);
49
+ const missing = routed.required.map((r) => r.meta.filePath).filter((p) => !read.has(p));
50
+ if (missing.length === 0) return null;
51
+ return {
52
+ kind: "block",
53
+ title: `APEX: read SOLID references for ${ctx.framework}`,
54
+ reason: `Read these before editing ${ctx.filePath}:`,
55
+ actions: missing
56
+ };
57
+ };
58
+ /** Gate: the required prior agents (explore + research) must have run within the window. */
59
+ const freshnessGate = (ctx) => ctx.agentsFresh === false ? {
60
+ kind: "block",
61
+ title: "APEX: explore + research required",
62
+ reason: `Run explore-codebase and research-expert (within the freshness window) before editing ${ctx.framework}.`,
63
+ actions: ["Launch the explore-codebase agent", "Launch the research-expert agent"]
64
+ } : null;
65
+ /** Default APEX gate chain (freshness, then docs, then SOLID refs). */
66
+ const APEX_GATES = [
67
+ freshnessGate,
68
+ docConsultedGate,
69
+ solidReadGate
70
+ ];
71
+ /**
72
+ * Run the APEX gates (chain-of-responsibility): the first failing gate's prompt
73
+ * wins; null means every gate passed (allow).
74
+ */
75
+ function evaluateApex(ctx, gates = APEX_GATES) {
76
+ return gates.reduce((hit, gate) => hit ?? gate(ctx), null);
77
+ }
78
+ //#endregion
79
+ export { solidReadGate as a, isApexCommand as c, freshnessGate as i, docConsultedGate as n, DEV_KEYWORDS as o, evaluateApex as r, detectProjectType as s, APEX_GATES as t };
@@ -1,2 +1,3 @@
1
- import { a as RefMeta, i as parseFrontmatter, n as scoreReferences, o as RouteResult, r as globToRe, s as ScoredRef, t as routeReferences } from "../index-DqNKW8Ki.mjs";
1
+ import { n as RouteResult, r as ScoredRef, t as RefMeta } from "../types-CY5qT2X1.mjs";
2
+ import { i as parseFrontmatter, n as scoreReferences, r as globToRe, t as routeReferences } from "../index-C2Lz-cwJ.mjs";
2
3
  export { RefMeta, RouteResult, ScoredRef, globToRe, parseFrontmatter, routeReferences, scoreReferences };
@@ -1,2 +1,3 @@
1
- import { i as parseFrontmatter, n as scoreReferences, r as globToRe, t as routeReferences } from "../refs-Dj3AfgBE.mjs";
1
+ import { i as parseFrontmatter, n as scoreReferences, r as globToRe, t as routeReferences } from "../router-Dj3AfgBE.mjs";
2
+ import "../refs-la_KkjCS.mjs";
2
3
  export { globToRe, parseFrontmatter, routeReferences, scoreReferences };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ import { t as AuthEntry } from "../doc-helpers-CG1nuf-c.mjs";
2
+
3
+ //#region src/tracking/session-state.d.ts
4
+ /** Recorded session activity that feeds the APEX gates. */
5
+ interface SessionTrack {
6
+ authorizations: Record<string, AuthEntry>;
7
+ refsRead: string[];
8
+ agents: {
9
+ name: string;
10
+ ts: number;
11
+ }[];
12
+ }
13
+ /** A fresh, empty track. */
14
+ declare function emptyTrack(): SessionTrack;
15
+ /** Record a doc consultation (Context7/Exa) for a framework in this session. Immutable. */
16
+ declare function recordDoc(track: SessionTrack, framework: string, sessionId: string, source: string): SessionTrack;
17
+ /** Record that a SOLID reference file was read (deduped). Immutable. */
18
+ declare function recordRefRead(track: SessionTrack, path: string): SessionTrack;
19
+ /** Record an agent/tool call with a timestamp. Immutable. */
20
+ declare function recordAgent(track: SessionTrack, name: string, ts: number): SessionTrack;
21
+ /** True when ALL of `names` were called within `windowMs` before `now`. */
22
+ declare function agentsFresh(track: SessionTrack, names: string[], windowMs: number, now: number): boolean;
23
+ //#endregion
24
+ //#region src/tracking/store.d.ts
25
+ /** Load a session track from a file (an empty track if absent/corrupt). */
26
+ declare function loadTrack(file: string): Promise<SessionTrack>;
27
+ /** Persist a session track. */
28
+ declare function saveTrack(file: string, track: SessionTrack): Promise<void>;
29
+ //#endregion
30
+ export { SessionTrack, agentsFresh, emptyTrack, loadTrack, recordAgent, recordDoc, recordRefRead, saveTrack };
@@ -0,0 +1,63 @@
1
+ import { n as readJsonFile, r as writeJsonFile } from "../json-io-RH82El2J.mjs";
2
+ //#region src/tracking/session-state.ts
3
+ /** A fresh, empty track. */
4
+ function emptyTrack() {
5
+ return {
6
+ authorizations: {},
7
+ refsRead: [],
8
+ agents: []
9
+ };
10
+ }
11
+ /** Record a doc consultation (Context7/Exa) for a framework in this session. Immutable. */
12
+ function recordDoc(track, framework, sessionId, source) {
13
+ const prev = track.authorizations[framework] ?? {};
14
+ const sessions = new Set(prev.doc_sessions ?? []);
15
+ sessions.add(sessionId);
16
+ const sources = new Set(prev.sources ?? (prev.source ? [prev.source] : []));
17
+ sources.add(source);
18
+ return {
19
+ ...track,
20
+ authorizations: {
21
+ ...track.authorizations,
22
+ [framework]: {
23
+ ...prev,
24
+ doc_sessions: [...sessions],
25
+ sources: [...sources]
26
+ }
27
+ }
28
+ };
29
+ }
30
+ /** Record that a SOLID reference file was read (deduped). Immutable. */
31
+ function recordRefRead(track, path) {
32
+ return track.refsRead.includes(path) ? track : {
33
+ ...track,
34
+ refsRead: [...track.refsRead, path]
35
+ };
36
+ }
37
+ /** Record an agent/tool call with a timestamp. Immutable. */
38
+ function recordAgent(track, name, ts) {
39
+ return {
40
+ ...track,
41
+ agents: [...track.agents, {
42
+ name,
43
+ ts
44
+ }]
45
+ };
46
+ }
47
+ /** True when ALL of `names` were called within `windowMs` before `now`. */
48
+ function agentsFresh(track, names, windowMs, now) {
49
+ const cutoff = now - windowMs;
50
+ return names.every((n) => track.agents.some((a) => a.name === n && a.ts > cutoff));
51
+ }
52
+ //#endregion
53
+ //#region src/tracking/store.ts
54
+ /** Load a session track from a file (an empty track if absent/corrupt). */
55
+ async function loadTrack(file) {
56
+ return await readJsonFile(file) ?? emptyTrack();
57
+ }
58
+ /** Persist a session track. */
59
+ async function saveTrack(file, track) {
60
+ await writeJsonFile(file, track);
61
+ }
62
+ //#endregion
63
+ export { agentsFresh, emptyTrack, loadTrack, recordAgent, recordDoc, recordRefRead, saveTrack };
@@ -0,0 +1,26 @@
1
+ //#region src/refs/types.d.ts
2
+ /** Parsed frontmatter metadata from a reference .md file. */
3
+ interface RefMeta {
4
+ name: string;
5
+ description: string;
6
+ keywords: string;
7
+ priority: string;
8
+ related: string;
9
+ appliesTo: string;
10
+ triggerOnEdit: string;
11
+ level: string;
12
+ filePath: string;
13
+ }
14
+ /** A reference with its computed routing score. */
15
+ interface ScoredRef {
16
+ meta: RefMeta;
17
+ score: number;
18
+ }
19
+ /** Routing result with categorized references. */
20
+ interface RouteResult {
21
+ required: ScoredRef[];
22
+ optional: ScoredRef[];
23
+ skillPath: string;
24
+ }
25
+ //#endregion
26
+ export { RouteResult as n, ScoredRef as r, RefMeta as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusengine/harness",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
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",
@@ -23,6 +23,7 @@
23
23
  "./statusline": { "bun": "./src/statusline/index.ts", "types": "./dist/statusline/index.d.mts", "import": "./dist/statusline/index.mjs" },
24
24
  "./cli": { "bun": "./src/cli/index.ts", "types": "./dist/cli/index.d.mts", "import": "./dist/cli/index.mjs" },
25
25
  "./init": { "bun": "./src/init/index.ts", "types": "./dist/init/index.d.mts", "import": "./dist/init/index.mjs" },
26
+ "./tracking": { "bun": "./src/tracking/index.ts", "types": "./dist/tracking/index.d.mts", "import": "./dist/tracking/index.mjs" },
26
27
  "./adapters/claude": { "bun": "./src/adapters/claude/index.ts", "types": "./dist/adapters/claude/index.d.mts", "import": "./dist/adapters/claude/index.mjs" },
27
28
  "./adapters/codex": { "bun": "./src/adapters/codex/index.ts", "types": "./dist/adapters/codex/index.d.mts", "import": "./dist/adapters/codex/index.mjs" },
28
29
  "./adapters/cursor": { "bun": "./src/adapters/cursor/index.ts", "types": "./dist/adapters/cursor/index.d.mts", "import": "./dist/adapters/cursor/index.mjs" },
@@ -35,7 +36,7 @@
35
36
  "scripts": {
36
37
  "test": "bun test",
37
38
  "typecheck": "tsc --noEmit",
38
- "build": "tsdown src/index.ts src/config/index.ts src/util/index.ts src/detect/index.ts src/policy/index.ts src/prompt/index.ts src/memory/index.ts src/cache/index.ts src/freshness/index.ts src/refs/index.ts src/state/index.ts src/statusline/index.ts src/cli/index.ts src/cli/bin.ts src/init/index.ts src/adapters/claude/index.ts src/adapters/codex/index.ts src/adapters/cursor/index.ts src/adapters/cline/index.ts src/adapters/gemini/index.ts --dts --format esm --clean --out-dir dist",
39
+ "build": "tsdown src/index.ts src/config/index.ts src/util/index.ts src/detect/index.ts src/policy/index.ts src/prompt/index.ts src/memory/index.ts src/cache/index.ts src/freshness/index.ts src/refs/index.ts src/state/index.ts src/statusline/index.ts src/cli/index.ts src/cli/bin.ts src/init/index.ts src/tracking/index.ts src/adapters/claude/index.ts src/adapters/codex/index.ts src/adapters/cursor/index.ts src/adapters/cline/index.ts src/adapters/gemini/index.ts --dts --format esm --clean --out-dir dist",
39
40
  "prepublishOnly": "bun test && tsc --noEmit && bun run build"
40
41
  },
41
42
  "publishConfig": {
@@ -1,33 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { join } from "node:path";
3
- //#region src/policy/detect-project.ts
4
- /** Keywords that signal a development task (APEX trigger). */
5
- const DEV_KEYWORDS = /\b(implement|create|build|fix|add|refactor|develop|feature|bug|update|modify|change|write|code)\b/i;
6
- /** True when the prompt invokes the /apex command. */
7
- function isApexCommand(prompt) {
8
- return /(?:^|\s)\/apex|\/fuse-ai-pilot:apex/i.test(prompt);
9
- }
10
- /** Detect the project type by scanning config files in `dir`. */
11
- function detectProjectType(dir) {
12
- const has = (f) => existsSync(join(dir, f));
13
- if (has("next.config.js") || has("next.config.ts") || has("next.config.mjs")) return "nextjs";
14
- if (has("nuxt.config.ts") || has("nuxt.config.js")) return "nuxt";
15
- if (has("angular.json")) return "angular";
16
- if (has("svelte.config.js") || has("svelte.config.ts")) return "svelte";
17
- if (has("vite.config.ts") && has("src/App.vue")) return "vue";
18
- if (has("vite.config.ts") || has("vite.config.js")) return "react";
19
- if (has("tailwind.config.js") || has("tailwind.config.ts")) return "tailwind";
20
- if (has("composer.json") && has("artisan")) return "laravel";
21
- if (has("Gemfile") && has("config/routes.rb")) return "rails";
22
- if (has("requirements.txt") || has("pyproject.toml") || has("setup.py")) return has("manage.py") ? "django" : "python";
23
- if (has("go.mod")) return "go";
24
- if (has("Cargo.toml")) return "rust";
25
- if (has("Package.swift")) return "swift";
26
- if (has("pom.xml") || has("build.gradle") || has("build.gradle.kts")) return "java";
27
- if (has("build.sbt")) return "scala";
28
- if (has("mix.exs")) return "elixir";
29
- if (has("Gemfile")) return "ruby";
30
- return "generic";
31
- }
32
- //#endregion
33
- export { detectProjectType as n, isApexCommand as r, DEV_KEYWORDS as t };
File without changes