@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.
@@ -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
- export { Activity, DEFAULT_WINDOW_MS, DuplicationVerdict, type GateInput, HandleOptions, HandleOutcome, MCP_TTL_MS, McpIntercept, NormalizedEvent, REQUIRED_AGENTS, TRIVIAL_BUDGET, ToolEvent, activityFor, detectDuplication, dryGate, extractSymbols, gate, handleHook, harnessStateDir, isMcpTool, mcpPostStore, mcpPreIntercept, normalizeEvent, preCommitGate, queryOf, recordActivity, respond, trackFile };
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 };
@@ -1,5 +1,5 @@
1
1
  import { r as projectLayout } from "../layout-C0jaaCQC.mjs";
2
- import { _ as dryGate, a as normalizeEvent, c as mcpPostStore, d as DEFAULT_WINDOW_MS, f as REQUIRED_AGENTS, g as detectDuplication, h as preCommitGate, i as trackFile, l as mcpPreIntercept, m as gate, n as respond, o as MCP_TTL_MS, p as TRIVIAL_BUDGET, r as recordActivity, s as isMcpTool, t as handleHook, u as queryOf, v as extractSymbols, y as activityFor } from "../handle-nu3GYVek.mjs";
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 };
@@ -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-Cs0Y0MG_.mjs";
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 { n as readJsonFile, r as writeJsonFile, t as ensureDir } from "./json-io-xpTDuvtn.mjs";
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,4 +1,4 @@
1
- import { n as readJsonFile, r as writeJsonFile } from "./json-io-xpTDuvtn.mjs";
1
+ import { i as writeJsonFile, r as readJsonFile } from "./json-io-CAn72gI4.mjs";
2
2
  //#region src/tracking/session-state.ts
3
3
  /** A fresh, empty track. */
4
4
  function emptyTrack() {
@@ -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 };
@@ -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-BnHpq2ZB.mjs";
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.29",
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": ["dist", "README.md", "LICENSE"],
118
+ "files": [
119
+ "dist",
120
+ "README.md",
121
+ "LICENSE"
122
+ ],
119
123
  "license": "MIT",
120
- "repository": { "type": "git", "url": "git+https://github.com/fusengine/harness.git" },
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,