/** Our skill-path slugs, inherited from the `skills` CLI era. */ export type SkillAgent = 'claude-code' | 'cline' | 'codex' | 'command-code' | 'cursor' | 'hermes-agent' | 'kilo' | 'opencode' | 'pi'; export interface HarnessSupportCheck { args: string[]; /** Missing → nothing can work; setup fails. */ patterns: RegExp[]; /** Missing → degrade to key or deferred auth, never refuse. */ optionalPatterns?: RegExp[]; /** Missing → this install cannot take a stored key; fall back to OAuth. */ keyAuthPattern?: RegExp; /** Ordered alternative flags in one help output; first match wins. */ variants?: string[]; /** Matches an alternative we do not know; renames fail loudly. */ unknownVariant?: RegExp; /** Defaults to NON_INTERACTIVE_TIMEOUT_MS; raise for a slow cold start. */ timeoutMs?: number; unavailableMessage: string; } /** One entry per harness: every per-harness fact, and no behaviour. */ export interface HarnessSpec { command: string; displayName: string; /** Home-relative; existing dir means the harness ran here once. */ configDir: string; /** MCP config is read at startup; a running harness needs this to see TinyFish. */ reloadAction: string; skillAgent?: SkillAgent; /** Absent for Cursor: its connect writes mcp.json, no binary probe. */ supportCheck?: HarnessSupportCheck; /** Capturable: this `mcp add` never runs OAuth inline; unset stays inherited. */ nonInteractiveAdd?: true; /** Present only on native MCP harnesses; absent means connect is an override. */ urlStyle?: 'flag' | 'positional'; /** "positional": before `tinyfish `. "flag": after `--url `. */ addFlags?: string[]; /** Keyed adds append `--header` built from these parts. */ header?: { name: string; sep: ': ' | '='; valuePrefix?: string; viaEnv?: boolean; }; /** Keyed adds append this flag plus the env var name (Codex). */ keyEnvFlag?: string; /** Keyed adds append these flags; the key itself is seeded elsewhere (Hermes). */ keyAddFlags?: string[]; /** Keyed adds go through `mcp add-json`, which takes the URL and header in its body. */ keyedAddJsonFlags?: string[]; /** No sign-in exists here, so connect refuses a keyless install (Hermes). */ keyRequired?: true; keylessFallback?: true; loginArgs?: string[]; /** Default: one `mcp remove tinyfish` labeled from displayName. */ removals?: { args: string[]; label: string; }[]; /** Extra --uninstall row beyond `removals`; never runs pre-add. */ uninstallExtra?: { args: string[]; label: string; }; /** Shown when a keyless install leaves sign-in to the harness. */ signInHint?: string; /** Replaces the `loginArgs` step when `supportCheck.optionalPatterns` miss. */ degradedAuthNotes?: { keyed: string; deferred: string; }; /** Printed after a successful connect, on both the launch and non-launch paths. */ postConnectNote?: string; /** Sign-in is the CLI's own interactive `tinyfish auth login`. */ signInViaCliLogin?: true; /** Stored-key auth verification works against this harness's config. */ canVerifyAuth?: true; /** The CLI holds or writes the key; no harness `mcp add` carries it. */ keyHeldByCli?: true; /** Connect writes the MCP config file; no harness binary is spawned. */ cliWritesConfig?: true; /** A second reader (Cursor's IDE) loads the config with no harness binary on PATH. */ readableWithoutBinary?: true; /** `mcp add` succeeds unauthenticated; OAuth lands at first tool use. */ authDeferredAtInstall?: true; } /** One entry per harness; every per-harness list derives from it. */ export declare const HARNESS_SPECS: { 'claude-code': { command: string; displayName: string; configDir: string; reloadAction: string; skillAgent: "claude-code"; nonInteractiveAdd: true; supportCheck: { args: string[]; patterns: RegExp[]; optionalPatterns: RegExp[]; keyAuthPattern: RegExp; unavailableMessage: string; }; urlStyle: "positional"; addFlags: string[]; header: { name: string; sep: ": "; }; loginArgs: string[]; degradedAuthNotes: { keyed: string; deferred: string; }; removals: { args: string[]; label: string; }[]; }; cline: { command: string; displayName: string; configDir: string; reloadAction: string; skillAgent: "cline"; nonInteractiveAdd: true; keyRequired: true; supportCheck: { args: string[]; patterns: RegExp[]; keyAuthPattern: RegExp; unavailableMessage: string; }; urlStyle: "positional"; addFlags: string[]; header: { name: string; sep: ": "; }; }; codex: { command: string; displayName: string; configDir: string; reloadAction: string; skillAgent: "codex"; supportCheck: { args: string[]; patterns: RegExp[]; optionalPatterns: RegExp[]; keyAuthPattern: RegExp; unavailableMessage: string; }; urlStyle: "flag"; keyEnvFlag: string; degradedAuthNotes: { keyed: string; deferred: string; }; loginArgs: string[]; authDeferredAtInstall: true; }; 'command-code': { command: string; displayName: string; configDir: string; reloadAction: string; skillAgent: "command-code"; supportCheck: { args: string[]; patterns: RegExp[]; timeoutMs: number; unavailableMessage: string; }; keyRequired: true; header: { name: string; sep: ": "; valuePrefix: string; }; keyedAddJsonFlags: string[]; removals: { args: string[]; label: string; }[]; }; cursor: { command: string; displayName: string; configDir: string; reloadAction: string; skillAgent: "cursor"; readableWithoutBinary: true; canVerifyAuth: true; keyHeldByCli: true; cliWritesConfig: true; }; grok: { command: string; displayName: string; configDir: string; reloadAction: string; nonInteractiveAdd: true; supportCheck: { args: string[]; patterns: RegExp[]; keyAuthPattern: RegExp; unavailableMessage: string; }; urlStyle: "positional"; addFlags: string[]; header: { name: string; sep: ": "; valuePrefix: string; viaEnv: true; }; signInHint: string; }; hermes: { command: string; displayName: string; configDir: string; reloadAction: string; skillAgent: "hermes-agent"; keyRequired: true; keylessFallback: true; supportCheck: { args: string[]; patterns: RegExp[]; keyAuthPattern: RegExp; unavailableMessage: string; }; urlStyle: "flag"; keyAddFlags: string[]; uninstallExtra: { args: string[]; label: string; }; postConnectNote: string; keyHeldByCli: true; }; kilo: { command: string; displayName: string; configDir: string; reloadAction: string; skillAgent: "kilo"; nonInteractiveAdd: true; supportCheck: { args: string[]; patterns: RegExp[]; keyAuthPattern: RegExp; unavailableMessage: string; }; urlStyle: "flag"; keyRequired: true; header: { name: string; sep: "="; }; removals: never[]; }; omp: { command: string; displayName: string; configDir: string; reloadAction: string; keylessFallback: true; canVerifyAuth: true; keyHeldByCli: true; cliWritesConfig: true; }; openclaw: { command: string; displayName: string; configDir: string; reloadAction: string; supportCheck: { args: string[]; patterns: RegExp[]; variants: string[]; unknownVariant: RegExp; timeoutMs: number; unavailableMessage: string; }; signInViaCliLogin: true; canVerifyAuth: true; keyHeldByCli: true; }; opencode: { command: string; displayName: string; configDir: string; reloadAction: string; skillAgent: "opencode"; nonInteractiveAdd: true; keylessFallback: true; supportCheck: { args: string[]; patterns: RegExp[]; keyAuthPattern: RegExp; unavailableMessage: string; }; urlStyle: "flag"; header: { name: string; sep: "="; }; loginArgs: string[]; removals: never[]; postConnectNote: string; }; pi: { command: string; displayName: string; configDir: string; reloadAction: string; skillAgent: "pi"; canVerifyAuth: true; keyHeldByCli: true; cliWritesConfig: true; }; }; /** Derived from spec keys; one entry extends every union. */ export type Harness = keyof typeof HARNESS_SPECS; export declare const ALL_HARNESSES: readonly Harness[]; export declare function harnessSpec(harness: Harness): HarnessSpec; /** The rest of the union: every one of these must supply a connect and launch override. */ export type NonNativeHarness = { [K in Harness]: 'urlStyle' extends keyof (typeof HARNESS_SPECS)[K] ? never : 'keyRequired' extends keyof (typeof HARNESS_SPECS)[K] ? never : K; }[Harness]; /** Native MCP harnesses generate an add; key-only ones generate only the keyed form. */ export declare const NATIVE_HARNESSES: ("openclaw" | "omp" | "grok" | "cursor" | "codex" | "hermes" | "command-code" | "kilo" | "opencode" | "pi" | "cline" | "claude-code")[];