@cachly-dev/mcp-server 0.10.117 → 0.10.119
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/README.md +9 -5
- package/dist/src/ambient-cli.d.ts +85 -0
- package/dist/src/ambient-cli.js +199 -0
- package/dist/src/ambient-cli.js.map +1 -0
- package/dist/src/ambient-hooks.d.ts +128 -0
- package/dist/src/ambient-hooks.js +269 -0
- package/dist/src/ambient-hooks.js.map +1 -0
- package/dist/src/ambient-ledger.d.ts +19 -0
- package/dist/src/ambient-ledger.js +95 -0
- package/dist/src/ambient-ledger.js.map +1 -0
- package/dist/src/ambient-recall.d.ts +59 -0
- package/dist/src/ambient-recall.js +96 -0
- package/dist/src/ambient-recall.js.map +1 -0
- package/dist/src/doctor.d.ts +47 -0
- package/dist/src/doctor.js +247 -0
- package/dist/src/doctor.js.map +1 -0
- package/dist/src/editor.d.ts +9 -0
- package/dist/src/editor.js +63 -0
- package/dist/src/editor.js.map +1 -0
- package/dist/src/first-contact.d.ts +56 -0
- package/dist/src/first-contact.js +108 -0
- package/dist/src/first-contact.js.map +1 -0
- package/dist/src/funnel-milestones.d.ts +5 -0
- package/dist/src/funnel-milestones.js +48 -0
- package/dist/src/funnel-milestones.js.map +1 -0
- package/dist/src/handlers/brain.d.ts +1 -0
- package/dist/src/handlers/brain.js +45 -8
- package/dist/src/handlers/brain.js.map +1 -1
- package/dist/src/handlers/fedbrain.js +48 -16
- package/dist/src/handlers/fedbrain.js.map +1 -1
- package/dist/src/handlers/syndicate.js +91 -1
- package/dist/src/handlers/syndicate.js.map +1 -1
- package/dist/src/handlers/team.d.ts +10 -0
- package/dist/src/handlers/team.js +45 -1
- package/dist/src/handlers/team.js.map +1 -1
- package/dist/src/index.d.ts +27 -5
- package/dist/src/index.js +293 -28
- package/dist/src/index.js.map +1 -1
- package/dist/src/tools.d.ts +5 -0
- package/dist/src/tools.js +12 -1
- package/dist/src/tools.js.map +1 -1
- package/glama.json +1 -1
- package/package.json +2 -2
- package/server.json +4 -4
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
// `cachly doctor` — one-command setup diagnosis.
|
|
2
|
+
//
|
|
3
|
+
// The activation funnel dies silently: a missing JWT, an unreachable API, a
|
|
4
|
+
// half-installed hook set or a stale v1 shell hook all look identical from the
|
|
5
|
+
// editor ("the brain just doesn't answer"). doctor makes every link of the
|
|
6
|
+
// chain visible in one run: environment → credential → API → instance → hooks
|
|
7
|
+
// → ledger. Pure check functions with injected effects so the whole module is
|
|
8
|
+
// unit-testable; the CLI shell in index.ts only wires and prints.
|
|
9
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
10
|
+
import { resolve } from 'node:path';
|
|
11
|
+
import { AMBIENT_HOOK_VERSION } from './ambient-hooks.js';
|
|
12
|
+
import { netBalance, shouldBackoff } from './ambient-recall.js';
|
|
13
|
+
const MIN_NODE_MAJOR = 18;
|
|
14
|
+
/** Node runtime new enough for fetch/AbortSignal (>= 18). */
|
|
15
|
+
export function checkNodeVersion(version = process.version) {
|
|
16
|
+
const major = Number(version.replace(/^v/, '').split('.')[0]);
|
|
17
|
+
if (Number.isFinite(major) && major >= MIN_NODE_MAJOR) {
|
|
18
|
+
return { name: 'Node.js', status: 'ok', detail: `${version}` };
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
name: 'Node.js',
|
|
22
|
+
status: 'fail',
|
|
23
|
+
detail: `${version} — too old`,
|
|
24
|
+
hint: `cachly needs Node ${MIN_NODE_MAJOR}+ (built-in fetch). Install a current LTS from nodejs.org.`,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Credential present and roughly the right shape (cky_… API key or JWT). */
|
|
28
|
+
export function checkCredential(jwt) {
|
|
29
|
+
if (!jwt) {
|
|
30
|
+
return {
|
|
31
|
+
name: 'Credential',
|
|
32
|
+
status: 'fail',
|
|
33
|
+
detail: 'CACHLY_JWT is not set',
|
|
34
|
+
hint: 'Run `npx @cachly-dev/mcp-server@latest autopilot` (browser sign-in) or export CACHLY_JWT=<cky_…>.',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const looksApiKey = jwt.startsWith('cky_');
|
|
38
|
+
const looksJwt = jwt.split('.').length === 3;
|
|
39
|
+
if (!looksApiKey && !looksJwt) {
|
|
40
|
+
return {
|
|
41
|
+
name: 'Credential',
|
|
42
|
+
status: 'warn',
|
|
43
|
+
detail: 'CACHLY_JWT is set but looks neither like a cky_… key nor a JWT',
|
|
44
|
+
hint: 'Re-copy the key from the dashboard (API Keys) — it may be truncated.',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return { name: 'Credential', status: 'ok', detail: looksApiKey ? 'API key (cky_…)' : 'JWT' };
|
|
48
|
+
}
|
|
49
|
+
/** API reachable at all (public /health — no auth involved). */
|
|
50
|
+
export async function checkApiReachable(apiUrl, fetchFn = fetch) {
|
|
51
|
+
try {
|
|
52
|
+
const res = await fetchFn(`${apiUrl}/health`, { signal: AbortSignal.timeout(5000) });
|
|
53
|
+
if (res.ok)
|
|
54
|
+
return { name: 'API', status: 'ok', detail: `${apiUrl} reachable` };
|
|
55
|
+
return {
|
|
56
|
+
name: 'API',
|
|
57
|
+
status: 'fail',
|
|
58
|
+
detail: `${apiUrl}/health returned HTTP ${res.status}`,
|
|
59
|
+
hint: 'The API answered but is unhealthy — check https://cachly.dev/status or your CACHLY_API_URL.',
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return {
|
|
64
|
+
name: 'API',
|
|
65
|
+
status: 'fail',
|
|
66
|
+
detail: `${apiUrl} not reachable`,
|
|
67
|
+
hint: 'Check your network/proxy. Self-hosted: is CACHLY_API_URL correct and the API running?',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Credential actually accepted by the API (authenticated list call). */
|
|
72
|
+
export async function checkAuthAccepted(apiUrl, jwt, fetchFn = fetch) {
|
|
73
|
+
if (!jwt) {
|
|
74
|
+
return { name: 'Auth', status: 'fail', detail: 'skipped — no credential', hint: 'Fix the Credential check first.' };
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const res = await fetchFn(`${apiUrl}/api/v1/instances`, {
|
|
78
|
+
headers: { Authorization: `Bearer ${jwt}` },
|
|
79
|
+
signal: AbortSignal.timeout(5000),
|
|
80
|
+
});
|
|
81
|
+
if (res.ok)
|
|
82
|
+
return { name: 'Auth', status: 'ok', detail: 'credential accepted' };
|
|
83
|
+
if (res.status === 401 || res.status === 403) {
|
|
84
|
+
return {
|
|
85
|
+
name: 'Auth',
|
|
86
|
+
status: 'fail',
|
|
87
|
+
detail: `API rejected the credential (HTTP ${res.status})`,
|
|
88
|
+
hint: 'The key is expired or revoked. Create a new one in the dashboard, or re-run autopilot.',
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return { name: 'Auth', status: 'warn', detail: `unexpected HTTP ${res.status} from /api/v1/instances` };
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return { name: 'Auth', status: 'warn', detail: 'could not verify (network error)' };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Brain instance configured (env or auto-provisioned default). */
|
|
98
|
+
export function checkInstance(instanceId) {
|
|
99
|
+
if (!instanceId) {
|
|
100
|
+
return {
|
|
101
|
+
name: 'Instance',
|
|
102
|
+
status: 'warn',
|
|
103
|
+
detail: 'no CACHLY_BRAIN_INSTANCE_ID set and no default instance resolved',
|
|
104
|
+
hint: 'Run `npx @cachly-dev/mcp-server@latest init` in your project, or export CACHLY_BRAIN_INSTANCE_ID.',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const uuidish = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(instanceId);
|
|
108
|
+
return uuidish
|
|
109
|
+
? { name: 'Instance', status: 'ok', detail: instanceId.slice(0, 8) + '…' }
|
|
110
|
+
: {
|
|
111
|
+
name: 'Instance',
|
|
112
|
+
status: 'warn',
|
|
113
|
+
detail: `"${instanceId.slice(0, 24)}" does not look like an instance UUID`,
|
|
114
|
+
hint: 'Copy the instance ID from the dashboard (Instances → your brain).',
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/** Read-only look at .claude/ in projectDir — never throws. */
|
|
118
|
+
export function inspectAmbientHooks(projectDir) {
|
|
119
|
+
const out = { settingsExists: false, wiredEvents: [], scriptFiles: [] };
|
|
120
|
+
try {
|
|
121
|
+
const settingsPath = resolve(projectDir, '.claude/settings.json');
|
|
122
|
+
out.settingsExists = existsSync(settingsPath);
|
|
123
|
+
if (out.settingsExists) {
|
|
124
|
+
const parsed = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
125
|
+
for (const [event, groups] of Object.entries(parsed.hooks ?? {})) {
|
|
126
|
+
// Normalise Windows backslash paths so the marker match and the path
|
|
127
|
+
// extraction below work on every platform (node fs accepts / on Windows).
|
|
128
|
+
const norm = (c) => (c ?? '').replace(/\\/g, '/');
|
|
129
|
+
const ours = (groups ?? []).some((g) => (g.hooks ?? []).some((h) => norm(h.command).includes('.claude/hooks/cachly-ambient-')));
|
|
130
|
+
if (ours) {
|
|
131
|
+
out.wiredEvents.push(event);
|
|
132
|
+
for (const g of groups ?? []) {
|
|
133
|
+
for (const h of g.hooks ?? []) {
|
|
134
|
+
const cmd = norm(h.command);
|
|
135
|
+
const m = cmd.match(/["']?([^"']*\.claude\/hooks\/cachly-ambient-[^"']+)["']?/);
|
|
136
|
+
if (!m)
|
|
137
|
+
continue;
|
|
138
|
+
const scriptPath = m[1].replace('$CLAUDE_PROJECT_DIR', projectDir);
|
|
139
|
+
const exists = existsSync(scriptPath);
|
|
140
|
+
let version = 'unknown';
|
|
141
|
+
if (exists) {
|
|
142
|
+
try {
|
|
143
|
+
const body = readFileSync(scriptPath, 'utf-8');
|
|
144
|
+
version = body.includes(AMBIENT_HOOK_VERSION) ? 'current' : 'stale';
|
|
145
|
+
}
|
|
146
|
+
catch { /* keep unknown */ }
|
|
147
|
+
}
|
|
148
|
+
out.scriptFiles.push({ path: scriptPath, exists, version });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch { /* corrupt settings → report as not wired */ }
|
|
156
|
+
return out;
|
|
157
|
+
}
|
|
158
|
+
const AMBIENT_EVENTS = ['SessionStart', 'UserPromptSubmit', 'PreToolUse', 'Stop'];
|
|
159
|
+
/** Ambient hooks wired, scripts present, version current. */
|
|
160
|
+
export function checkHooks(inspection) {
|
|
161
|
+
if (!inspection.settingsExists || inspection.wiredEvents.length === 0) {
|
|
162
|
+
return {
|
|
163
|
+
name: 'Hooks',
|
|
164
|
+
status: 'warn',
|
|
165
|
+
detail: 'Ambient Recall hooks are not installed in this project',
|
|
166
|
+
hint: 'Run `npx @cachly-dev/mcp-server@latest init` here — memory then injects itself (no hooks = pull-only).',
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
const missingEvents = AMBIENT_EVENTS.filter((e) => !inspection.wiredEvents.includes(e));
|
|
170
|
+
const missingFiles = inspection.scriptFiles.filter((f) => !f.exists);
|
|
171
|
+
const stale = inspection.scriptFiles.filter((f) => f.exists && f.version === 'stale');
|
|
172
|
+
if (missingFiles.length > 0) {
|
|
173
|
+
return {
|
|
174
|
+
name: 'Hooks',
|
|
175
|
+
status: 'fail',
|
|
176
|
+
detail: `settings.json wires hooks whose script files are missing (${missingFiles.length})`,
|
|
177
|
+
hint: 'Re-run `npx @cachly-dev/mcp-server@latest init` to rewrite the scripts.',
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
if (stale.length > 0) {
|
|
181
|
+
return {
|
|
182
|
+
name: 'Hooks',
|
|
183
|
+
status: 'warn',
|
|
184
|
+
detail: `installed hooks are an older version (current: ${AMBIENT_HOOK_VERSION}); v1/v2 shell hooks never ran on native Windows`,
|
|
185
|
+
hint: 'Re-run `npx @cachly-dev/mcp-server@latest init` to upgrade in place.',
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
if (missingEvents.length > 0) {
|
|
189
|
+
return {
|
|
190
|
+
name: 'Hooks',
|
|
191
|
+
status: 'warn',
|
|
192
|
+
detail: `partial install — missing events: ${missingEvents.join(', ')}`,
|
|
193
|
+
hint: 'Re-run `npx @cachly-dev/mcp-server@latest init` to add the missing hooks.',
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return { name: 'Hooks', status: 'ok', detail: `all 4 events wired, scripts ${AMBIENT_HOOK_VERSION}` };
|
|
197
|
+
}
|
|
198
|
+
/** Net-token ledger readable + backoff state. */
|
|
199
|
+
export function checkLedger(entries, ledgerPath) {
|
|
200
|
+
const bal = netBalance(entries);
|
|
201
|
+
const backing = shouldBackoff(entries);
|
|
202
|
+
if (entries.length === 0) {
|
|
203
|
+
return {
|
|
204
|
+
name: 'Ledger',
|
|
205
|
+
status: 'ok',
|
|
206
|
+
detail: `empty (${ledgerPath}) — fills up once ambient hooks inject`,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
if (backing) {
|
|
210
|
+
return {
|
|
211
|
+
name: 'Ledger',
|
|
212
|
+
status: 'warn',
|
|
213
|
+
detail: `${entries.length} entries, net ${bal.net} tokens — auto-backoff ACTIVE, injection paused`,
|
|
214
|
+
hint: 'The agent has not reported prevented tokens lately. This self-heals; `ambient-stats` shows details.',
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
name: 'Ledger',
|
|
219
|
+
status: 'ok',
|
|
220
|
+
detail: `${entries.length} entries, net ${bal.net >= 0 ? '+' : ''}${bal.net} tokens`,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
const ICON = { ok: '✅', warn: '⚠️ ', fail: '❌' };
|
|
224
|
+
/** Render the report; deterministic, no colors (CI-friendly). */
|
|
225
|
+
export function renderDoctorReport(checks) {
|
|
226
|
+
const lines = ['', '🩺 cachly doctor', ''];
|
|
227
|
+
for (const c of checks) {
|
|
228
|
+
lines.push(` ${ICON[c.status]} ${c.name.padEnd(11)} ${c.detail}`);
|
|
229
|
+
if (c.hint && c.status !== 'ok')
|
|
230
|
+
lines.push(` ↳ ${c.hint}`);
|
|
231
|
+
}
|
|
232
|
+
const fails = checks.filter((c) => c.status === 'fail').length;
|
|
233
|
+
const warns = checks.filter((c) => c.status === 'warn').length;
|
|
234
|
+
lines.push('');
|
|
235
|
+
lines.push(fails > 0
|
|
236
|
+
? ` ${fails} problem${fails > 1 ? 's' : ''} to fix${warns ? `, ${warns} warning${warns > 1 ? 's' : ''}` : ''}.`
|
|
237
|
+
: warns > 0
|
|
238
|
+
? ` No blockers — ${warns} warning${warns > 1 ? 's' : ''}.`
|
|
239
|
+
: ' Everything looks healthy. Your brain is wired.');
|
|
240
|
+
lines.push('');
|
|
241
|
+
return lines.join('\n');
|
|
242
|
+
}
|
|
243
|
+
/** Exit code contract: 1 only on hard failures (warnings stay 0 for CI use). */
|
|
244
|
+
export function doctorExitCode(checks) {
|
|
245
|
+
return checks.some((c) => c.status === 'fail') ? 1 : 0;
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type Editor = 'claude' | 'cursor' | 'windsurf' | 'copilot' | 'vscode' | 'jetbrains' | 'zed' | 'unknown';
|
|
2
|
+
type Env = Record<string, string | undefined>;
|
|
3
|
+
/**
|
|
4
|
+
* Best-effort host detection. Order matters: forks of VS Code (Cursor,
|
|
5
|
+
* Windsurf) also set TERM_PROGRAM=vscode, so their fork-specific signals are
|
|
6
|
+
* checked BEFORE the generic vscode fallback.
|
|
7
|
+
*/
|
|
8
|
+
export declare function detectEditor(env?: Env): Editor;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Editor / host detection for telemetry attribution.
|
|
2
|
+
//
|
|
3
|
+
// The evening funnel report showed `unknown: 345` vs `claude: 96` / `vscode: 25`
|
|
4
|
+
// — the largest bucket was unattributed. The old detector only checked four
|
|
5
|
+
// env vars (CURSOR_TRACE_ID, WINDSURF_SESSION_ID, GITHUB_COPILOT_WORKSPACE,
|
|
6
|
+
// CLAUDE_CODE_ENTRYPOINT), so every other host — VS Code running the server as
|
|
7
|
+
// a native MCP stdio child, JetBrains, Zed, plain terminals — fell through to
|
|
8
|
+
// "unknown". This widens detection to the stable signals each host actually
|
|
9
|
+
// sets, ordered most-specific first, and stays a pure function of the env so it
|
|
10
|
+
// is exhaustively unit-testable.
|
|
11
|
+
/**
|
|
12
|
+
* Best-effort host detection. Order matters: forks of VS Code (Cursor,
|
|
13
|
+
* Windsurf) also set TERM_PROGRAM=vscode, so their fork-specific signals are
|
|
14
|
+
* checked BEFORE the generic vscode fallback.
|
|
15
|
+
*/
|
|
16
|
+
export function detectEditor(env = process.env) {
|
|
17
|
+
const has = (k) => typeof env[k] === 'string' && env[k] !== '';
|
|
18
|
+
// ── Claude Code ─────────────────────────────────────────────────────────
|
|
19
|
+
// CLAUDE_CODE_ENTRYPOINT is the long-standing signal; CLAUDECODE=1 is set by
|
|
20
|
+
// newer builds. Either identifies the Claude Code host.
|
|
21
|
+
if (has('CLAUDE_CODE_ENTRYPOINT') || env.CLAUDECODE === '1')
|
|
22
|
+
return 'claude';
|
|
23
|
+
// ── VS Code forks (must precede the generic vscode check) ────────────────
|
|
24
|
+
if (has('CURSOR_TRACE_ID') || env.TERM_PROGRAM === 'cursor')
|
|
25
|
+
return 'cursor';
|
|
26
|
+
if (has('WINDSURF_SESSION_ID') || env.TERM_PROGRAM === 'windsurf')
|
|
27
|
+
return 'windsurf';
|
|
28
|
+
// ── GitHub Copilot (agent workspace) ─────────────────────────────────────
|
|
29
|
+
if (has('GITHUB_COPILOT_WORKSPACE') || has('COPILOT_AGENT_ID'))
|
|
30
|
+
return 'copilot';
|
|
31
|
+
// ── Zed ──────────────────────────────────────────────────────────────────
|
|
32
|
+
if (env.TERM_PROGRAM === 'zed' || has('ZED_TERM'))
|
|
33
|
+
return 'zed';
|
|
34
|
+
// ── JetBrains (IntelliJ/PyCharm/GoLand/… integrated terminal) ────────────
|
|
35
|
+
if (env.TERMINAL_EMULATOR === 'JetBrains-JediTerm' ||
|
|
36
|
+
has('__INTELLIJ_COMMAND_HISTFILE__') ||
|
|
37
|
+
has('IDEA_INITIAL_DIRECTORY')) {
|
|
38
|
+
return 'jetbrains';
|
|
39
|
+
}
|
|
40
|
+
// ── VS Code (generic — after the forks above) ────────────────────────────
|
|
41
|
+
// TERM_PROGRAM=vscode covers the integrated terminal; the macOS bundle id
|
|
42
|
+
// and VSCODE_* handshake vars cover the native MCP stdio child (no terminal).
|
|
43
|
+
if (env.TERM_PROGRAM === 'vscode' ||
|
|
44
|
+
has('VSCODE_PID') ||
|
|
45
|
+
has('VSCODE_CWD') ||
|
|
46
|
+
has('VSCODE_IPC_HOOK') ||
|
|
47
|
+
has('VSCODE_IPC_HOOK_CLI') ||
|
|
48
|
+
has('VSCODE_GIT_IPC_HANDLE') ||
|
|
49
|
+
isVSCodeBundle(env.__CFBundleIdentifier)) {
|
|
50
|
+
return 'vscode';
|
|
51
|
+
}
|
|
52
|
+
return 'unknown';
|
|
53
|
+
}
|
|
54
|
+
/** macOS sets __CFBundleIdentifier to the launching app's bundle id. */
|
|
55
|
+
function isVSCodeBundle(bundle) {
|
|
56
|
+
if (!bundle)
|
|
57
|
+
return false;
|
|
58
|
+
return (bundle === 'com.microsoft.VSCode' ||
|
|
59
|
+
bundle === 'com.microsoft.VSCodeInsiders' ||
|
|
60
|
+
bundle === 'com.visualstudio.code.oss' ||
|
|
61
|
+
bundle.startsWith('com.microsoft.VSCode'));
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=editor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor.js","sourceRoot":"","sources":["../../src/editor.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,EAAE;AACF,iFAAiF;AACjF,4EAA4E;AAC5E,4EAA4E;AAC5E,+EAA+E;AAC/E,8EAA8E;AAC9E,4EAA4E;AAC5E,gFAAgF;AAChF,iCAAiC;AAcjC;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,MAAW,OAAO,CAAC,GAAG;IACjD,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;IAEvE,2EAA2E;IAC3E,6EAA6E;IAC7E,wDAAwD;IACxD,IAAI,GAAG,CAAC,wBAAwB,CAAC,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG;QAAE,OAAO,QAAQ,CAAC;IAE7E,4EAA4E;IAC5E,IAAI,GAAG,CAAC,iBAAiB,CAAC,IAAI,GAAG,CAAC,YAAY,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC7E,IAAI,GAAG,CAAC,qBAAqB,CAAC,IAAI,GAAG,CAAC,YAAY,KAAK,UAAU;QAAE,OAAO,UAAU,CAAC;IAErF,4EAA4E;IAC5E,IAAI,GAAG,CAAC,0BAA0B,CAAC,IAAI,GAAG,CAAC,kBAAkB,CAAC;QAAE,OAAO,SAAS,CAAC;IAEjF,4EAA4E;IAC5E,IAAI,GAAG,CAAC,YAAY,KAAK,KAAK,IAAI,GAAG,CAAC,UAAU,CAAC;QAAE,OAAO,KAAK,CAAC;IAEhE,4EAA4E;IAC5E,IACE,GAAG,CAAC,iBAAiB,KAAK,oBAAoB;QAC9C,GAAG,CAAC,+BAA+B,CAAC;QACpC,GAAG,CAAC,wBAAwB,CAAC,EAC7B,CAAC;QACD,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,4EAA4E;IAC5E,0EAA0E;IAC1E,8EAA8E;IAC9E,IACE,GAAG,CAAC,YAAY,KAAK,QAAQ;QAC7B,GAAG,CAAC,YAAY,CAAC;QACjB,GAAG,CAAC,YAAY,CAAC;QACjB,GAAG,CAAC,iBAAiB,CAAC;QACtB,GAAG,CAAC,qBAAqB,CAAC;QAC1B,GAAG,CAAC,uBAAuB,CAAC;QAC5B,cAAc,CAAC,GAAG,CAAC,oBAAoB,CAAC,EACxC,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,wEAAwE;AACxE,SAAS,cAAc,CAAC,MAA0B;IAChD,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1B,OAAO,CACL,MAAM,KAAK,sBAAsB;QACjC,MAAM,KAAK,8BAA8B;QACzC,MAAM,KAAK,2BAA2B;QACtC,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAC1C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* First-contact response builder for `brain_from_git` (roadmap P1-5:
|
|
3
|
+
* "Onboarding-Magie messen & polieren").
|
|
4
|
+
*
|
|
5
|
+
* The very first `brain_from_git` run is a new user's first contact with the
|
|
6
|
+
* Brain, and it decides whether they ever reach the "aha". The response must,
|
|
7
|
+
* in one message:
|
|
8
|
+
* (a) summarize what was just learned (counts by category + severity),
|
|
9
|
+
* (b) prove recall already works — show the top hit of a real search that
|
|
10
|
+
* ran against a topic seeded seconds ago,
|
|
11
|
+
* (c) hand the user 2-3 copy-pasteable next queries, and
|
|
12
|
+
* (d) degrade gracefully for empty/small repos (clear guidance, not empty stats).
|
|
13
|
+
*
|
|
14
|
+
* Pure string building — no I/O, no schema/tool changes — so the onboarding
|
|
15
|
+
* bench and its CI test can assert the exact first-contact response shape.
|
|
16
|
+
*/
|
|
17
|
+
export interface FirstContactProof {
|
|
18
|
+
/** The query that was run internally against the freshly seeded lessons. */
|
|
19
|
+
query: string;
|
|
20
|
+
/** Topic of the top hit. */
|
|
21
|
+
topic: string;
|
|
22
|
+
/** Short snippet of the top hit's lesson. */
|
|
23
|
+
snippet: string;
|
|
24
|
+
}
|
|
25
|
+
export interface FirstContactReportInput {
|
|
26
|
+
repoDir: string;
|
|
27
|
+
revRange: string;
|
|
28
|
+
processed: number;
|
|
29
|
+
ingested: number;
|
|
30
|
+
skipped: number;
|
|
31
|
+
/** Wall-clock time the seeding run took. */
|
|
32
|
+
durationMs: number;
|
|
33
|
+
isIncremental: boolean;
|
|
34
|
+
lastSha?: string;
|
|
35
|
+
instanceId: string;
|
|
36
|
+
/** category → count, sorted descending by the caller. */
|
|
37
|
+
categories: Array<[string, number]>;
|
|
38
|
+
/** severity → count (optional), sorted descending by the caller. */
|
|
39
|
+
severities?: Array<[string, number]>;
|
|
40
|
+
/** Proof-of-value: top hit of an internal search against a just-seeded topic. */
|
|
41
|
+
proof?: FirstContactProof | null;
|
|
42
|
+
/** Copy-pasteable follow-up queries (see suggestRecallQueries). */
|
|
43
|
+
suggestedQueries: string[];
|
|
44
|
+
/** Optional context for the empty case (e.g. "No commits found on branch X"). */
|
|
45
|
+
emptyReason?: string;
|
|
46
|
+
}
|
|
47
|
+
/** Below this many ingested lessons we treat the repo as "small" and add growth guidance. */
|
|
48
|
+
export declare const SMALL_SEED_THRESHOLD = 5;
|
|
49
|
+
export declare function formatSeedDuration(ms: number): string;
|
|
50
|
+
/**
|
|
51
|
+
* Turn seeded topics (`category:some-domain`) into up to `max` copy-pasteable
|
|
52
|
+
* `smart_recall` calls a brand-new user can paste as their literal next message.
|
|
53
|
+
* Deduplicates by domain so three "fix:*" topics don't yield three identical queries.
|
|
54
|
+
*/
|
|
55
|
+
export declare function suggestRecallQueries(topics: string[], instanceId: string, max?: number): string[];
|
|
56
|
+
export declare function buildFirstContactReport(r: FirstContactReportInput): string;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* First-contact response builder for `brain_from_git` (roadmap P1-5:
|
|
3
|
+
* "Onboarding-Magie messen & polieren").
|
|
4
|
+
*
|
|
5
|
+
* The very first `brain_from_git` run is a new user's first contact with the
|
|
6
|
+
* Brain, and it decides whether they ever reach the "aha". The response must,
|
|
7
|
+
* in one message:
|
|
8
|
+
* (a) summarize what was just learned (counts by category + severity),
|
|
9
|
+
* (b) prove recall already works — show the top hit of a real search that
|
|
10
|
+
* ran against a topic seeded seconds ago,
|
|
11
|
+
* (c) hand the user 2-3 copy-pasteable next queries, and
|
|
12
|
+
* (d) degrade gracefully for empty/small repos (clear guidance, not empty stats).
|
|
13
|
+
*
|
|
14
|
+
* Pure string building — no I/O, no schema/tool changes — so the onboarding
|
|
15
|
+
* bench and its CI test can assert the exact first-contact response shape.
|
|
16
|
+
*/
|
|
17
|
+
/** Below this many ingested lessons we treat the repo as "small" and add growth guidance. */
|
|
18
|
+
export const SMALL_SEED_THRESHOLD = 5;
|
|
19
|
+
export function formatSeedDuration(ms) {
|
|
20
|
+
if (ms < 1000)
|
|
21
|
+
return `${Math.max(ms, 1)}ms`;
|
|
22
|
+
if (ms < 60_000)
|
|
23
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
24
|
+
return `${Math.floor(ms / 60_000)}m ${Math.round((ms % 60_000) / 1000)}s`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Turn seeded topics (`category:some-domain`) into up to `max` copy-pasteable
|
|
28
|
+
* `smart_recall` calls a brand-new user can paste as their literal next message.
|
|
29
|
+
* Deduplicates by domain so three "fix:*" topics don't yield three identical queries.
|
|
30
|
+
*/
|
|
31
|
+
export function suggestRecallQueries(topics, instanceId, max = 3) {
|
|
32
|
+
const seen = new Set();
|
|
33
|
+
const out = [];
|
|
34
|
+
for (const topic of topics) {
|
|
35
|
+
const domain = (topic.includes(':') ? topic.slice(topic.indexOf(':') + 1) : topic)
|
|
36
|
+
.replace(/[-_]+/g, ' ')
|
|
37
|
+
.trim();
|
|
38
|
+
if (!domain || seen.has(domain))
|
|
39
|
+
continue;
|
|
40
|
+
seen.add(domain);
|
|
41
|
+
out.push(`smart_recall(instance_id="${instanceId}", query="${domain}")`);
|
|
42
|
+
if (out.length >= max)
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
/** Steps that take an empty/tiny Brain to a first recall hit in under a minute. */
|
|
48
|
+
function growthSteps(instanceId) {
|
|
49
|
+
return [
|
|
50
|
+
` 1. \`brain_seed_starter(instance_id="${instanceId}")\` — load 16 curated engineering lessons instantly`,
|
|
51
|
+
` 2. \`smart_recall(instance_id="${instanceId}", query="docker build slow")\` — see your first recall hit`,
|
|
52
|
+
` 3. \`learn_from_attempts(...)\` after your next fix — the Brain grows with every lesson from here on`,
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
export function buildFirstContactReport(r) {
|
|
56
|
+
const lines = [];
|
|
57
|
+
lines.push(`🔁 **brain_from_git: ${r.repoDir}**`);
|
|
58
|
+
if (r.isIncremental && r.lastSha) {
|
|
59
|
+
lines.push(`🔄 Incremental mode — only new commits since \`${r.lastSha.slice(0, 8)}\` were processed`);
|
|
60
|
+
}
|
|
61
|
+
lines.push('');
|
|
62
|
+
lines.push(`📂 Branch: \`${r.revRange}\` | Processed: **${r.processed}** commits | ` +
|
|
63
|
+
`Ingested: **${r.ingested}** lessons | Skipped: ${r.skipped} | ` +
|
|
64
|
+
`⏱️ Seeding took ${formatSeedDuration(r.durationMs)}`);
|
|
65
|
+
lines.push('');
|
|
66
|
+
// ── Empty repo / nothing ingested — guidance instead of empty stats ─────────
|
|
67
|
+
if (r.ingested === 0) {
|
|
68
|
+
if (r.emptyReason)
|
|
69
|
+
lines.push(`⚠️ ${r.emptyReason}`);
|
|
70
|
+
lines.push(`🌱 **Nothing to learn from this git history yet** — normal for fresh, shallow, or squash-only repos.`);
|
|
71
|
+
lines.push(`Get to your first recall in under a minute:`);
|
|
72
|
+
lines.push(...growthSteps(r.instanceId));
|
|
73
|
+
return lines.join('\n');
|
|
74
|
+
}
|
|
75
|
+
// ── (a) Seeded summary — what the Brain just learned ────────────────────────
|
|
76
|
+
lines.push(`**What your Brain just learned (by category):**`);
|
|
77
|
+
lines.push(r.categories.map(([k, v]) => ` • **${k}** (${v})`).join('\n'));
|
|
78
|
+
if (r.severities && r.severities.length > 0) {
|
|
79
|
+
lines.push(`**By severity:** ${r.severities.map(([k, v]) => `${k} ${v}`).join(' · ')}`);
|
|
80
|
+
}
|
|
81
|
+
// ── (b) Proof of value — a real recall against a just-seeded topic ──────────
|
|
82
|
+
if (r.proof) {
|
|
83
|
+
lines.push('');
|
|
84
|
+
lines.push(`🎯 **Proof — your first recall already works.** We just searched your fresh Brain for:`);
|
|
85
|
+
lines.push(` "${r.proof.query}"`);
|
|
86
|
+
lines.push(` → Top hit: **${r.proof.topic}** — ${r.proof.snippet}`);
|
|
87
|
+
}
|
|
88
|
+
// ── (c) Suggested next queries — copy-pasteable ──────────────────────────────
|
|
89
|
+
if (r.suggestedQueries.length > 0) {
|
|
90
|
+
lines.push('');
|
|
91
|
+
lines.push(`**Try these next (copy-paste):**`);
|
|
92
|
+
for (const q of r.suggestedQueries)
|
|
93
|
+
lines.push(` • \`${q}\``);
|
|
94
|
+
}
|
|
95
|
+
// ── (d) Small-repo guidance ──────────────────────────────────────────────────
|
|
96
|
+
if (r.ingested < SMALL_SEED_THRESHOLD) {
|
|
97
|
+
lines.push('');
|
|
98
|
+
lines.push(`🌱 **Small git history — only ${r.ingested} lesson${r.ingested === 1 ? '' : 's'} so far.** Top it up:`);
|
|
99
|
+
lines.push(...growthSteps(r.instanceId));
|
|
100
|
+
}
|
|
101
|
+
lines.push('');
|
|
102
|
+
lines.push(`💡 New lessons are stored with confidence 0.55 (auto-inferred).`);
|
|
103
|
+
lines.push(`💡 As you confirm them via \`learn_from_attempts\`, confidence rises automatically.`);
|
|
104
|
+
lines.push(`🔍 Explore: \`brain_search(query="fix")\` | \`ckg_inspect(concept="deploy")\``);
|
|
105
|
+
lines.push(`💾 Next run will automatically continue from the latest commit (incremental mode).`);
|
|
106
|
+
return lines.join('\n');
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=first-contact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"first-contact.js","sourceRoot":"","sources":["../../src/first-contact.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function defaultMilestonePath(): string;
|
|
2
|
+
/** True when this milestone was already sent from this machine. */
|
|
3
|
+
export declare function milestoneSent(name: string, path?: string): boolean;
|
|
4
|
+
/** Record a milestone (idempotent). Never throws. */
|
|
5
|
+
export declare function markMilestoneSent(name: string, path?: string): void;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Persistent once-per-machine funnel milestones.
|
|
2
|
+
//
|
|
3
|
+
// Milestones like first_call_success were guarded by an in-memory flag only —
|
|
4
|
+
// but ambient hooks spawn a FRESH server process per prompt, so every hook
|
|
5
|
+
// invocation re-sent "first call" and the backend saw 66 "first" calls from
|
|
6
|
+
// one user (and Telegram pinged the founder on every one). Persisting the
|
|
7
|
+
// marker in ~/.cachly/ makes "first" mean first-on-this-machine across
|
|
8
|
+
// process restarts. Best-effort: any filesystem problem degrades to the old
|
|
9
|
+
// in-memory behaviour (the backend now dedupes too), never throws.
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import { homedir } from 'node:os';
|
|
12
|
+
import { dirname, join } from 'node:path';
|
|
13
|
+
export function defaultMilestonePath() {
|
|
14
|
+
return join(homedir(), '.cachly', 'funnel-milestones.json');
|
|
15
|
+
}
|
|
16
|
+
function readAll(path) {
|
|
17
|
+
try {
|
|
18
|
+
if (!existsSync(path))
|
|
19
|
+
return {};
|
|
20
|
+
const parsed = JSON.parse(readFileSync(path, 'utf-8'));
|
|
21
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
22
|
+
return parsed;
|
|
23
|
+
}
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return {}; // corrupt file → treat as empty, next mark rewrites it
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** True when this milestone was already sent from this machine. */
|
|
31
|
+
export function milestoneSent(name, path = defaultMilestonePath()) {
|
|
32
|
+
return name in readAll(path);
|
|
33
|
+
}
|
|
34
|
+
/** Record a milestone (idempotent). Never throws. */
|
|
35
|
+
export function markMilestoneSent(name, path = defaultMilestonePath()) {
|
|
36
|
+
try {
|
|
37
|
+
const all = readAll(path);
|
|
38
|
+
if (name in all)
|
|
39
|
+
return;
|
|
40
|
+
all[name] = new Date().toISOString();
|
|
41
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
42
|
+
writeFileSync(path, JSON.stringify(all, null, 2) + '\n', 'utf-8');
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
/* best-effort — the server-side dedup is the safety net */
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=funnel-milestones.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"funnel-milestones.js","sourceRoot":"","sources":["../../src/funnel-milestones.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,4EAA4E;AAC5E,0EAA0E;AAC1E,uEAAuE;AACvE,4EAA4E;AAC5E,mEAAmE;AAEnE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,UAAU,oBAAoB;IAClC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,wBAAwB,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,OAAO,CAAC,IAAY;IAC3B,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAY,CAAC;QAClE,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACnE,OAAO,MAAgC,CAAC;QAC1C,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC,CAAC,uDAAuD;IACpE,CAAC;AACH,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,OAAe,oBAAoB,EAAE;IAC/E,OAAO,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,OAAe,oBAAoB,EAAE;IACnF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,IAAI,IAAI,GAAG;YAAE,OAAO;QACxB,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,2DAA2D;IAC7D,CAAC;AACH,CAAC"}
|
|
@@ -13,6 +13,7 @@ export interface Instance {
|
|
|
13
13
|
memory_mb: number;
|
|
14
14
|
encryption_at_rest: boolean;
|
|
15
15
|
created_at: string;
|
|
16
|
+
org_id?: string;
|
|
16
17
|
}
|
|
17
18
|
type GetConnection = (instanceId: string) => Promise<Redis>;
|
|
18
19
|
type ApiFetch = <T>(path: string, options?: RequestInit) => Promise<T>;
|