import type { IdeId } from '../ide/ide-types.js'; /** Sentinel substring identifying a Claude-Code gate-enforce hook entry. */ export declare const HOOK_ENFORCE_SENTINEL = "peaks gate enforce"; /** * Resolve the adapter + per-IDE values used to render the settings.json entries. * Each adapter that wants its own gate command (Trae / Cursor / Codex use * `peaks hook handle`, the new dispatcher) overrides the default here. * * Slice #2 + #12 + #13 (2.4.0): the per-IDE hook command is read from a * small dispatch table below. New adapters pick a command by adding a * single line to that table — no if/else growth in this function. */ interface ResolvedHookSpec { readonly hookEnforceCommand: string; readonly hookCodeGateCommand: string; readonly hookEnforceSentinel: string; readonly hookEnforceMatcher: string; readonly hookEnforceEvent: string; readonly hookEnforceMcpMatcher: string | undefined; /** * True when the gate-enforce entry must be materialized into the IDE's * MACHINE-LOCAL settings file rather than the shared one (see * `hookEnforceShell` below). Set for Claude Code, whose project-scope * hooks have a gitignored per-machine sibling file. */ readonly hookEnforceMachineLocal: boolean; /** * The `shell` field for the gate-enforce handler, or `undefined` to omit * the key entirely and let the IDE use its documented default. * * Claude Code only. See `resolveHookShell` for why Windows needs this. */ readonly hookEnforceShell: string | undefined; } /** * Claude Code runs a shell-form hook command through a shell that defaults * to bash — which on Windows means Git Bash, and MSYS2's bash * force-allocates its own console window. The result is a visible window on * EVERY Bash tool call. The window is created by the spawner (Claude Code) * before any peaks code runs, so `windowsHide` and any in-process hiding * cannot help; pinning the hook's `shell` is the only lever the hook schema * offers. The platform-neutral default (`undefined` → omit the key) is kept * everywhere else. * * Scope note: this is applied to the `Bash`-matcher handlers only. The three * `SessionStart` entries are deliberately NOT pinned, and not because the * mechanism is believed absent there — see the block comment in * `resolveHookEntries` for what is and is not established, and for why adding * the pin in place would break every non-Windows teammate. */ export declare function resolveHookShell(platform?: NodeJS.Platform): string | undefined; /** * True when the IDE is tabled in `HOOK_COMMAND_BY_IDE`, i.e. peaks can write a * hook entry for it. `qoder` / `tongyi-lingma` / `zcode` are reserved adapter * ids the registry knows but this table does not, so no hook can exist for * them — which makes "uninstall" a no-op (nothing was ever installed) while * "install" remains a hard failure. */ export declare function hasHookSpec(ide: IdeId): boolean; export declare function resolveHookSpec(ide: IdeId): ResolvedHookSpec; /** A typed descriptor for a single peaks-managed hook entry. */ export type PeaksHookEntry = { sentinel: string; matcher: string; command: string; event: string; /** * When true the entry is written to the machine-local, gitignored settings * file (`.claude/settings.local.json`) instead of the shared one, because * the entry carries a machine-specific value (the `shell` field). */ machineLocal?: boolean; /** Optional `shell` field for the emitted handler. */ shell?: string; }; /** * The code-gate entry's CLI adapter is `src/cli/commands/code-gate-command.ts`, * its decision logic is `src/services/hooks/pre-tool-code-gate.ts`, and the * shell-script sibling `src/services/hooks/pre-tool-code-gate.sh` encodes the * same logic for non-Node harnesses. */ export declare const HOOK_CODE_GATE_SENTINEL = "peaks code-gate"; export declare const HOOK_CODE_GATE_MATCHER = "Edit|Write|MultiEdit"; export declare const HOOK_CODE_GATE_EVENT = "PreToolUse"; export declare function resolveHookEntries(ide: IdeId, _skipProgress?: boolean): PeaksHookEntry[]; export declare function resolveLegacySentinels(ide: IdeId): ReadonlyArray; /** * The set of Skills whose use is denied at the IDE permissions layer for * every `peaks hooks install` call. Each entry is the namespace-qualified * skill id; the helper `formatSuperpowersDenyEntry` wraps it in the IDE's * `UseSkill(...)` envelope. * * new skill, append its id here and re-run `peaks hooks install`. The list * is also the inverse for uninstall: `removeHookInstall` rebuilds the deny * list by filtering out anything whose wrapped form matches one of these * ids. * * denied so the superpowers chain cannot silently override peaks-code. * All four are referenced by `superpowers:using-superpowers` as the * "bug → systematic-debugging / TDD / verification-before-completion / * recursion via using-superpowers" auto-routing pattern — denying the * trigger removes the auto-routing surface while leaving all four * still readable as reference material from * `references/external-skill-invocation.md`. */ export declare const SUPERPOWERS_DENIED_SKILLS: ReadonlyArray; export declare function formatSuperpowersDenyEntry(skillId: string): string; export declare const SUPERPOWERS_DENY_SENTINELS: ReadonlySet; /** * Adapter table: a Peaks *concept* → the settings value an EXTERNAL, * non-Peaks PreToolUse gate must read to honour it. * * The concept Peaks holds is "the `.peaks/**` workspace tree is not project * source, so 'who imports this / what schema' carries no signal there". * Peaks already enforces it for its own hooks: slice 2.0.1-bug3 materializes * a `Write|Edit|MultiEdit` bypass in `.claude/settings.local.json` precisely * so the first workspace write is never fact-gated. This table is that same * intent declared to a gate Peaks does not own. * * The mapped key is read by the ECC plugin's `gateguard-fact-force` hook: a * comma-separated glob list matched against the normalized (forward-slash, * lowercased) path, where a match skips first-touch fact-forcing. The row is * INERT when that plugin is absent — an env var nothing reads. * * The key below is the ONLY occurrence of that third-party name in the source * tree (a test pins the count). Vendor-specific translation belongs in the * adapter layer, next to `HOOK_COMMAND_BY_IDE` and `resolveHookShell` — never * scattered through the installer. And it is emitted into a MACHINE-LOCAL * settings file only (see `resolveHookTargets`): a third-party variable name in * the COMMITTED shared settings would be pushed to every consumer of this repo. */ export declare const EXTERNAL_GATE_EXEMPT_ENV: Readonly>; /** True when every `EXTERNAL_GATE_EXEMPT_ENV` glob is already declared in `settings.env`. */ export declare function hasExternalGateExemptions(settings: Record): boolean; /** * Union every `EXTERNAL_GATE_EXEMPT_ENV` row into `settings.env`. An existing * value is EXTENDED, never replaced, so a user who exempted other trees keeps * them; unrelated `env` keys are untouched. Rows already carrying our glob are * left byte-identical (so a re-run cannot churn the file), and the input object * is returned unchanged when there is nothing to add. Pure. */ export declare function withExternalGateExemptions(settings: Record): Record; /** * Inverse of `withExternalGateExemptions`: remove exactly the globs this repo * added, keeping any the user wrote. The key is deleted once it holds nothing * of ours, and `env` itself is dropped when it becomes empty — so uninstall * leaves no orphan field behind. Pure. */ export declare function withoutExternalGateExemptions(settings: Record): Record; export {};