Files
zernie__vigiles/api-surface/vigiles-adapter.api.md
T
zernie effed2afcc fix(vocab): the validator knew 9 of 31 hook events, and the other 21 passed by failing open (#158)
* fix(dialect): vocabulary is a status per name, not membership in two lists

Three checks answered "does this string look like something I recognise?"
instead of "is this string valid?", and decided what to SAY about an
unrecognised name by its EDIT DISTANCE to a hand-kept list. Measured against
the vendor docs (claude-code 2.1.233, read 2026-08-17):

- `Setup`, a documented hook event, was reported as "a hook here never fires"
  and told to become `Stop` — a different event with different semantics. It
  was caught only because `setup` sits 2 edits from `stop`.
- The catalog held 9 of the vendor's 31 events. The other 22 were not
  "accepted"; 21 were unrecognised and >2 away from anything, so nothing was
  said. `SubagentStart` and `PostCompact` sat at 3 — one character from
  becoming the next `Setup`.
- `Agent` was on the never-available denylist while its own deprecated alias
  `Task` sat in the built-in catalog. The vendor renamed it the other way in
  2.1.63 and ships `tools: Agent, Read, Bash` as a worked example, so vigiles
  told delegating subagents to drop the one tool they exist to use.
- `MultiEdit`, `BashOutput`, `KillBash`, `LS` appear zero times across six
  vendor doc pages. They were in the catalog; four real tools were not.

So a valid name got an accusation or silence by spelling luck. The fix is not
a longer array: `classify` returns one of four verdicts — available /
withheld / conditional / unrecognised — and no near-match is in scope where
the report-or-skip decision is made. `unrecognised` is the third option
between silence and accusation: an advisory that names vigiles's own capture
as the possibly-stale party, surfaced and never scored.

The ≤2 threshold was not merely loose, it was set at the width of the
collision: over 465 event pairs and 741 tool pairs, ZERO are 1 edit apart and
exactly one pair in each is 2 apart — one of them being `Setup`/`Stop`. So
distance 1 is scored as a typo and distance 2 stays advisory, and a test pins
that measurement so a future catalog entry landing 1 edit from an existing
name fails here rather than in someone's repo.

Subtracts `confidentToolIssues` / `confidentHookEventIssues`: severity now
travels on the issue, so scan, lint and compileAgent cannot disagree about
which findings count. `builtinAgentTools` / `neverAvailableTools` /
`sideEffectingTools` become projections of one authored vocabulary, and the
conformance kit rejects a dialect that contradicts itself — the invariant
whose absence let `Agent` sit in two lists while dialect-drift read it out of
the vendor's own sdk-tools.d.ts every run.

Also fixes an unrelated defect found on the way: the dialect-drift hook-event
test never applied the stale-leftover reconciliation its sibling applies, so
it only ever ran against whatever old package was lying around.

Grades on faithful reductions that reproduce the recorded before-state
exactly: SethGammon/Citadel F(57) -> C(73), thedotmack/claude-mem F(55) -> C(70).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uvk2Zx66BAuxuLGLFL2umd

* fix(site): keep the browser engine's graph node-free; retire LS in the type test

collectVocabularyNotes lived in scan.ts and scan-files.ts imported it as a
VALUE, which pulled the node-only graph into the browser bundle:

  dist/scan-files.js → dist/scan.js → core/mcp.js → core/refs.js
    → core/symbols.js → @ast-grep/napi (a native .node binding esbuild
      has no loader for)

Measured by walking the built CJS require graph, and confirmed by mutation:
restoring the scan.js import makes the chain above reappear, removing it makes
@ast-grep/napi unreachable. The functions are pure data transforms and now sit
in scan-core.ts, the module whose stated job is to be node-free.

test/types/purity.ts still declared LS, which left the catalog on 2026-08-17.
It is now a negative case instead: a retired name has to FAIL at tsc time,
since its absence from a list proves nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uvk2Zx66BAuxuLGLFL2umd

* chore: regenerate the browser-parity fixture for the vocabulary message

The one changed line is the finding this PR exists to fix: the old rationale
dumped the whole catalog at the reader — including MultiEdit, BashOutput,
KillBash and LS, which are not Claude Code tools — instead of saying what is
wrong with the name it was given.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uvk2Zx66BAuxuLGLFL2umd

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-08-18 20:21:51 +05:00

4.4 KiB

API Report File for "vigiles-adapter"

Do not edit this file. It is a report generated by API Extractor.


// @public
export interface AdapterCapabilities {
    readonly harnessTesting: boolean;
    readonly referenceVerification: true;
    readonly shellHooks: boolean;
    readonly subagents: boolean;
}

// @public
export const ADAPTERS: readonly HarnessAdapter[];

// @public
export function assertAdapterConformance(adapter: HarnessAdapter): void;

// @public
export function assertAdapterLoadsHooks(adapter: HarnessAdapter): void;

// @public
export function assertHarnessTestable(adapter: HarnessAdapter): {
    runtime: NonNullable<HarnessAdapter["runtime"]>;
    modelMock: NonNullable<HarnessAdapter["modelMock"]>;
};

// @public
export function checkAdapterConformance(adapter: HarnessAdapter): ConformanceResult;

// @public (undocumented)
export interface ConformanceResult {
    // (undocumented)
    readonly failures: readonly string[];
    // (undocumented)
    readonly ok: boolean;
}

// @public
export const defaultAdapter: HarnessAdapter;

// @public
export function detectAdapter(root: string): HarnessAdapter;

// @public
export function detectAdapterResult(root: string): DetectResult;

// @public
export interface DetectResult {
    // (undocumented)
    readonly adapter: HarnessAdapter;
    readonly ambiguousWith: readonly string[];
    readonly fallback: boolean;
}

// @public
export function getAdapter(name: string): HarnessAdapter | undefined;

// @public (undocumented)
export interface HarnessAdapter {
    readonly capabilities: AdapterCapabilities;
    detect(root: string): number;
    readonly dialect: HarnessDialect;
    readonly harnessTestDriver?: HarnessTestDriver;
    readonly hookProtocol?: HookProtocol;
    readonly layout: PluginLayout;
    readonly modelMock?: ModelMock;
    readonly name: string;
    readonly runtime?: HarnessRuntime;
}

// @public (undocumented)
export interface HarnessDialect {
    readonly builtinAgentTools: readonly string[];
    readonly hookEvents: readonly string[];
    readonly hookEventVocabulary?: HarnessVocabulary;
    readonly instructionTargets: readonly string[];
    readonly knownMcpServers?: readonly string[];
    readonly mcpToolPattern: RegExp;
    readonly name: string;
    readonly neverAvailableTools: readonly string[];
    readonly noEffectHookEvents?: readonly string[];
    readonly permissionDecisionHookEvents?: readonly string[];
    readonly pluginRootToken: string;
    readonly sideEffectingTools?: readonly string[];
    readonly skillFrontmatter: SkillFrontmatterProfile;
    readonly subagentToolVocabulary?: HarnessVocabulary;
}

// @public
export interface HarnessRuntime {
    readonly agentBinary: string;
    readonly mockApiKey: string;
    readonly modelApiKeyEnv: string;
    readonly modelBaseUrlEnv: string;
    readonly name: string;
    versionKey(raw: string): string;
    wireMock(baseUrl: string): {
        readonly args: readonly string[];
        readonly env: Record<string, string>;
    };
}

// @public
export interface HookProtocol {
    readonly blockExitCode: number;
    readonly denyDecisionValues: readonly string[];
    readonly eventEnvVars: readonly string[];
    readonly injectableEvents: readonly string[];
    readonly matcherStyle?: "exact" | "regex";
    readonly name: string;
}

// @public
export interface ModelMock {
    readonly countTokensEndpoint?: string;
    readonly modelEndpoint: string;
    readonly name: string;
    readonly wireApi: string;
}

// @public
export interface PluginLayout {
    readonly agentDir: string;
    readonly commandDir: string;
    readonly hooksConventionPath: string;
    readonly instructionFile: string;
    readonly intraRefDirs: readonly string[];
    readonly manifestPath: string;
    readonly materializeRoot: string;
    readonly mcpConfigFile: string;
    readonly mcpManifestKey: string;
    readonly name: string;
    readonly pluginRootToken: string;
    readonly projectRootTokens?: readonly string[];
    readonly settingsFormat: "json" | "toml";
    readonly settingsPath: string;
    readonly skillDir: string;
    readonly surfaceDirs: readonly string[];
    readonly userSurfaceRoot?: string;
}

// @public
export function resolveAdapter(root: string, harness?: string): HarnessAdapter;

// (No @packageDocumentation comment for this package)