@fusengine/harness 0.1.29 → 0.1.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache/index.mjs +2 -2
- package/dist/{cache-BzbX-ztL.mjs → cache-C9z9LclL.mjs} +1 -31
- package/dist/cli/bin.mjs +14 -3
- package/dist/{skill-triggers-BZxov1es.mjs → describe-BYqhoV4c.mjs} +363 -2
- package/dist/freshness/index.mjs +1 -1
- package/dist/{freshness-CezohJHo.mjs → freshness-otdUpuvP.mjs} +1 -1
- package/dist/handle-DW9cWdVt.mjs +3356 -0
- package/dist/{index-BqdCjaT9.d.mts → index-mISsk0ff.d.mts} +143 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +6 -6
- package/dist/{json-io-xpTDuvtn.mjs → json-io-CAn72gI4.mjs} +1 -1
- package/dist/policy/index.d.mts +2 -2
- package/dist/policy/index.mjs +2 -2
- package/dist/runtime/index.d.mts +431 -1
- package/dist/runtime/index.mjs +2 -2
- package/dist/state/index.mjs +1 -1
- package/dist/{state-Cs0Y0MG_.mjs → state-ByhLeKyD.mjs} +1 -1
- package/dist/{store-BnHpq2ZB.mjs → store-D-ge2ZPI.mjs} +1 -1
- package/dist/{store-DeIsfMg5.mjs → store-PrNPm6So.mjs} +30 -1
- package/dist/tracking/index.mjs +1 -1
- package/package.json +10 -3
- package/dist/handle-nu3GYVek.mjs +0 -1090
package/dist/runtime/index.d.mts
CHANGED
|
@@ -156,6 +156,414 @@ declare function mcpPreIntercept(id: string, tool: string, input: Record<string,
|
|
|
156
156
|
/** Post-event: store the MCP/WebFetch response (extracted to markdown) in the cache. */
|
|
157
157
|
declare function mcpPostStore(tool: string, input: Record<string, unknown>, response: unknown, dir: string): void;
|
|
158
158
|
//#endregion
|
|
159
|
+
//#region src/runtime/inject-context.d.ts
|
|
160
|
+
/**
|
|
161
|
+
* UserPromptSubmit context injection: render the CLAUDE.md (+ optional APEX)
|
|
162
|
+
* preamble as a Claude `additionalContext` response, or "" when nothing to emit.
|
|
163
|
+
* @param prompt - The raw user prompt.
|
|
164
|
+
* @param cwd - Project root (for project-type detection).
|
|
165
|
+
* @returns The native hook stdout (possibly empty).
|
|
166
|
+
*/
|
|
167
|
+
declare function promptSubmitContext(prompt: string, cwd: string): string;
|
|
168
|
+
/**
|
|
169
|
+
* PreToolUse Task context injection: render the APEX sub-agent context as a
|
|
170
|
+
* Claude `additionalContext` response when `.claude/apex/` exists, else "".
|
|
171
|
+
* @param cwd - Fallback project root when `CLAUDE_PROJECT_DIR` is unset.
|
|
172
|
+
* @returns The native hook stdout (possibly empty).
|
|
173
|
+
*/
|
|
174
|
+
declare function taskContext(cwd: string): string;
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/runtime/home-state.d.ts
|
|
177
|
+
/** Home `~/.claude` dir (single source for every home-based hook path). */
|
|
178
|
+
declare function claudeHome(home?: string): string;
|
|
179
|
+
/** `~/.claude/fusengine-cache` base dir for legacy session/cache state. */
|
|
180
|
+
declare function fusengineCache(home?: string): string;
|
|
181
|
+
/** `~/.claude/fusengine-cache/sessions` — per-session JSON state dir. */
|
|
182
|
+
declare function sessionsDir(home?: string): string;
|
|
183
|
+
/** Validate a session id (1-128 url-safe chars); null when invalid. */
|
|
184
|
+
declare function sanitizeSessionId(sid: unknown): string | null;
|
|
185
|
+
/** Unified per-session state file path: `sessions/session-<sid>.json`. */
|
|
186
|
+
declare function sessionStatePath(sid: string, home?: string): string;
|
|
187
|
+
/** Load a session-state dict, or `{}` when missing/corrupt (mirrors Python). */
|
|
188
|
+
declare function loadSessionState(sid: string, home?: string): Record<string, unknown>;
|
|
189
|
+
/** Atomically persist a session-state dict (0o600 via atomicWrite, indent 2). */
|
|
190
|
+
declare function saveSessionState(sid: string, state: Record<string, unknown>, home?: string): void;
|
|
191
|
+
//#endregion
|
|
192
|
+
//#region src/runtime/fs-cleanup.d.ts
|
|
193
|
+
/** Remove files directly under `dir` matching `test` older than `maxAgeSec`. */
|
|
194
|
+
declare function removeOldFiles(dir: string, test: (name: string) => boolean, maxAgeSec: number, now?: number): void;
|
|
195
|
+
/** Trim `file` to its last `keepLines` lines when it exceeds `maxBytes`. */
|
|
196
|
+
declare function trimLogFile(file: string, maxBytes: number, keepLines: number): void;
|
|
197
|
+
/** Recursively purge files under `root/<top>` older than `ttls[top]` seconds. */
|
|
198
|
+
declare function purgeTtlTree(root: string, ttls: Record<string, number>, now?: number): void;
|
|
199
|
+
/** Bottom-up removal of empty subdirs under each `root/<top>` (best effort). */
|
|
200
|
+
declare function pruneEmptyDirs(root: string, tops: string[]): void;
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/runtime/dev-context.d.ts
|
|
203
|
+
/** Build the git portion of the dev context (branch + up to 5 changed files). */
|
|
204
|
+
declare function gitContext(cwd: string): string[];
|
|
205
|
+
/** Build the project-type portion (mirrors load-dev-context.py exactly). */
|
|
206
|
+
declare function projectContext(cwd: string): string[];
|
|
207
|
+
/**
|
|
208
|
+
* Build the SessionStart dev-context block (git + project type), or "" when
|
|
209
|
+
* nothing applies. Ports `core-guards/scripts/session-start/load-dev-context.py`.
|
|
210
|
+
* @param cwd - Project root to inspect.
|
|
211
|
+
* @returns The joined additionalContext text (possibly empty).
|
|
212
|
+
*/
|
|
213
|
+
declare function devContext(cwd: string): string;
|
|
214
|
+
//#endregion
|
|
215
|
+
//#region src/runtime/lifecycle/session-start.d.ts
|
|
216
|
+
/** Run the legacy SessionStart cleanups (stale states, caches, log trim). */
|
|
217
|
+
declare function runSessionStartCleanups(home?: string, now?: number): void;
|
|
218
|
+
/**
|
|
219
|
+
* Handle core-guards SessionStart: inject CLAUDE.md + dev context as
|
|
220
|
+
* `additionalContext`, then run the cache/state cleanups. Ports the four
|
|
221
|
+
* `session-start/*.py` scripts into one harness call.
|
|
222
|
+
* @param cwd - Project root for dev-context detection.
|
|
223
|
+
* @param home - Home dir (defaults to `~`).
|
|
224
|
+
* @param now - Clock for TTL cleanup (defaults to `Date.now()`).
|
|
225
|
+
* @returns The native hook stdout (possibly empty).
|
|
226
|
+
*/
|
|
227
|
+
declare function sessionStartCore(cwd: string, home?: string, now?: number): string;
|
|
228
|
+
//#endregion
|
|
229
|
+
//#region src/runtime/lifecycle/inject-rules.d.ts
|
|
230
|
+
/** Read & concatenate all `*.md` files (sorted) under `rulesDir`. */
|
|
231
|
+
declare function readRules(rulesDir: string): string;
|
|
232
|
+
/**
|
|
233
|
+
* Build the rules injection for claude-rules (SessionStart + UserPromptSubmit):
|
|
234
|
+
* read `<pluginRoot>/rules/*.md` and emit as `additionalContext`, or "" when no
|
|
235
|
+
* rules. Ports `claude-rules/scripts/inject-rules.py` (which always tags the
|
|
236
|
+
* output `hookEventName: "SessionStart"`, even on UserPromptSubmit).
|
|
237
|
+
* @param pluginRoot - `CLAUDE_PLUGIN_ROOT` of the claude-rules plugin.
|
|
238
|
+
* @returns The native hook stdout (possibly empty).
|
|
239
|
+
*/
|
|
240
|
+
declare function injectRules(pluginRoot: string): string;
|
|
241
|
+
//#endregion
|
|
242
|
+
//#region src/runtime/lifecycle/solid-detect.d.ts
|
|
243
|
+
/** A SOLID project profile: type label, per-file line limit, interface dir. */
|
|
244
|
+
interface SolidProfile {
|
|
245
|
+
type: string;
|
|
246
|
+
limit: number;
|
|
247
|
+
ifaceDir: string;
|
|
248
|
+
}
|
|
249
|
+
/** Detect the SOLID profile for `projectDir`, defaulting to `unknown`. */
|
|
250
|
+
declare function detectSolidProfile(projectDir: string): SolidProfile;
|
|
251
|
+
/**
|
|
252
|
+
* Handle solid SessionStart: detect the profile, append SOLID_* exports to
|
|
253
|
+
* `CLAUDE_ENV_FILE`, and return the `SOLID: …` stdout line (or "" for unknown).
|
|
254
|
+
* Ports `solid/scripts/detect-project.py`.
|
|
255
|
+
* @param env - Environment (defaults to `process.env`).
|
|
256
|
+
* @returns The plain-text stdout line (possibly empty).
|
|
257
|
+
*/
|
|
258
|
+
declare function solidDetectStart(env?: Record<string, string | undefined>): string;
|
|
259
|
+
//#endregion
|
|
260
|
+
//#region src/runtime/lifecycle/subagent-cache.d.ts
|
|
261
|
+
/**
|
|
262
|
+
* Handle SubagentStart: surface fresh MCP cache entries for the session as
|
|
263
|
+
* `additionalContext`. Ports `subagent-start/inject-context-cache.py`.
|
|
264
|
+
* @param sessionIdRaw - Raw session id from the payload.
|
|
265
|
+
* @param home - Home dir (defaults to `~`).
|
|
266
|
+
* @param env - Environment (defaults to `process.env`).
|
|
267
|
+
* @param now - Clock (defaults to `Date.now()`).
|
|
268
|
+
* @returns The native hook stdout (possibly empty).
|
|
269
|
+
*/
|
|
270
|
+
declare function subagentCacheContext(sessionIdRaw: unknown, home?: string, env?: Record<string, string | undefined>, now?: number): string;
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region src/runtime/lifecycle/agent-memory.d.ts
|
|
273
|
+
/**
|
|
274
|
+
* Handle SubagentStop: append the completion to agent-history.jsonl and, for a
|
|
275
|
+
* non-skipped agent that touched code, emit the sniper reminder + reset the
|
|
276
|
+
* counter. Ports `subagent-stop/track-agent-memory.py`.
|
|
277
|
+
* @param data - The raw hook payload.
|
|
278
|
+
* @param home - Home dir (defaults to `~`).
|
|
279
|
+
* @param now - Clock (defaults to `Date.now()`).
|
|
280
|
+
* @returns The native hook stdout (always a JSON message).
|
|
281
|
+
*/
|
|
282
|
+
declare function trackAgentMemory(data: Record<string, unknown>, home?: string, now?: number): string;
|
|
283
|
+
//#endregion
|
|
284
|
+
//#region src/runtime/lifecycle/teammate-idle.d.ts
|
|
285
|
+
/**
|
|
286
|
+
* Handle TeammateIdle: when the teammate's session-changes file shows code was
|
|
287
|
+
* modified, suggest sniper validation as `additionalContext`. Ports
|
|
288
|
+
* `teammate-idle/validate-teammate-output.py`.
|
|
289
|
+
* @param data - The raw hook payload.
|
|
290
|
+
* @param home - Home dir (defaults to `~`).
|
|
291
|
+
* @returns The native hook stdout (possibly empty).
|
|
292
|
+
*/
|
|
293
|
+
declare function validateTeammateOutput(data: Record<string, unknown>, home?: string): string;
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region src/runtime/lifecycle/tool-failure.d.ts
|
|
296
|
+
/**
|
|
297
|
+
* Handle PostToolUseFailure: append a `TOOL_FAILURE` line to
|
|
298
|
+
* `~/.claude/logs/tool-failures.log`, skipping user interrupts. Ports
|
|
299
|
+
* `post-tool-use/log-tool-failure.py`. No stdout (logging only).
|
|
300
|
+
* @param data - The raw hook payload.
|
|
301
|
+
* @param home - Home dir (defaults to `~`).
|
|
302
|
+
* @param now - Clock (defaults to `Date.now()`).
|
|
303
|
+
*/
|
|
304
|
+
declare function logToolFailure(data: Record<string, unknown>, home?: string, now?: number): void;
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region src/runtime/lifecycle/pre-compact.d.ts
|
|
307
|
+
/**
|
|
308
|
+
* Handle PreCompact: back up `.claude/apex/task.json` to `backups/`, keep only
|
|
309
|
+
* the 5 newest, and emit a confirmation. Ports `pre-compact/save-apex-state.py`.
|
|
310
|
+
* @param cwd - Project root.
|
|
311
|
+
* @param now - Clock (defaults to `Date.now()`).
|
|
312
|
+
* @returns The native hook stdout (possibly empty when no task.json).
|
|
313
|
+
*/
|
|
314
|
+
declare function saveApexState(cwd: string, now?: number): string;
|
|
315
|
+
//#endregion
|
|
316
|
+
//#region src/runtime/lifecycle/session-end.d.ts
|
|
317
|
+
/**
|
|
318
|
+
* Handle SessionEnd: remove stale `*.tmp` (>1h) under `session-tmp/` and stale
|
|
319
|
+
* legacy `claude_solid_reads_*` / `claude_session_changes_*` files (>2h) under
|
|
320
|
+
* `fusengine-cache`. Ports `session-end/cleanup-session.py`. No stdout.
|
|
321
|
+
* @param home - Home dir (defaults to `~`).
|
|
322
|
+
* @param now - Clock (defaults to `Date.now()`).
|
|
323
|
+
*/
|
|
324
|
+
declare function cleanupSession(home?: string, now?: number): void;
|
|
325
|
+
//#endregion
|
|
326
|
+
//#region src/runtime/lifecycle/instructions-loaded.d.ts
|
|
327
|
+
/**
|
|
328
|
+
* Handle InstructionsLoaded: append `load_reason | memory_type | file_path` to
|
|
329
|
+
* the per-session debug log. Ports `instructions-loaded/validate-rules-loaded.py`.
|
|
330
|
+
* No stdout (logging only; InstructionsLoaded has no decision control).
|
|
331
|
+
* @param data - The raw hook payload.
|
|
332
|
+
* @param home - Home dir (defaults to `~`).
|
|
333
|
+
*/
|
|
334
|
+
declare function validateRulesLoaded(data: Record<string, unknown>, home?: string): void;
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region src/runtime/lifecycle/track-changes.d.ts
|
|
337
|
+
/**
|
|
338
|
+
* Handle PostToolUse Write/Edit: track the cumulative set of modified code
|
|
339
|
+
* files per session and emit the mandatory "SNIPER VALIDATION REQUIRED"
|
|
340
|
+
* additionalContext. Ports `post-tool-use/track-session-changes.py`.
|
|
341
|
+
* @param sessionIdRaw - Raw session id from the payload.
|
|
342
|
+
* @param filePath - The edited file path.
|
|
343
|
+
* @param home - Home dir (defaults to `~`).
|
|
344
|
+
* @param now - Clock (defaults to `Date.now()`).
|
|
345
|
+
* @returns The native hook stdout (possibly empty when not a code file).
|
|
346
|
+
*/
|
|
347
|
+
declare function trackSessionChanges(sessionIdRaw: unknown, filePath: string, home?: string, now?: number): string;
|
|
348
|
+
//#endregion
|
|
349
|
+
//#region src/runtime/lifecycle/post-edit-ts.d.ts
|
|
350
|
+
/**
|
|
351
|
+
* Handle PostToolUse for TS/TSX: report eslint/prettier issues (never fixes) as
|
|
352
|
+
* additionalContext. Ports `post-tool-use/post-edit-typescript.py`.
|
|
353
|
+
* @param filePath - The edited file path.
|
|
354
|
+
* @returns The native hook stdout (possibly empty).
|
|
355
|
+
*/
|
|
356
|
+
declare function postEditTypescript(filePath: string): string;
|
|
357
|
+
//#endregion
|
|
358
|
+
//#region src/runtime/lifecycle/aipilot/dispatch-aipilot.d.ts
|
|
359
|
+
/**
|
|
360
|
+
* Dispatch an ai-pilot-scope lifecycle event. Returns the native stdout, or
|
|
361
|
+
* `null` when unhandled (caller falls through to the default pipeline).
|
|
362
|
+
*/
|
|
363
|
+
declare function dispatchAipilot(event: string, payload: Record<string, unknown>, cwd: string, now: number): Promise<string | null>;
|
|
364
|
+
/** PostToolUse (TaskCreate/TaskUpdate) sync for the ai-pilot scope. */
|
|
365
|
+
declare function aipilotPostToolUse(payload: Record<string, unknown>, cwd: string): Promise<string>;
|
|
366
|
+
//#endregion
|
|
367
|
+
//#region src/runtime/lifecycle/dispatch.d.ts
|
|
368
|
+
/** Which plugin's hooks.json invoked the harness (selects SessionStart behavior). */
|
|
369
|
+
type PluginScope = "core" | "solid" | "rules" | "carto" | "security" | "changelog" | "aipilot";
|
|
370
|
+
/** Inputs the lifecycle dispatcher needs (clock + roots injected). */
|
|
371
|
+
interface LifecycleInput {
|
|
372
|
+
event: string;
|
|
373
|
+
payload: Record<string, unknown>;
|
|
374
|
+
cwd: string;
|
|
375
|
+
scope: PluginScope;
|
|
376
|
+
now: number;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Route a lifecycle/session/context hook event to its ported handler. Returns
|
|
380
|
+
* the native stdout when handled, or `null` when the event is not a lifecycle
|
|
381
|
+
* event (so the caller falls through to the PreToolUse/PostToolUse pipeline).
|
|
382
|
+
* @param input - The dispatch input.
|
|
383
|
+
* @returns The native hook stdout, or `null` when unhandled.
|
|
384
|
+
*/
|
|
385
|
+
declare function dispatchLifecycle(input: LifecycleInput): string | null;
|
|
386
|
+
//#endregion
|
|
387
|
+
//#region src/runtime/lifecycle/post-tracking.d.ts
|
|
388
|
+
/**
|
|
389
|
+
* Dispatch the appropriate PostToolUse tracker for the invoking scope. Carto
|
|
390
|
+
* persists manual enrichments; security records skill reads + MCP research;
|
|
391
|
+
* changelog records watch research. Side-effect only.
|
|
392
|
+
* @param scope - The invoking plugin scope.
|
|
393
|
+
* @param event - The normalized event.
|
|
394
|
+
* @param input - The raw tool input.
|
|
395
|
+
* @param now - Clock.
|
|
396
|
+
*/
|
|
397
|
+
declare function postTrackingSideEffects(scope: PluginScope, event: NormalizedEvent, input: Record<string, unknown>, now: number): void;
|
|
398
|
+
//#endregion
|
|
399
|
+
//#region src/runtime/lifecycle/changelog-research.d.ts
|
|
400
|
+
/**
|
|
401
|
+
* Append an exa/WebFetch/WebSearch query to today's changelog research log.
|
|
402
|
+
* No-op for other tools. No stdout (errors swallowed).
|
|
403
|
+
* @param tool - The tool name.
|
|
404
|
+
* @param input - The tool input (query/url/prompt).
|
|
405
|
+
* @param now - Clock.
|
|
406
|
+
* @param home - Home dir.
|
|
407
|
+
*/
|
|
408
|
+
declare function trackWatchResearch(tool: string, input: Record<string, unknown>, now?: number, home?: string): void;
|
|
409
|
+
//#endregion
|
|
410
|
+
//#region src/runtime/lifecycle/cartographer/session-start.d.ts
|
|
411
|
+
/**
|
|
412
|
+
* Regenerate the project map for `cwd` on SessionStart. Returns "" (side-effect
|
|
413
|
+
* only — no additionalContext).
|
|
414
|
+
* @param cwd - The working directory.
|
|
415
|
+
* @returns "" always.
|
|
416
|
+
*/
|
|
417
|
+
declare function cartoSessionStart(cwd: string): string;
|
|
418
|
+
//#endregion
|
|
419
|
+
//#region src/runtime/lifecycle/cartographer/project-map.d.ts
|
|
420
|
+
/**
|
|
421
|
+
* True when `dir` looks like a project root (has an indicator file) and is not
|
|
422
|
+
* the home directory or filesystem root.
|
|
423
|
+
* @param dir - Directory to test.
|
|
424
|
+
* @returns Whether `dir` is a project root.
|
|
425
|
+
*/
|
|
426
|
+
declare function isProject(dir: string): boolean;
|
|
427
|
+
/**
|
|
428
|
+
* Generate the `.cartographer/project` index tree for `cwd` when it is a real
|
|
429
|
+
* project directory. Always returns "" (no additionalContext emitted).
|
|
430
|
+
* @param cwd - The working directory.
|
|
431
|
+
* @param outputDir - Override for the output tree root.
|
|
432
|
+
* @returns "" (side-effect only).
|
|
433
|
+
*/
|
|
434
|
+
declare function generateProjectMap(cwd: string, outputDir?: string): string;
|
|
435
|
+
//#endregion
|
|
436
|
+
//#region src/runtime/lifecycle/cartographer/track-enrichment.d.ts
|
|
437
|
+
/**
|
|
438
|
+
* Record manually-edited descriptions from a cartographer `index.md` into the
|
|
439
|
+
* adjacent `.enriched.json` sidecar. No-op for unrelated paths. No stdout.
|
|
440
|
+
* @param filePath - The edited file path.
|
|
441
|
+
*/
|
|
442
|
+
declare function trackEnrichment(filePath: string): void;
|
|
443
|
+
//#endregion
|
|
444
|
+
//#region src/runtime/lifecycle/cartographer/write-tree.d.ts
|
|
445
|
+
/**
|
|
446
|
+
* Write `index.md` files mirroring `source` under `output`, recursing into
|
|
447
|
+
* subdirectories. Directory lines carry a file-count hint; file lines carry a
|
|
448
|
+
* derived description and link to the real absolute source path.
|
|
449
|
+
* @param source - Absolute source directory.
|
|
450
|
+
* @param output - Absolute output directory for the index tree.
|
|
451
|
+
* @param back - Relative `← back` link target ("" at the root).
|
|
452
|
+
* @param exclude - Directory/name set to skip.
|
|
453
|
+
*/
|
|
454
|
+
declare function writeTree(source: string, output: string, back?: string, exclude?: ReadonlySet<string>): void;
|
|
455
|
+
//#endregion
|
|
456
|
+
//#region src/runtime/lifecycle/cartographer/merge.d.ts
|
|
457
|
+
/**
|
|
458
|
+
* Load the `.enriched.json` sidecar's `entries` map for an output index.
|
|
459
|
+
* @param outputIndexPath - Path to the index.md being written.
|
|
460
|
+
* @returns The path→desc enrichment map (possibly empty).
|
|
461
|
+
*/
|
|
462
|
+
declare function loadEnriched(outputIndexPath: string): Record<string, string>;
|
|
463
|
+
/**
|
|
464
|
+
* Merge freshly generated lines with prior descriptions: enriched sidecar wins,
|
|
465
|
+
* else a longer pre-existing description is preserved.
|
|
466
|
+
* @param newLines - The freshly generated index lines.
|
|
467
|
+
* @param outputIndexPath - Path to the existing index.md (if any).
|
|
468
|
+
* @returns The merged lines.
|
|
469
|
+
*/
|
|
470
|
+
declare function mergeLines(newLines: string[], outputIndexPath: string): string[];
|
|
471
|
+
//#endregion
|
|
472
|
+
//#region src/runtime/lifecycle/cartographer/fs-util.d.ts
|
|
473
|
+
/**
|
|
474
|
+
* Read a file and derive its one-line description (frontmatter / heading /
|
|
475
|
+
* comment). "" on any error or when nothing is found.
|
|
476
|
+
* @param filePath - Absolute path to the file.
|
|
477
|
+
* @returns The description, or "".
|
|
478
|
+
*/
|
|
479
|
+
declare function getFileDesc(filePath: string): string;
|
|
480
|
+
/**
|
|
481
|
+
* Recursively count files whose relative path parts are all visible (no leading
|
|
482
|
+
* "." or "_") and none excluded. Best-effort (partial count on errors).
|
|
483
|
+
* @param dir - Directory to count under.
|
|
484
|
+
* @param exclude - Directory/name set to skip.
|
|
485
|
+
* @returns The file count.
|
|
486
|
+
*/
|
|
487
|
+
declare function countFiles(dir: string, exclude: ReadonlySet<string>): number;
|
|
488
|
+
/** Absolute children of `source`, split into dirs/files, sorted by full path. */
|
|
489
|
+
declare function listChildren(source: string, exclude: ReadonlySet<string>): {
|
|
490
|
+
dirs: string[];
|
|
491
|
+
files: string[];
|
|
492
|
+
};
|
|
493
|
+
//#endregion
|
|
494
|
+
//#region src/runtime/lifecycle/security/check-skill.d.ts
|
|
495
|
+
/**
|
|
496
|
+
* Build a non-blocking PreToolUse `allow` response with a security advisory when
|
|
497
|
+
* editing a code file before the security skill has been read. "" otherwise.
|
|
498
|
+
* @param tool - The tool name (`Write`/`Edit`).
|
|
499
|
+
* @param filePath - The target file path.
|
|
500
|
+
* @param now - Clock.
|
|
501
|
+
* @param home - Home dir.
|
|
502
|
+
* @returns The advisory response JSON, or "".
|
|
503
|
+
*/
|
|
504
|
+
declare function securityAdvisory(tool: string, filePath: string, now?: number, home?: string): string;
|
|
505
|
+
//#endregion
|
|
506
|
+
//#region src/runtime/lifecycle/security/track-skill-read.d.ts
|
|
507
|
+
/**
|
|
508
|
+
* Mark the security skill as read when a Read hits a security skill reference.
|
|
509
|
+
* No-op for other tools/paths. No stdout.
|
|
510
|
+
* @param tool - The tool name.
|
|
511
|
+
* @param filePath - The read file path.
|
|
512
|
+
* @param now - Clock.
|
|
513
|
+
* @param home - Home dir.
|
|
514
|
+
*/
|
|
515
|
+
declare function trackSkillRead(tool: string, filePath: string, now?: number, home?: string): void;
|
|
516
|
+
//#endregion
|
|
517
|
+
//#region src/runtime/lifecycle/security/track-mcp.d.ts
|
|
518
|
+
/**
|
|
519
|
+
* Append a context7/exa research call to today's security state. No-op for other
|
|
520
|
+
* tools. No stdout.
|
|
521
|
+
* @param tool - The tool name.
|
|
522
|
+
* @param input - The tool input (query/libraryId/libraryName).
|
|
523
|
+
* @param now - Clock.
|
|
524
|
+
* @param home - Home dir.
|
|
525
|
+
*/
|
|
526
|
+
declare function trackMcpResearch(tool: string, input: Record<string, unknown>, now?: number, home?: string): void;
|
|
527
|
+
//#endregion
|
|
528
|
+
//#region src/runtime/lifecycle/security/skill-state.d.ts
|
|
529
|
+
/** `~/.claude/logs/00-security` state directory. */
|
|
530
|
+
declare function securityStateDir(home?: string): string;
|
|
531
|
+
/** Current UTC date as `YYYY-MM-DD`. */
|
|
532
|
+
declare function todayUtc(now?: number): string;
|
|
533
|
+
/** Current UTC instant as `YYYY-MM-DDTHH:MM:SSZ` (seconds, no millis). */
|
|
534
|
+
declare function isoUtc(now?: number): string;
|
|
535
|
+
/** Today's security-state file path. */
|
|
536
|
+
declare function securityStatePath(now?: number, home?: string): string;
|
|
537
|
+
/** Load today's security state, or `{}` when missing/corrupt. */
|
|
538
|
+
declare function loadSecurityState(now?: number, home?: string): Record<string, unknown>;
|
|
539
|
+
/** Persist today's security state (indent 2, no trailing newline). */
|
|
540
|
+
declare function saveSecurityState(state: Record<string, unknown>, now?: number, home?: string): void;
|
|
541
|
+
//#endregion
|
|
542
|
+
//#region src/runtime/lifecycle-bridge.d.ts
|
|
543
|
+
/**
|
|
544
|
+
* Run the ported lifecycle/session/context hooks (SessionStart, SubagentStart/
|
|
545
|
+
* Stop, TeammateIdle, PostToolUseFailure, PreCompact, SessionEnd,
|
|
546
|
+
* InstructionsLoaded, rules-scope UserPromptSubmit). Returns the native stdout
|
|
547
|
+
* when handled, or `null` to fall through to the tool-use pipeline.
|
|
548
|
+
* @param payload - The raw hook payload.
|
|
549
|
+
* @param cwd - Project root.
|
|
550
|
+
* @param scope - The invoking plugin scope (defaults to `core`).
|
|
551
|
+
* @param now - Clock.
|
|
552
|
+
* @returns The native stdout, or `null` when unhandled.
|
|
553
|
+
*/
|
|
554
|
+
declare function lifecycleStdout(payload: Record<string, unknown>, cwd: string, scope: PluginScope, now: number): string | null;
|
|
555
|
+
/**
|
|
556
|
+
* Post-edit additions for core-scope PostToolUse Write/Edit: track cumulative
|
|
557
|
+
* session changes (sniper reminder) + report eslint/prettier issues. Returns the
|
|
558
|
+
* combined extra stdout (track-changes wins; lint appended only when no track
|
|
559
|
+
* output), or "" when nothing to emit.
|
|
560
|
+
* @param scope - The invoking plugin scope.
|
|
561
|
+
* @param event - The normalized event.
|
|
562
|
+
* @param now - Clock.
|
|
563
|
+
* @returns The extra stdout (possibly empty).
|
|
564
|
+
*/
|
|
565
|
+
declare function postEditContext(scope: PluginScope, event: NormalizedEvent, now: number): string;
|
|
566
|
+
//#endregion
|
|
159
567
|
//#region src/runtime/handle.d.ts
|
|
160
568
|
/** Options for {@link handleHook} (caller supplies the clock + project root). */
|
|
161
569
|
interface HandleOptions {
|
|
@@ -165,6 +573,8 @@ interface HandleOptions {
|
|
|
165
573
|
refsDir?: string;
|
|
166
574
|
/** APEX freshness window in ms (from `FUSE_ENFORCE_TTL_SEC`). */
|
|
167
575
|
windowMs?: number;
|
|
576
|
+
/** Which plugin's hooks.json invoked the harness (selects lifecycle behavior). */
|
|
577
|
+
scope?: PluginScope;
|
|
168
578
|
}
|
|
169
579
|
/** What the hook bin should print + exit with. */
|
|
170
580
|
interface HandleOutcome {
|
|
@@ -179,4 +589,24 @@ interface HandleOutcome {
|
|
|
179
589
|
*/
|
|
180
590
|
declare function handleHook(id: string, payload: Record<string, unknown>, opts: HandleOptions): Promise<HandleOutcome>;
|
|
181
591
|
//#endregion
|
|
182
|
-
|
|
592
|
+
//#region src/runtime/handle-pre.d.ts
|
|
593
|
+
/** Context the PreToolUse pipeline needs (resolved once by {@link handleHook}). */
|
|
594
|
+
interface PreContext {
|
|
595
|
+
id: string;
|
|
596
|
+
payload: Record<string, unknown>;
|
|
597
|
+
event: NormalizedEvent;
|
|
598
|
+
framework: string;
|
|
599
|
+
mcpDir: string;
|
|
600
|
+
file: string;
|
|
601
|
+
opts: HandleOptions;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Run the PreToolUse pipeline: MCP/WebFetch cache intercept, design gate, APEX
|
|
605
|
+
* Task context injection, then the stateless+APEX gate chain. Returns the native
|
|
606
|
+
* hook outcome (deny/ask/inject or allow).
|
|
607
|
+
* @param ctx - The resolved pre-context.
|
|
608
|
+
* @returns The hook outcome.
|
|
609
|
+
*/
|
|
610
|
+
declare function handlePre(ctx: PreContext): Promise<HandleOutcome>;
|
|
611
|
+
//#endregion
|
|
612
|
+
export { Activity, DEFAULT_WINDOW_MS, DuplicationVerdict, type GateInput, HandleOptions, HandleOutcome, LifecycleInput, MCP_TTL_MS, McpIntercept, NormalizedEvent, PluginScope, PreContext, REQUIRED_AGENTS, SolidProfile, TRIVIAL_BUDGET, ToolEvent, activityFor, aipilotPostToolUse, cartoSessionStart, claudeHome, cleanupSession, countFiles, detectDuplication, detectSolidProfile, devContext, dispatchAipilot, dispatchLifecycle, dryGate, extractSymbols, fusengineCache, gate, generateProjectMap, getFileDesc, gitContext, handleHook, handlePre, harnessStateDir, injectRules, isMcpTool, isProject, isoUtc, lifecycleStdout, listChildren, loadEnriched, loadSecurityState, loadSessionState, logToolFailure, mcpPostStore, mcpPreIntercept, mergeLines, normalizeEvent, postEditContext, postEditTypescript, postTrackingSideEffects, preCommitGate, projectContext, promptSubmitContext, pruneEmptyDirs, purgeTtlTree, queryOf, readRules, recordActivity, removeOldFiles, respond, runSessionStartCleanups, sanitizeSessionId, saveApexState, saveSecurityState, saveSessionState, securityAdvisory, securityStateDir, securityStatePath, sessionStartCore, sessionStatePath, sessionsDir, solidDetectStart, subagentCacheContext, taskContext, todayUtc, trackAgentMemory, trackEnrichment, trackFile, trackMcpResearch, trackSessionChanges, trackSkillRead, trackWatchResearch, trimLogFile, validateRulesLoaded, validateTeammateOutput, writeTree };
|
package/dist/runtime/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as projectLayout } from "../layout-C0jaaCQC.mjs";
|
|
2
|
-
import { _ as
|
|
2
|
+
import { $ as purgeTtlTree, A as isProject, B as cleanupSession, C as todayUtc, Ct as activityFor, D as dispatchAipilot, E as aipilotPostToolUse, F as getFileDesc, G as subagentCacheContext, H as logToolFailure, I as listChildren, J as injectRules, K as detectSolidProfile, L as postEditTypescript, M as loadEnriched, N as mergeLines, O as cartoSessionStart, P as countFiles, Q as pruneEmptyDirs, R as trackSessionChanges, S as securityStatePath, St as queryOf, T as dispatchLifecycle, U as validateTeammateOutput, V as saveApexState, W as trackAgentMemory, X as runSessionStartCleanups, Y as readRules, Z as sessionStartCore, _ as trackSkillRead, _t as normalizeEvent, a as TRIVIAL_BUDGET, at as claudeHome, b as saveSecurityState, bt as mcpPostStore, c as detectDuplication, ct as sanitizeSessionId, d as lifecycleStdout, dt as sessionsDir, et as removeOldFiles, f as postEditContext, ft as promptSubmitContext, g as trackMcpResearch, gt as trackFile, h as trackWatchResearch, ht as recordActivity, i as REQUIRED_AGENTS, it as projectContext, j as writeTree, k as generateProjectMap, l as dryGate, lt as saveSessionState, m as postTrackingSideEffects, mt as respond, n as handlePre, nt as devContext, o as gate, ot as fusengineCache, p as securityAdvisory, pt as taskContext, q as solidDetectStart, r as DEFAULT_WINDOW_MS, rt as gitContext, s as preCommitGate, st as loadSessionState, t as handleHook, tt as trimLogFile, u as extractSymbols, ut as sessionStatePath, v as isoUtc, vt as MCP_TTL_MS, w as trackEnrichment, x as securityStateDir, xt as mcpPreIntercept, y as loadSecurityState, yt as isMcpTool, z as validateRulesLoaded } from "../handle-DW9cWdVt.mjs";
|
|
3
3
|
//#region src/runtime/storage.ts
|
|
4
4
|
/**
|
|
5
5
|
* The project's single state dir (`<root>/.harness`) — neutral + harness-agnostic,
|
|
@@ -9,4 +9,4 @@ function harnessStateDir(root) {
|
|
|
9
9
|
return projectLayout(root).stateDir;
|
|
10
10
|
}
|
|
11
11
|
//#endregion
|
|
12
|
-
export { DEFAULT_WINDOW_MS, MCP_TTL_MS, REQUIRED_AGENTS, TRIVIAL_BUDGET, activityFor, detectDuplication, dryGate, extractSymbols, gate, handleHook, harnessStateDir, isMcpTool, mcpPostStore, mcpPreIntercept, normalizeEvent, preCommitGate, queryOf, recordActivity, respond, trackFile };
|
|
12
|
+
export { DEFAULT_WINDOW_MS, MCP_TTL_MS, REQUIRED_AGENTS, TRIVIAL_BUDGET, activityFor, aipilotPostToolUse, cartoSessionStart, claudeHome, cleanupSession, countFiles, detectDuplication, detectSolidProfile, devContext, dispatchAipilot, dispatchLifecycle, dryGate, extractSymbols, fusengineCache, gate, generateProjectMap, getFileDesc, gitContext, handleHook, handlePre, harnessStateDir, injectRules, isMcpTool, isProject, isoUtc, lifecycleStdout, listChildren, loadEnriched, loadSecurityState, loadSessionState, logToolFailure, mcpPostStore, mcpPreIntercept, mergeLines, normalizeEvent, postEditContext, postEditTypescript, postTrackingSideEffects, preCommitGate, projectContext, promptSubmitContext, pruneEmptyDirs, purgeTtlTree, queryOf, readRules, recordActivity, removeOldFiles, respond, runSessionStartCleanups, sanitizeSessionId, saveApexState, saveSecurityState, saveSessionState, securityAdvisory, securityStateDir, securityStatePath, sessionStartCore, sessionStatePath, sessionsDir, solidDetectStart, subagentCacheContext, taskContext, todayUtc, trackAgentMemory, trackEnrichment, trackFile, trackMcpResearch, trackSessionChanges, trackSkillRead, trackWatchResearch, trimLogFile, validateRulesLoaded, validateTeammateOutput, writeTree };
|
package/dist/state/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as ensureStateDir, c as stateFilePath, i as apexStateDir, l as acquireLock, n as taskCreate, o as loadState, r as taskStart, s as saveState, t as taskComplete } from "../state-
|
|
1
|
+
import { a as ensureStateDir, c as stateFilePath, i as apexStateDir, l as acquireLock, n as taskCreate, o as loadState, r as taskStart, s as saveState, t as taskComplete } from "../state-ByhLeKyD.mjs";
|
|
2
2
|
export { acquireLock, apexStateDir, ensureStateDir, loadState, saveState, stateFilePath, taskComplete, taskCreate, taskStart };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as writeJsonFile, n as ensureDir, r as readJsonFile } from "./json-io-CAn72gI4.mjs";
|
|
2
2
|
import { mkdir, rmdir } from "node:fs/promises";
|
|
3
3
|
//#region src/state/lock.ts
|
|
4
4
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
import { dirname, join } from "node:path";
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
//#region src/cache/io.ts
|
|
4
|
+
/** Read a JSON array from `path`; [] on missing/corrupt/non-array. */
|
|
5
|
+
function loadIndex(path) {
|
|
6
|
+
try {
|
|
7
|
+
if (!existsSync(path)) return [];
|
|
8
|
+
const data = JSON.parse(readFileSync(path, "utf8"));
|
|
9
|
+
return Array.isArray(data) ? data : [];
|
|
10
|
+
} catch {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** Summarize an index of `{ tool?, ts? }` entries. */
|
|
15
|
+
function summarizeIndex(index) {
|
|
16
|
+
const byTool = {};
|
|
17
|
+
const timestamps = [];
|
|
18
|
+
for (const entry of index) {
|
|
19
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
20
|
+
const e = entry;
|
|
21
|
+
if (typeof e.tool === "string") byTool[e.tool] = (byTool[e.tool] ?? 0) + 1;
|
|
22
|
+
if (typeof e.ts === "string") timestamps.push(e.ts);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
total: index.length,
|
|
26
|
+
byTool,
|
|
27
|
+
oldestTs: timestamps.length ? timestamps.reduce((a, b) => a < b ? a : b) : null,
|
|
28
|
+
newestTs: timestamps.length ? timestamps.reduce((a, b) => a > b ? a : b) : null
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
3
32
|
//#region src/cache/mcp-response.ts
|
|
4
33
|
const MAX_DEPTH = 5;
|
|
5
34
|
/**
|
|
@@ -57,4 +86,4 @@ function cacheStore(dir, tool, query, content) {
|
|
|
57
86
|
writeFileSync(path, content);
|
|
58
87
|
}
|
|
59
88
|
//#endregion
|
|
60
|
-
export { extractText as a, mcpCacheKey as i, cachePath as n, cacheStore as r, cacheLookup as t };
|
|
89
|
+
export { extractText as a, mcpCacheKey as i, cachePath as n, loadIndex as o, cacheStore as r, summarizeIndex as s, cacheLookup as t };
|
package/dist/tracking/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as recordAgent, c as recordRefRead, i as emptyTrack, l as recordTrivialEdit, n as saveTrack, o as recordBrainstormRequired, r as agentsFresh, s as recordDoc, t as loadTrack, u as trivialCount } from "../store-
|
|
1
|
+
import { a as recordAgent, c as recordRefRead, i as emptyTrack, l as recordTrivialEdit, n as saveTrack, o as recordBrainstormRequired, r as agentsFresh, s as recordDoc, t as loadTrack, u as trivialCount } from "../store-D-ge2ZPI.mjs";
|
|
2
2
|
export { agentsFresh, emptyTrack, loadTrack, recordAgent, recordBrainstormRequired, recordDoc, recordRefRead, recordTrivialEdit, saveTrack, trivialCount };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fusengine/harness",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.31",
|
|
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",
|
|
@@ -115,9 +115,16 @@
|
|
|
115
115
|
"import": "./dist/adapters/gemini/index.mjs"
|
|
116
116
|
}
|
|
117
117
|
},
|
|
118
|
-
"files": [
|
|
118
|
+
"files": [
|
|
119
|
+
"dist",
|
|
120
|
+
"README.md",
|
|
121
|
+
"LICENSE"
|
|
122
|
+
],
|
|
119
123
|
"license": "MIT",
|
|
120
|
-
"repository": {
|
|
124
|
+
"repository": {
|
|
125
|
+
"type": "git",
|
|
126
|
+
"url": "git+https://github.com/fusengine/harness.git"
|
|
127
|
+
},
|
|
121
128
|
"homepage": "https://github.com/fusengine/harness#readme",
|
|
122
129
|
"bugs": "https://github.com/fusengine/harness/issues",
|
|
123
130
|
"sideEffects": false,
|