@cachly-dev/mcp-server 0.10.117 → 0.10.118

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.
Files changed (38) hide show
  1. package/README.md +2 -2
  2. package/dist/src/ambient-cli.d.ts +85 -0
  3. package/dist/src/ambient-cli.js +199 -0
  4. package/dist/src/ambient-cli.js.map +1 -0
  5. package/dist/src/ambient-hooks.d.ts +128 -0
  6. package/dist/src/ambient-hooks.js +269 -0
  7. package/dist/src/ambient-hooks.js.map +1 -0
  8. package/dist/src/ambient-ledger.d.ts +19 -0
  9. package/dist/src/ambient-ledger.js +95 -0
  10. package/dist/src/ambient-ledger.js.map +1 -0
  11. package/dist/src/ambient-recall.d.ts +59 -0
  12. package/dist/src/ambient-recall.js +96 -0
  13. package/dist/src/ambient-recall.js.map +1 -0
  14. package/dist/src/doctor.d.ts +47 -0
  15. package/dist/src/doctor.js +247 -0
  16. package/dist/src/doctor.js.map +1 -0
  17. package/dist/src/first-contact.d.ts +56 -0
  18. package/dist/src/first-contact.js +108 -0
  19. package/dist/src/first-contact.js.map +1 -0
  20. package/dist/src/handlers/brain.d.ts +1 -0
  21. package/dist/src/handlers/brain.js +37 -7
  22. package/dist/src/handlers/brain.js.map +1 -1
  23. package/dist/src/handlers/fedbrain.js +48 -16
  24. package/dist/src/handlers/fedbrain.js.map +1 -1
  25. package/dist/src/handlers/syndicate.js +91 -1
  26. package/dist/src/handlers/syndicate.js.map +1 -1
  27. package/dist/src/handlers/team.d.ts +10 -0
  28. package/dist/src/handlers/team.js +45 -1
  29. package/dist/src/handlers/team.js.map +1 -1
  30. package/dist/src/index.d.ts +8 -5
  31. package/dist/src/index.js +211 -14
  32. package/dist/src/index.js.map +1 -1
  33. package/dist/src/tools.d.ts +5 -0
  34. package/dist/src/tools.js +12 -1
  35. package/dist/src/tools.js.map +1 -1
  36. package/glama.json +1 -1
  37. package/package.json +2 -2
  38. package/server.json +4 -4
package/README.md CHANGED
@@ -63,7 +63,7 @@ hundreds of teams lose the same knowledge the same way, and we built the fix:
63
63
  - **It learns automatically** — from every commit, every fix, every session. No extra calls.
64
64
  - **It arrives pre-briefed** — your AI opens each session already knowing your stack.
65
65
  - **It's shared** — one engineer's solved bug becomes the whole team's reflex.
66
- - **It's provable** — quality-aware recall beats raw text search by **+22.2 % Precision@1**
66
+ - **It's provable** — quality-aware recall beats raw text search by **+33.3 % Precision@1**
67
67
  ([see the benchmark](./BENCH.md)). A claim without a number is marketing; this is the number.
68
68
  - **It's neutral** — speaks [MCP](https://modelcontextprotocol.io), so it works with
69
69
  Claude, Cursor, Copilot, Windsurf, Cline, Zed. Switch models anytime — **your brain stays.**
@@ -177,7 +177,7 @@ using only Claude, alone.** That's not the game we're playing. Here's the honest
177
177
  | Works across **models & tools** | ✅ MCP — Claude, Cursor, Copilot, Windsurf, Zed… | ❌ Claude + Anthropic API only |
178
178
  | **Structured** knowledge | ✅ topic · outcome · severity · causal graph | ⚠️ flat text files, read linearly |
179
179
  | **Causal root-cause** (`causal_trace`) | ✅ problem → chain → proven fix | ❌ |
180
- | **Provable recall quality** | ✅ +22.2 % Precision@1 vs. BM25 ([benchmark](./BENCH.md)) | ❌ no public metric |
180
+ | **Provable recall quality** | ✅ +33.3 % Precision@1 vs. BM25 ([benchmark](./BENCH.md)) | ❌ no public metric |
181
181
  | **Governance** (review, attribution, audit) | ✅ `team_confirm`, roles, audit trail | ❌ |
182
182
  | **Self-hosting / BYOK / VPC** | ✅ data stays in your infra | ❌ Anthropic-hosted |
183
183
  | Survives a **model switch** | ✅ your brain is yours | ❌ memory is gone or fragmented |
