auriga-cli 1.25.0 → 1.27.0

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/hooks.d.ts DELETED
@@ -1,236 +0,0 @@
1
- import { type InstallOpts } from "./utils.js";
2
- export interface HookDep {
3
- name: string;
4
- via: "brew";
5
- optional?: boolean;
6
- }
7
- export interface HookSettingsEvent {
8
- event: string;
9
- /** Tool-name / regex filter; mapped onto the container-level
10
- * `matcher` field in settings.json. Absent → every tool fires. */
11
- matcher?: string;
12
- /** Permission-rule-syntax filter — mapped onto the nested action-level
13
- * `if` field in settings.json. Format: `<ToolName>(<substring>)`, e.g.
14
- * `Bash(gh pr create)` — see IF_RE for the exact grammar. Lets the
15
- * Claude Code runtime skip hook dispatch when the tool input doesn't
16
- * match, avoiding a Node subprocess spawn per unrelated call.
17
- * Absent → no registry-level content filter (the hook script runs
18
- * for every invocation that passed `matcher`). */
19
- if?: string;
20
- }
21
- export interface HookDef {
22
- name: string;
23
- description: string;
24
- runtimePlatforms: string[];
25
- settingsEvents: HookSettingsEvent[];
26
- command: string;
27
- files: string[];
28
- preserveFiles?: string[];
29
- deps?: HookDep[];
30
- marker: string;
31
- /**
32
- * Per-hook customization hints rendered in the post-install summary.
33
- * The literal `{hookDir}` is substituted with the hook's resolved
34
- * install directory at print time. Empty / omitted → installer falls
35
- * back to a generic "see <dir>/README.md" pointer.
36
- */
37
- customizeHints?: string[];
38
- /**
39
- * Whether the hook is part of the default-on set. `false` makes the
40
- * hook opt-in: non-interactive `install hooks` with no `--hook` filter
41
- * skips it, and the interactive checkbox leaves it unchecked. Absent
42
- * / `true` → installed by default. Used for hooks with intrusive
43
- * side effects (OS notifications, brew deps, platform constraints)
44
- * that users probably want to pick up consciously.
45
- */
46
- defaultOn?: boolean;
47
- }
48
- export interface HooksConfig {
49
- hooks: HookDef[];
50
- }
51
- export interface SettingsHookAction {
52
- type: "command";
53
- command: string;
54
- _marker?: string;
55
- /** Per-action permission-rule filter (Claude Code ≥ 2026-04 schema).
56
- * Format: `<ToolName>(<substring>)`. Older runtimes ignore unknown
57
- * fields, so emitting this is forward-safe. */
58
- if?: string;
59
- }
60
- export interface SettingsHookGroup {
61
- matcher?: string;
62
- hooks: SettingsHookAction[];
63
- }
64
- export interface SettingsFile {
65
- hooks?: Record<string, SettingsHookGroup[]>;
66
- [key: string]: unknown;
67
- }
68
- /**
69
- * Pure, idempotent settings merge. Deep-clones input, dedupes by two
70
- * checks in priority order:
71
- *
72
- * 1. sentinel `_marker` field — primary key. Survives path drift, lets
73
- * a future uninstall command find our entries unambiguously.
74
- * 2. command-string equality — secondary, catches the case where the
75
- * user (or another tool) already added an equivalent entry by hand
76
- * and never wrote our marker. Without this fallback we would happily
77
- * append a duplicate next to it and the hook would fire twice.
78
- *
79
- * `options.matcher` writes to the container-level `matcher` (tool-name
80
- * filter); `options.ifRule` writes to the action-level `if` (permission-
81
- * rule substring filter, Claude Code ≥ 2026-04). Either or both may be
82
- * absent.
83
- *
84
- * Upgrade path: if an entry with our marker already exists but its
85
- * matcher / if disagrees with the desired values, we update those two
86
- * fields in place (preserving everything else — command, sibling
87
- * actions, the user's other groups — untouched). This is the path for
88
- * a user who installed an older registry version and re-runs the
89
- * installer after hooks.json changed. Pure no-op when the existing
90
- * fields already match.
91
- *
92
- * Inputs are defense-in-depth revalidated here against IF_RE + the
93
- * event-name regex even though registry callers already passed
94
- * loadHooksConfig, so a direct library caller can't write malformed
95
- * values into settings.json by bypassing the registry loader.
96
- *
97
- * Throws if `settings.hooks[event]` exists but is not an array — that
98
- * means the user has hand-edited their settings into a shape we do not
99
- * recognize, and silently replacing it with an empty array would lose
100
- * data. Callers should catch and surface the error to the user.
101
- */
102
- export declare function addHookToSettings(settings: SettingsFile, event: string, command: string, marker: string, options?: {
103
- matcher?: string;
104
- ifRule?: string;
105
- }): {
106
- settings: SettingsFile;
107
- mutated: boolean;
108
- };
109
- /**
110
- * Pure inverse of addHookToSettings: removes every action carrying
111
- * `_marker` from every event in the settings tree. Returns the mutated
112
- * copy and the count of actions removed. If a group becomes empty after
113
- * removal, the whole group is dropped; if an event becomes empty, the
114
- * event key is dropped.
115
- */
116
- export declare function removeHookFromSettings(settings: SettingsFile, marker: string): {
117
- settings: SettingsFile;
118
- removed: number;
119
- };
120
- type Scope = "project-local" | "project" | "user";
121
- /**
122
- * Non-interactive scope map for hooks.
123
- *
124
- * Non-interactive surface only knows about two values — `project` (the
125
- * default) and `user`. `project-local` exists only in the TTY menu; it's
126
- * a per-developer uncommitted scope and carries enough "did you really
127
- * mean this?" surface area that we gate it behind an interactive
128
- * confirmation rather than exposing it as a CLI flag value.
129
- *
130
- * Exported so `tests/hooks.test.ts` can lock the contract down as a
131
- * unit test.
132
- */
133
- export declare function mapNonInteractiveScope(scope: string | undefined): Scope;
134
- export declare function depBinary(dep: HookDep): string;
135
- export declare function loadHooksConfig(packageRoot: string): HooksConfig;
136
- export interface InstallHookResult {
137
- hook: string;
138
- written: number;
139
- preserved: number;
140
- scope: Scope;
141
- hookDir: string;
142
- settingsPath: string;
143
- settingsMutated: boolean;
144
- settingsError?: string;
145
- aborted?: string;
146
- }
147
- /**
148
- * Non-interactive single-hook install. Driven by installHooks (which
149
- * collects user choices via prompts) and by tools/verify-hooks.mjs (which
150
- * exercises the install path end-to-end without prompts).
151
- *
152
- * Failure ordering matters: deps run first (no state changes), then
153
- * settings is read AND parsed (still no state changes), and only after
154
- * parsing succeeds do we touch the filesystem to copy hook files. A
155
- * malformed settings file therefore aborts cleanly and leaves nothing
156
- * behind.
157
- */
158
- export declare function installHook(hook: HookDef, scope: Scope, projectBase: string, packageRoot: string): Promise<InstallHookResult>;
159
- /**
160
- * Scan all 3 scope settings files for a hook's marker, returning every
161
- * scope where the marker is currently present and is NOT the scope the
162
- * caller is about to install into. Used by installHooks to detect
163
- * cross-scope leftovers from a previous install — which would cause the
164
- * hook to fire multiple times if not cleaned up.
165
- *
166
- * Pure-ish: reads files but does not mutate. Silently skips files that
167
- * fail to parse — surfacing those errors is the install path's job.
168
- */
169
- export interface StaleScope {
170
- scope: Scope;
171
- settingsPath: string;
172
- count: number;
173
- }
174
- export declare function findStaleScopes(hook: HookDef, currentScope: Scope, projectBase: string): StaleScope[];
175
- /**
176
- * Remove every action carrying `hook.marker` from the given scope's
177
- * settings file. Atomic write, snapshot-once .bak. Returns the count of
178
- * actions removed (0 if nothing matched or file did not exist).
179
- */
180
- export declare function cleanHookFromScope(hook: HookDef, scope: Scope, projectBase: string): {
181
- removed: number;
182
- settingsPath: string;
183
- };
184
- /**
185
- * Non-interactive selection resolver for hooks.
186
- *
187
- * Diverges from resolvePluginSelection in the no-filter case. Hooks can
188
- * carry intrusive side effects (OS notifications, brew deps), so the
189
- * safe default is NOT "install everything". Three cases:
190
- *
191
- * - undefined (no --hook passed) → default-on set (filter on defaultOn !== false)
192
- * - ["*"] (explicit opt-in to everything) → full compatible set
193
- * - explicit names → exactly those (even if defaultOn is false)
194
- */
195
- export declare function resolveHookSelection(compatible: HookDef[], selected: string[] | undefined): HookDef[];
196
- /**
197
- * Given the full registry and the platform-filtered compatible subset,
198
- * return the names in `selected` that refer to real hooks but aren't
199
- * available on the current platform. Empty result means the selection
200
- * is either fully compatible or references unknown hooks (that case is
201
- * left to the catalog validator — we don't pretend unknown names are
202
- * platform issues).
203
- */
204
- export declare function findIncompatibleExplicit(all: HookDef[], compatible: HookDef[], selected: string[]): string[];
205
- export declare function installHooks(packageRoot: string, opts: InstallOpts): Promise<void>;
206
- /**
207
- * Uninstall a single hook. Defaults to project scope; explicit
208
- * `scope:"user"` cleans `~/.claude/...` instead.
209
- *
210
- * Project scope (default):
211
- * - rm `<cwd>/.claude/hooks/<name>/` directory.
212
- * - Strip the hook's marker from `<cwd>/.claude/settings.json` AND
213
- * `<cwd>/.claude/settings.local.json` (project + project-local share
214
- * the on-disk hook dir, so cleaning both settings files keeps users
215
- * who switched scopes from accumulating dangling registrations).
216
- *
217
- * User scope:
218
- * - rm `~/.claude/hooks/<name>/` directory.
219
- * - Strip the hook's marker from `~/.claude/settings.json`.
220
- * - Project files are NOT touched.
221
- *
222
- * Marker discovery: tries the live registry at `<cwd>` (or the npx
223
- * package root if that fails) so we use the same marker the install path
224
- * stamped in. If the registry can't resolve the hook (renamed / removed
225
- * upstream), we fall back to a `auriga:<name>` convention — every shipped
226
- * hook to date follows it, so the fallback is reliable for the common
227
- * case.
228
- *
229
- * Idempotent: missing hook dir / missing settings / absent marker → no-op.
230
- */
231
- export declare function uninstallHook(name: string, opts: {
232
- cwd: string;
233
- scope?: "project" | "user";
234
- onLog?: (line: string) => void;
235
- }): Promise<void>;
236
- export {};