@@ -0,0 +1,85 @@
1
+ import { type LessonCandidate, type GateOptions } from './ambient-recall.js';
2
+ /** The subset of the Claude Code hook payload we care about. */
3
+ export interface HookPayload {
4
+ hook_event_name?: string;
5
+ /** Present on UserPromptSubmit — the text the user just typed. */
6
+ prompt?: string;
7
+ /** Present on SessionStart — 'startup' | 'resume' | 'clear' | 'compact'. */
8
+ source?: string;
9
+ /** Present on PreToolUse — the tool about to run and its input. */
10
+ tool_name?: string;
11
+ tool_input?: {
12
+ file_path?: string;
13
+ [k: string]: unknown;
14
+ };
15
+ /** Present on Stop — what the assistant just said (auto-learn signal). */
16
+ last_assistant_message?: string;
17
+ cwd?: string;
18
+ }
19
+ /** Parse the hook payload from stdin. Never throws — returns null on any problem. */
20
+ export declare function parseHookPayload(raw: string): HookPayload | null;
21
+ /**
22
+ * The query to recall on for a payload, or null when recall should be skipped
23
+ * before any brain call:
24
+ * • UserPromptSubmit → the prompt, unless it is trivial (§6.3 guardrail 2).
25
+ * • SessionStart → a fixed briefing query (there is no user prompt yet);
26
+ * skipped on 'compact'/'clear' resumes where a mid-session briefing is noise.
27
+ * • PreToolUse → a file-scoped query for Edit/Write-class tools only.
28
+ * • Stop → never recalls (it learns instead — see stopObservation).
29
+ */
30
+ export declare function recallQueryFor(payload: HookPayload): string | null;
31
+ export interface StopObservation {
32
+ action: string;
33
+ outcome: 'success';
34
+ details: string;
35
+ severity: 'minor';
36
+ }
37
+ /**
38
+ * Derive an auto-learn observation from a Stop payload, or null when the turn
39
+ * carries no clear fix signal. Pure — the caller feeds it to auto_learn_session.
40
+ */
41
+ export declare function stopObservation(payload: HookPayload): StopObservation | null;
42
+ /** Truncate text to fit an estimated token budget (~4 chars/token), on a word edge. */
43
+ export declare function truncateToTokens(text: string, maxTokens: number): string;
44
+ /**
45
+ * Tiny footer that closes the net-token loop (§6.2): the agent self-reports
46
+ * prevented tokens via `ambient-credit` when a recalled lesson changed its path.
47
+ * Kept to one short line — its own token cost is counted as injected.
48
+ */
49
+ export declare const CREDIT_FOOTER = "(If this memory just saved you from a wrong path, record it: `npx @cachly-dev/mcp-server@latest ambient-credit <tokens-saved>`)";
50
+ /**
51
+ * Render the gated lessons into a context block for the agent.
52
+ * • A single already-formatted briefing (multi-line summary — what smart_recall
53
+ * returns) is injected verbatim; it carries its own headings.
54
+ * • Multiple short lessons are rendered as a titled bullet list.
55
+ * `withCreditFooter` appends the self-report line (per-prompt events only —
56
+ * a SessionStart briefing is ambience, not a decision-changing recall).
57
+ */
58
+ export declare function formatContextBlock(lessons: LessonCandidate[], withCreditFooter?: boolean): string;
59
+ /**
60
+ * Build the JSON Claude Code expects from a hook. `additionalContext` is spliced
61
+ * into the model's context for the turn. Empty context → empty string (no output),
62
+ * which Claude Code treats as "hook contributed nothing".
63
+ */
64
+ export declare function buildHookOutput(event: string, additionalContext: string): string;
65
+ export interface AmbientDeps {
66
+ /** Fetch candidate lessons for a query. Should already be scoped to the brain. */
67
+ recall: (query: string, event: string) => Promise<LessonCandidate[]>;
68
+ /** Overrides for the relevance gate. */
69
+ gate?: Partial<GateOptions>;
70
+ /** Hard latency budget for the whole recall step (ms). Default 3000. */
71
+ timeoutMs?: number;
72
+ /**
73
+ * Auto-backoff probe (§6.3 guardrail 3): return true when the recent ledger
74
+ * window is net-negative — recall is then skipped BEFORE any brain call.
75
+ */
76
+ backoff?: () => boolean | Promise<boolean>;
77
+ /** Called with the estimated injected tokens whenever context is emitted. */
78
+ onInject?: (tokens: number, event: string) => void;
79
+ }
80
+ /**
81
+ * The whole ambient-recall CLI flow, as a pure function of (stdin, deps).
82
+ * Returns the string to print on stdout — either the hookSpecificOutput JSON or
83
+ * '' (nothing to inject). Never throws.
84
+ */
85
+ export declare function runAmbient(raw: string, deps: AmbientDeps): Promise<string>;
@@ -0,0 +1,199 @@
1
+ // Ambient Recall (Phase 4) — the CLI core that the hook scripts pipe to.
2
+ //
3
+ // The SessionStart / UserPromptSubmit hooks (ambient-hooks.ts) pipe Claude Code's
4
+ // hook payload JSON on stdin to `npx @cachly-dev/mcp-server ambient-recall`. This
5
+ // module is that command's brain: it parses the payload, runs the §6.3 relevance
6
+ // gate (ambient-recall.ts), and prints the `hookSpecificOutput` JSON Claude Code
7
+ // injects as additionalContext — or nothing at all when the gate says skip.
8
+ //
9
+ // Design constraints (roadmap §6.3):
10
+ // • Pure + dependency-injected (recall is passed in) so it is exhaustively
11
+ // unit-tested without any network.
12
+ // • Graceful: EVERY failure path returns '' (no output) — the caller exits 0 so
13
+ // a crashing hook can never block or corrupt the agent's turn.
14
+ // • Self-limited: recall runs under a hard timeout budget; a slow brain never
15
+ // stalls the user's prompt.
16
+ import { isTrivialPrompt, selectInjectable, estimateTokens, } from './ambient-recall.js';
17
+ /** Parse the hook payload from stdin. Never throws — returns null on any problem. */
18
+ export function parseHookPayload(raw) {
19
+ const trimmed = (raw ?? '').trim();
20
+ if (!trimmed)
21
+ return null;
22
+ try {
23
+ const p = JSON.parse(trimmed);
24
+ if (p && typeof p === 'object')
25
+ return p;
26
+ return null;
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ }
32
+ // PreToolUse fires for every tool; only file-mutating tools carry a wrong-path
33
+ // risk worth a recall (roadmap §6.1: file-open briefing on Edit/Write).
34
+ const FILE_TOOLS = new Set(['Edit', 'Write', 'MultiEdit', 'NotebookEdit']);
35
+ /**
36
+ * The query to recall on for a payload, or null when recall should be skipped
37
+ * before any brain call:
38
+ * • UserPromptSubmit → the prompt, unless it is trivial (§6.3 guardrail 2).
39
+ * • SessionStart → a fixed briefing query (there is no user prompt yet);
40
+ * skipped on 'compact'/'clear' resumes where a mid-session briefing is noise.
41
+ * • PreToolUse → a file-scoped query for Edit/Write-class tools only.
42
+ * • Stop → never recalls (it learns instead — see stopObservation).
43
+ */
44
+ export function recallQueryFor(payload) {
45
+ const event = payload.hook_event_name ?? 'UserPromptSubmit';
46
+ if (event === 'SessionStart') {
47
+ if (payload.source === 'compact' || payload.source === 'clear')
48
+ return null;
49
+ return 'session start: recent lessons, active pitfalls, and known failure modes for this project';
50
+ }
51
+ if (event === 'PreToolUse') {
52
+ if (payload.tool_name && !FILE_TOOLS.has(payload.tool_name))
53
+ return null;
54
+ const filePath = payload.tool_input?.file_path;
55
+ if (typeof filePath !== 'string' || !filePath.trim())
56
+ return null;
57
+ return `known pitfalls, past bugs and lessons for file ${filePath.trim()}`;
58
+ }
59
+ if (event === 'Stop')
60
+ return null;
61
+ const prompt = (payload.prompt ?? '').trim();
62
+ if (!prompt || isTrivialPrompt(prompt))
63
+ return null;
64
+ return prompt;
65
+ }
66
+ // Conservative fix-signal: only turns whose final message clearly reports a
67
+ // resolved problem become auto-lessons. Anything looser floods the brain with
68
+ // junk on every turn-end — the exact noise §6.4 warns about.
69
+ const FIX_SIGNAL_RE = /\b(fixed|resolved|behoben|gefixt|root cause|the bug was|der fehler (lag|war)|ursache (war|gefunden)|now passes|tests? (are|is|sind) (green|grün)|deployed successfully)\b/i;
70
+ /**
71
+ * Derive an auto-learn observation from a Stop payload, or null when the turn
72
+ * carries no clear fix signal. Pure — the caller feeds it to auto_learn_session.
73
+ */
74
+ export function stopObservation(payload) {
75
+ const msg = (payload.last_assistant_message ?? '').trim();
76
+ if (msg.length < 80)
77
+ return null; // too short to describe a real fix
78
+ if (!FIX_SIGNAL_RE.test(msg))
79
+ return null;
80
+ const firstLine = msg.split('\n').find((l) => l.trim().length > 0)?.trim() ?? msg;
81
+ return {
82
+ action: firstLine.slice(0, 200),
83
+ outcome: 'success',
84
+ details: msg.slice(0, 500),
85
+ severity: 'minor',
86
+ };
87
+ }
88
+ /** Truncate text to fit an estimated token budget (~4 chars/token), on a word edge. */
89
+ export function truncateToTokens(text, maxTokens) {
90
+ const t = text.trim();
91
+ if (estimateTokens(t) <= maxTokens)
92
+ return t;
93
+ const maxChars = Math.max(0, maxTokens * 4);
94
+ const cut = t.slice(0, maxChars);
95
+ const lastSpace = cut.lastIndexOf(' ');
96
+ return (lastSpace > maxChars * 0.6 ? cut.slice(0, lastSpace) : cut).trimEnd() + '…';
97
+ }
98
+ /**
99
+ * Tiny footer that closes the net-token loop (§6.2): the agent self-reports
100
+ * prevented tokens via `ambient-credit` when a recalled lesson changed its path.
101
+ * Kept to one short line — its own token cost is counted as injected.
102
+ */
103
+ export const CREDIT_FOOTER = '(If this memory just saved you from a wrong path, record it: `npx @cachly-dev/mcp-server@latest ambient-credit <tokens-saved>`)';
104
+ /**
105
+ * Render the gated lessons into a context block for the agent.
106
+ * • A single already-formatted briefing (multi-line summary — what smart_recall
107
+ * returns) is injected verbatim; it carries its own headings.
108
+ * • Multiple short lessons are rendered as a titled bullet list.
109
+ * `withCreditFooter` appends the self-report line (per-prompt events only —
110
+ * a SessionStart briefing is ambience, not a decision-changing recall).
111
+ */
112
+ export function formatContextBlock(lessons, withCreditFooter = false) {
113
+ if (lessons.length === 0)
114
+ return '';
115
+ const body = lessons.length === 1 && lessons[0].summary.includes('\n')
116
+ ? lessons[0].summary.trim()
117
+ : `🧠 Relevant memory from your cachly brain (auto-recalled):\n` +
118
+ lessons.map((l) => `- ${l.summary.trim()}`).join('\n');
119
+ return withCreditFooter ? `${body}\n${CREDIT_FOOTER}` : body;
120
+ }
121
+ /**
122
+ * Build the JSON Claude Code expects from a hook. `additionalContext` is spliced
123
+ * into the model's context for the turn. Empty context → empty string (no output),
124
+ * which Claude Code treats as "hook contributed nothing".
125
+ */
126
+ export function buildHookOutput(event, additionalContext) {
127
+ if (!additionalContext)
128
+ return '';
129
+ return JSON.stringify({
130
+ hookSpecificOutput: {
131
+ hookEventName: event,
132
+ additionalContext,
133
+ },
134
+ });
135
+ }
136
+ /** Resolve a promise to a fallback if it does not settle within `ms`. */
137
+ async function withTimeout(p, ms, fallback) {
138
+ let timer;
139
+ const timeout = new Promise((res) => {
140
+ timer = setTimeout(() => res(fallback), ms);
141
+ });
142
+ try {
143
+ return await Promise.race([p, timeout]);
144
+ }
145
+ finally {
146
+ if (timer)
147
+ clearTimeout(timer);
148
+ }
149
+ }
150
+ /**
151
+ * The whole ambient-recall CLI flow, as a pure function of (stdin, deps).
152
+ * Returns the string to print on stdout — either the hookSpecificOutput JSON or
153
+ * '' (nothing to inject). Never throws.
154
+ */
155
+ export async function runAmbient(raw, deps) {
156
+ const payload = parseHookPayload(raw);
157
+ if (!payload)
158
+ return '';
159
+ const event = payload.hook_event_name ?? 'UserPromptSubmit';
160
+ const query = recallQueryFor(payload);
161
+ if (query === null)
162
+ return ''; // trivial / non-recallable event → skip before any brain call
163
+ // Auto-backoff (§6.3 guardrail 3): when the recent net balance is red, stop
164
+ // paying — checked after trivial-skip but before any brain call.
165
+ try {
166
+ if (deps.backoff && (await deps.backoff()))
167
+ return '';
168
+ }
169
+ catch {
170
+ // a broken backoff probe must not disable recall
171
+ }
172
+ let candidates = [];
173
+ try {
174
+ candidates = await withTimeout(deps.recall(query, event), deps.timeoutMs ?? 3000, []);
175
+ }
176
+ catch {
177
+ return ''; // recall failed → inject nothing, never block the turn
178
+ }
179
+ if (!Array.isArray(candidates) || candidates.length === 0)
180
+ return '';
181
+ // SessionStart/PreToolUse have no user prompt, so their trivial-skip is
182
+ // meaningless; pass the recall query itself so the gate's non-trivial branch runs.
183
+ const gateInput = event === 'UserPromptSubmit' ? (payload.prompt ?? query) : query;
184
+ const decision = selectInjectable(gateInput, candidates, deps.gate);
185
+ if (!decision.inject)
186
+ return '';
187
+ // Credit footer only where a recall can change a decision mid-flight.
188
+ const withFooter = event === 'UserPromptSubmit' || event === 'PreToolUse';
189
+ const context = formatContextBlock(decision.selected, withFooter);
190
+ const injectedTokens = estimateTokens(context); // includes the footer's own cost
191
+ try {
192
+ deps.onInject?.(injectedTokens, event);
193
+ }
194
+ catch {
195
+ // ledger is telemetry — never block the injection over it
196
+ }
197
+ return buildHookOutput(event, context);
198
+ }
199
+ //# sourceMappingURL=ambient-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ambient-cli.js","sourceRoot":"","sources":["../../src/ambient-cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Bumped whenever a hook script changes so installers can upgrade old hooks.
3
+ * v3: hooks became Node scripts (.mjs) invoked as `node "<path>"` — the
4
+ * cross-platform shape from the Claude Code hooks guide. The v1/v2 POSIX shell
5
+ * scripts silently never ran on native Windows (no /bin/sh); Node is already a
6
+ * hard requirement of this package, so the hook now runs identically on
7
+ * Windows, macOS and Linux.
8
+ */
9
+ export declare const AMBIENT_HOOK_VERSION = "v3";
10
+ /**
11
+ * The CLI subcommand the hooks pipe their payload to. It reads the hook JSON on
12
+ * stdin, runs smart_recall through the relevance gate (ambient-recall.ts), and
13
+ * prints the `hookSpecificOutput` JSON Claude Code injects as additionalContext.
14
+ * The CLI self-limits its latency budget (§6.3 guardrail 4). See ambient-cli.ts.
15
+ */
16
+ export declare const AMBIENT_CLI_SUBCOMMAND = "ambient-recall";
17
+ export type AmbientHookEvent = 'SessionStart' | 'UserPromptSubmit' | 'PreToolUse' | 'Stop';
18
+ export interface AmbientHookOptions {
19
+ instanceId: string;
20
+ /** Optional cky_… / JWT embedded so the CLI can authenticate. */
21
+ apiKey?: string;
22
+ /**
23
+ * Command that resolves the ambient-recall CLI. Defaults to the published
24
+ * package via npx; an installer SHOULD pass a resolved local binary to avoid
25
+ * npx resolution latency on every prompt (UserPromptSubmit runs per-turn).
26
+ */
27
+ cliCommand?: string;
28
+ }
29
+ /**
30
+ * SessionStart hook: emits the session briefing as additionalContext at the
31
+ * start of every session — the automatic replacement for "the agent should call
32
+ * session_start".
33
+ */
34
+ export declare function buildSessionStartHook(opts: AmbientHookOptions): string;
35
+ /**
36
+ * UserPromptSubmit hook — the core of Ambient Recall. Runs before every user
37
+ * message, recalls on the prompt through the relevance gate, and pushes gated
38
+ * hits into context. This is the most-forgotten call, made automatic.
39
+ */
40
+ export declare function buildUserPromptSubmitHook(opts: AmbientHookOptions): string;
41
+ /**
42
+ * PreToolUse hook (Ausbau) — file-open briefing. Fires before Edit/Write-class
43
+ * tools (settings matcher `Edit|Write|MultiEdit`); the CLI recalls file-scoped
44
+ * lessons and injects them as additionalContext — the automatic "prüf mal, ob's
45
+ * zu dieser Datei Lessons gibt" (roadmap §6.1).
46
+ */
47
+ export declare function buildPreToolUseHook(opts: AmbientHookOptions): string;
48
+ /**
49
+ * Stop hook (Ausbau) — auto-learn. After a turn whose final message carries a
50
+ * clear fix signal, the CLI feeds one observation to auto_learn_session — the
51
+ * automatic replacement for the forgotten `learn_from_attempts` call.
52
+ */
53
+ export declare function buildStopHook(opts: AmbientHookOptions): string;
54
+ export interface AmbientHookPaths {
55
+ sessionStart: string;
56
+ userPromptSubmit: string;
57
+ /** Ausbau hooks — optional so MVP-era (v1) callers keep working. */
58
+ preToolUse?: string;
59
+ stop?: string;
60
+ }
61
+ export interface HookCommand {
62
+ type: 'command';
63
+ command: string;
64
+ /** Per-hook timeout in seconds (Claude Code hooks config). */
65
+ timeout?: number;
66
+ }
67
+ export interface HookMatcherGroup {
68
+ /** Tool matcher — only meaningful for PreToolUse/PostToolUse. */
69
+ matcher?: string;
70
+ hooks: HookCommand[];
71
+ }
72
+ /** Matcher for the PreToolUse briefing: only file-mutating tools. */
73
+ export declare const PRE_TOOL_USE_MATCHER = "Edit|Write|MultiEdit|NotebookEdit";
74
+ /**
75
+ * Build the `.claude/settings.json` `hooks` fragment that wires the scripts.
76
+ * The caller merges this into the user's existing settings (never overwrites).
77
+ * Shape matches Claude Code's hooks config: an array of matcher groups, each
78
+ * with a list of `{ type: "command", command, timeout }` entries.
79
+ */
80
+ export declare function buildAmbientSettingsHooks(paths: AmbientHookPaths): Record<string, HookMatcherGroup[]>;
81
+ export interface AmbientInstallResult {
82
+ sessionStartPath: string;
83
+ promptSubmitPath: string;
84
+ preToolUsePath: string;
85
+ stopPath: string;
86
+ scripts: 'written' | 'upgraded' | 'unchanged';
87
+ settings: 'written' | 'merged' | 'unchanged';
88
+ }
89
+ interface HookCommandEntry {
90
+ matcher?: string;
91
+ hooks?: Array<{
92
+ type?: string;
93
+ command?: string;
94
+ timeout?: number;
95
+ }>;
96
+ }
97
+ interface ClaudeSettings {
98
+ hooks?: Record<string, HookCommandEntry[]>;
99
+ [k: string]: unknown;
100
+ }
101
+ /**
102
+ * Merge our hook entries into an existing settings object without disturbing
103
+ * anything else. Idempotent AND upgrade-safe: existing cachly-ambient groups
104
+ * (from any prior version/paths) are replaced by the current fragment rather
105
+ * than accumulated; foreign groups are always preserved. Pure — unit-tested.
106
+ */
107
+ export declare function mergeAmbientSettings(existing: ClaudeSettings, paths: AmbientHookPaths): {
108
+ settings: ClaudeSettings;
109
+ changed: boolean;
110
+ };
111
+ export interface AmbientInstallOptions {
112
+ /**
113
+ * Write `"$CLAUDE_PROJECT_DIR"/.claude/hooks/…` commands into settings.json
114
+ * instead of absolute paths. Use for hooks that are COMMITTED to a repo
115
+ * (dogfooding/team setups): the settings stay valid on every checkout
116
+ * location. Claude Code sets $CLAUDE_PROJECT_DIR when running hooks.
117
+ * NOTE: never pass an apiKey together with portable — committed scripts
118
+ * must not embed credentials; the CLI falls back to the env's CACHLY_JWT.
119
+ */
120
+ portable?: boolean;
121
+ }
122
+ /**
123
+ * Install/upgrade the Ambient Recall hooks in `projectDir`. Best-effort:
124
+ * a filesystem error surfaces as a thrown error only for the top-level caller,
125
+ * which wraps it in try/catch (the git-hook feature is non-critical).
126
+ */
127
+ export declare function installAmbientHooks(projectDir: string, instanceId: string, apiKey?: string, options?: AmbientInstallOptions): Promise<AmbientInstallResult>;
128
+ export {};