@rely-ai/caliber 1.31.0-dev.1774741028 → 1.31.0-dev.1774815271

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 (2) hide show
  1. package/dist/bin.js +1170 -792
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -9,155 +9,6 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
- // src/llm/config.ts
13
- var config_exports = {};
14
- __export(config_exports, {
15
- DEFAULT_FAST_MODELS: () => DEFAULT_FAST_MODELS,
16
- DEFAULT_MODELS: () => DEFAULT_MODELS,
17
- MODEL_CONTEXT_WINDOWS: () => MODEL_CONTEXT_WINDOWS,
18
- getConfigFilePath: () => getConfigFilePath,
19
- getDisplayModel: () => getDisplayModel,
20
- getFastModel: () => getFastModel,
21
- getMaxPromptTokens: () => getMaxPromptTokens,
22
- loadConfig: () => loadConfig,
23
- readConfigFile: () => readConfigFile,
24
- resolveFromEnv: () => resolveFromEnv,
25
- writeConfigFile: () => writeConfigFile
26
- });
27
- import fs4 from "fs";
28
- import path5 from "path";
29
- import os2 from "os";
30
- function getMaxPromptTokens() {
31
- const config = loadConfig();
32
- const model = process.env.CALIBER_MODEL || config?.model;
33
- const contextWindow = model ? MODEL_CONTEXT_WINDOWS[model] ?? DEFAULT_CONTEXT_WINDOW : DEFAULT_CONTEXT_WINDOW;
34
- const budget = Math.floor(contextWindow * INPUT_BUDGET_FRACTION);
35
- return Math.max(MIN_PROMPT_TOKENS, Math.min(budget, MAX_PROMPT_TOKENS_CAP));
36
- }
37
- function loadConfig() {
38
- const envConfig = resolveFromEnv();
39
- if (envConfig) return envConfig;
40
- return readConfigFile();
41
- }
42
- function resolveFromEnv() {
43
- if (process.env.ANTHROPIC_API_KEY) {
44
- return {
45
- provider: "anthropic",
46
- apiKey: process.env.ANTHROPIC_API_KEY,
47
- model: process.env.CALIBER_MODEL || DEFAULT_MODELS.anthropic
48
- };
49
- }
50
- if (process.env.VERTEX_PROJECT_ID || process.env.GCP_PROJECT_ID) {
51
- return {
52
- provider: "vertex",
53
- model: process.env.CALIBER_MODEL || DEFAULT_MODELS.vertex,
54
- vertexProjectId: process.env.VERTEX_PROJECT_ID || process.env.GCP_PROJECT_ID,
55
- vertexRegion: process.env.VERTEX_REGION || process.env.GCP_REGION || "us-east5",
56
- vertexCredentials: process.env.VERTEX_SA_CREDENTIALS || process.env.GOOGLE_APPLICATION_CREDENTIALS
57
- };
58
- }
59
- if (process.env.OPENAI_API_KEY) {
60
- return {
61
- provider: "openai",
62
- apiKey: process.env.OPENAI_API_KEY,
63
- model: process.env.CALIBER_MODEL || DEFAULT_MODELS.openai,
64
- baseUrl: process.env.OPENAI_BASE_URL
65
- };
66
- }
67
- if (process.env.CALIBER_USE_CURSOR_SEAT === "1" || process.env.CALIBER_USE_CURSOR_SEAT === "true") {
68
- return {
69
- provider: "cursor",
70
- model: process.env.CALIBER_MODEL || DEFAULT_MODELS.cursor
71
- };
72
- }
73
- if (process.env.CALIBER_USE_CLAUDE_CLI === "1" || process.env.CALIBER_USE_CLAUDE_CLI === "true") {
74
- return {
75
- provider: "claude-cli",
76
- model: process.env.CALIBER_MODEL || DEFAULT_MODELS["claude-cli"]
77
- };
78
- }
79
- return null;
80
- }
81
- function readConfigFile() {
82
- try {
83
- if (!fs4.existsSync(CONFIG_FILE)) return null;
84
- const raw = fs4.readFileSync(CONFIG_FILE, "utf-8");
85
- const parsed = JSON.parse(raw);
86
- if (!parsed.provider || !["anthropic", "vertex", "openai", "cursor", "claude-cli"].includes(parsed.provider)) {
87
- return null;
88
- }
89
- return parsed;
90
- } catch {
91
- return null;
92
- }
93
- }
94
- function writeConfigFile(config) {
95
- if (!fs4.existsSync(CONFIG_DIR)) {
96
- fs4.mkdirSync(CONFIG_DIR, { recursive: true });
97
- }
98
- const sanitized = { ...config };
99
- if (sanitized.apiKey) {
100
- sanitized.apiKey = sanitized.apiKey.trim();
101
- }
102
- fs4.writeFileSync(CONFIG_FILE, JSON.stringify(sanitized, null, 2) + "\n", { mode: 384 });
103
- }
104
- function getConfigFilePath() {
105
- return CONFIG_FILE;
106
- }
107
- function getDisplayModel(config) {
108
- if (config.model === "default" && config.provider === "claude-cli") {
109
- return process.env.ANTHROPIC_MODEL || "default (inherited from Claude Code)";
110
- }
111
- return config.model;
112
- }
113
- function getFastModel() {
114
- if (process.env.CALIBER_FAST_MODEL) return process.env.CALIBER_FAST_MODEL;
115
- const config = loadConfig();
116
- const provider = config?.provider;
117
- if (process.env.ANTHROPIC_SMALL_FAST_MODEL && (!provider || provider === "anthropic" || provider === "vertex" || provider === "claude-cli")) {
118
- return process.env.ANTHROPIC_SMALL_FAST_MODEL;
119
- }
120
- if (config?.fastModel) return config.fastModel;
121
- if (provider) return DEFAULT_FAST_MODELS[provider];
122
- return void 0;
123
- }
124
- var CONFIG_DIR, CONFIG_FILE, DEFAULT_MODELS, MODEL_CONTEXT_WINDOWS, DEFAULT_CONTEXT_WINDOW, INPUT_BUDGET_FRACTION, MAX_PROMPT_TOKENS_CAP, MIN_PROMPT_TOKENS, DEFAULT_FAST_MODELS;
125
- var init_config = __esm({
126
- "src/llm/config.ts"() {
127
- "use strict";
128
- CONFIG_DIR = path5.join(os2.homedir(), ".caliber");
129
- CONFIG_FILE = path5.join(CONFIG_DIR, "config.json");
130
- DEFAULT_MODELS = {
131
- anthropic: "claude-sonnet-4-6",
132
- vertex: "claude-sonnet-4-6",
133
- openai: "gpt-4.1",
134
- cursor: "sonnet-4.6",
135
- "claude-cli": "default"
136
- };
137
- MODEL_CONTEXT_WINDOWS = {
138
- "claude-sonnet-4-6": 2e5,
139
- "claude-opus-4-6": 2e5,
140
- "claude-haiku-4-5-20251001": 2e5,
141
- "claude-sonnet-4-5-20250514": 2e5,
142
- "gpt-4.1": 1e6,
143
- "gpt-4.1-mini": 1e6,
144
- "gpt-4o": 128e3,
145
- "gpt-4o-mini": 128e3,
146
- "sonnet-4.6": 2e5
147
- };
148
- DEFAULT_CONTEXT_WINDOW = 2e5;
149
- INPUT_BUDGET_FRACTION = 0.6;
150
- MAX_PROMPT_TOKENS_CAP = 3e5;
151
- MIN_PROMPT_TOKENS = 3e4;
152
- DEFAULT_FAST_MODELS = {
153
- anthropic: "claude-haiku-4-5-20251001",
154
- vertex: "claude-haiku-4-5-20251001",
155
- openai: "gpt-4.1-mini",
156
- cursor: "gpt-5.3-codex-fast"
157
- };
158
- }
159
- });
160
-
161
12
  // src/lib/resolve-caliber.ts
162
13
  var resolve_caliber_exports = {};
163
14
  __export(resolve_caliber_exports, {
@@ -166,8 +17,8 @@ __export(resolve_caliber_exports, {
166
17
  resetResolvedCaliber: () => resetResolvedCaliber,
167
18
  resolveCaliber: () => resolveCaliber
168
19
  });
169
- import fs6 from "fs";
170
- import { execSync as execSync4 } from "child_process";
20
+ import fs2 from "fs";
21
+ import { execSync as execSync3 } from "child_process";
171
22
  function resolveCaliber() {
172
23
  if (_resolved) return _resolved;
173
24
  const isNpx = process.argv[1]?.includes("_npx") || process.env.npm_execpath?.includes("npx");
@@ -177,7 +28,7 @@ function resolveCaliber() {
177
28
  }
178
29
  try {
179
30
  const whichCmd = process.platform === "win32" ? "where caliber" : "which caliber";
180
- execSync4(whichCmd, {
31
+ execSync3(whichCmd, {
181
32
  encoding: "utf-8",
182
33
  stdio: ["pipe", "pipe", "pipe"]
183
34
  });
@@ -186,7 +37,7 @@ function resolveCaliber() {
186
37
  } catch {
187
38
  }
188
39
  const binPath = process.argv[1];
189
- if (binPath && /caliber/.test(binPath) && fs6.existsSync(binPath)) {
40
+ if (binPath && /caliber/.test(binPath) && fs2.existsSync(binPath)) {
190
41
  _resolved = binPath;
191
42
  return _resolved;
192
43
  }
@@ -214,214 +65,65 @@ var init_resolve_caliber = __esm({
214
65
  }
215
66
  });
216
67
 
217
- // src/llm/types.ts
218
- var types_exports = {};
219
- __export(types_exports, {
220
- isSeatBased: () => isSeatBased
221
- });
222
- function isSeatBased(provider) {
223
- return SEAT_BASED_PROVIDERS.has(provider);
224
- }
225
- var SEAT_BASED_PROVIDERS;
226
- var init_types = __esm({
227
- "src/llm/types.ts"() {
228
- "use strict";
229
- SEAT_BASED_PROVIDERS = /* @__PURE__ */ new Set(["cursor", "claude-cli"]);
230
- }
68
+ // src/lib/builtin-skills.ts
69
+ var builtin_skills_exports = {};
70
+ __export(builtin_skills_exports, {
71
+ BUILTIN_SKILLS: () => BUILTIN_SKILLS,
72
+ BUILTIN_SKILL_NAMES: () => BUILTIN_SKILL_NAMES,
73
+ FIND_SKILLS_SKILL: () => FIND_SKILLS_SKILL,
74
+ PLATFORM_CONFIGS: () => PLATFORM_CONFIGS,
75
+ SAVE_LEARNING_SKILL: () => SAVE_LEARNING_SKILL,
76
+ SETUP_CALIBER_SKILL: () => SETUP_CALIBER_SKILL,
77
+ buildSkillContent: () => buildSkillContent,
78
+ ensureBuiltinSkills: () => ensureBuiltinSkills
231
79
  });
80
+ import fs3 from "fs";
81
+ import path3 from "path";
82
+ function buildSkillContent(skill) {
83
+ const frontmatter = `---
84
+ name: ${skill.name}
85
+ description: ${skill.description}
86
+ ---
232
87
 
233
- // src/writers/pre-commit-block.ts
234
- var pre_commit_block_exports = {};
235
- __export(pre_commit_block_exports, {
236
- appendLearningsBlock: () => appendLearningsBlock,
237
- appendPreCommitBlock: () => appendPreCommitBlock,
238
- appendSyncBlock: () => appendSyncBlock,
239
- getCursorLearningsRule: () => getCursorLearningsRule,
240
- getCursorPreCommitRule: () => getCursorPreCommitRule,
241
- getCursorSyncRule: () => getCursorSyncRule,
242
- hasLearningsBlock: () => hasLearningsBlock,
243
- hasPreCommitBlock: () => hasPreCommitBlock,
244
- hasSyncBlock: () => hasSyncBlock
245
- });
246
- function getPreCommitBlock() {
88
+ `;
89
+ return frontmatter + skill.content;
90
+ }
91
+ function getFindSkillsContent() {
247
92
  const bin = resolveCaliber();
248
- return `${BLOCK_START}
249
- ## Before Committing
93
+ return `# Find Skills
250
94
 
251
- **IMPORTANT:** Before every git commit, you MUST ensure Caliber syncs agent configs with the latest code changes.
95
+ Search the public skill registry for community-contributed skills
96
+ relevant to the user's current task and install them into this project.
252
97
 
253
- First, check if the pre-commit hook is already installed:
254
- \`\`\`bash
255
- grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "hook-active" || echo "no-hook"
256
- \`\`\`
98
+ ## Instructions
257
99
 
258
- - If **hook-active**: the hook handles sync automatically \u2014 just commit normally. Tell the user: "Caliber will sync your agent configs automatically via the pre-commit hook."
259
- - If **no-hook**: run Caliber manually before committing:
260
- 1. Tell the user: "Caliber: Syncing agent configs with your latest changes..."
261
- 2. Run: \`${bin} refresh && git add ${MANAGED_DOC_PATHS} 2>/dev/null\`
262
- 3. After it completes, briefly tell the user what Caliber updated. Then proceed with the commit.
100
+ 1. Identify the key technologies, frameworks, or task types from the
101
+ user's request that might have community skills available
102
+ 2. Ask the user: "Would you like me to search for community skills
103
+ for [identified technologies]?"
104
+ 3. If the user agrees, run:
105
+ \`\`\`bash
106
+ ${bin} skills --query "<relevant terms>"
107
+ \`\`\`
108
+ This outputs the top 5 matching skills with scores and descriptions.
109
+ 4. Present the results to the user and ask which ones to install
110
+ 5. Install the selected skills:
111
+ \`\`\`bash
112
+ ${bin} skills --install <slug1>,<slug2>
113
+ \`\`\`
114
+ 6. Read the installed SKILL.md files to load them into your current
115
+ context so you can use them immediately in this session
116
+ 7. Summarize what was installed and continue with the user's task
263
117
 
264
- If \`${bin}\` is not found, tell the user: "This project uses Caliber for agent config sync. Run /setup-caliber to get set up."
265
- ${BLOCK_END}`;
266
- }
267
- function getCursorRuleContent() {
268
- const bin = resolveCaliber();
269
- return `---
270
- description: Run Caliber sync before git commits to keep agent configs up to date
271
- alwaysApply: true
272
- ---
273
- Before every git commit, check if the Caliber pre-commit hook is installed:
274
- Run: \`grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "hook-active" || echo "no-hook"\`
118
+ ## Examples
275
119
 
276
- If hook-active: just commit normally \u2014 Caliber syncs automatically via the hook. Tell the user: "Caliber will sync your agent configs automatically."
277
- If no-hook: run \`${bin} refresh && git add ${MANAGED_DOC_PATHS} 2>/dev/null\` before committing, and tell the user what was updated.
278
-
279
- If \`${bin}\` is not found, read the setup-caliber skill from .cursor/skills/setup-caliber/SKILL.md and follow its instructions to install Caliber.
280
- `;
281
- }
282
- function hasPreCommitBlock(content) {
283
- return content.includes(BLOCK_START);
284
- }
285
- function appendPreCommitBlock(content) {
286
- if (hasPreCommitBlock(content)) return content;
287
- const trimmed = content.trimEnd();
288
- return trimmed + "\n\n" + getPreCommitBlock() + "\n";
289
- }
290
- function getCursorPreCommitRule() {
291
- return { filename: CURSOR_RULE_FILENAME, content: getCursorRuleContent() };
292
- }
293
- function hasLearningsBlock(content) {
294
- return content.includes(LEARNINGS_BLOCK_START);
295
- }
296
- function appendLearningsBlock(content) {
297
- if (hasLearningsBlock(content)) return content;
298
- const trimmed = content.trimEnd();
299
- return trimmed + "\n\n" + LEARNINGS_BLOCK + "\n";
300
- }
301
- function getCursorLearningsRule() {
302
- return { filename: CURSOR_LEARNINGS_FILENAME, content: CURSOR_LEARNINGS_CONTENT };
303
- }
304
- function getSyncBlock() {
305
- const bin = resolveCaliber();
306
- return `${SYNC_BLOCK_START}
307
- ## Context Sync
308
-
309
- This project uses [Caliber](https://github.com/caliber-ai-org/ai-setup) to keep AI agent configs in sync across Claude Code, Cursor, Copilot, and Codex.
310
- Configs update automatically before each commit via \`${bin} refresh\`.
311
- If the pre-commit hook is not set up, run \`${bin} hooks --install\` to enable automatic sync.
312
- ${SYNC_BLOCK_END}`;
313
- }
314
- function hasSyncBlock(content) {
315
- return content.includes(SYNC_BLOCK_START);
316
- }
317
- function appendSyncBlock(content) {
318
- if (hasSyncBlock(content)) return content;
319
- const trimmed = content.trimEnd();
320
- return trimmed + "\n\n" + getSyncBlock() + "\n";
321
- }
322
- function getCursorSyncContent() {
323
- const bin = resolveCaliber();
324
- return `---
325
- description: This project uses Caliber for automatic AI agent context sync
326
- alwaysApply: true
327
- ---
328
- This project uses Caliber to keep AI agent configs (CLAUDE.md, Cursor rules, Copilot instructions)
329
- in sync with the codebase automatically. Before each commit, run \`${bin} refresh\` to sync configs.
330
- If \`${bin}\` is not found, read the setup-caliber skill from .cursor/skills/setup-caliber/SKILL.md and follow its instructions to install Caliber.
331
- `;
332
- }
333
- function getCursorSyncRule() {
334
- return { filename: CURSOR_SYNC_FILENAME, content: getCursorSyncContent() };
335
- }
336
- var BLOCK_START, BLOCK_END, MANAGED_DOC_PATHS, CURSOR_RULE_FILENAME, LEARNINGS_BLOCK_START, LEARNINGS_BLOCK_END, LEARNINGS_BLOCK, CURSOR_LEARNINGS_FILENAME, CURSOR_LEARNINGS_CONTENT, SYNC_BLOCK_START, SYNC_BLOCK_END, CURSOR_SYNC_FILENAME;
337
- var init_pre_commit_block = __esm({
338
- "src/writers/pre-commit-block.ts"() {
339
- "use strict";
340
- init_resolve_caliber();
341
- BLOCK_START = "<!-- caliber:managed:pre-commit -->";
342
- BLOCK_END = "<!-- /caliber:managed:pre-commit -->";
343
- MANAGED_DOC_PATHS = "CLAUDE.md .claude/ .cursor/ .cursorrules .github/copilot-instructions.md .github/instructions/ AGENTS.md CALIBER_LEARNINGS.md";
344
- CURSOR_RULE_FILENAME = "caliber-pre-commit.mdc";
345
- LEARNINGS_BLOCK_START = "<!-- caliber:managed:learnings -->";
346
- LEARNINGS_BLOCK_END = "<!-- /caliber:managed:learnings -->";
347
- LEARNINGS_BLOCK = `${LEARNINGS_BLOCK_START}
348
- ## Session Learnings
349
-
350
- Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
351
- These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
352
- ${LEARNINGS_BLOCK_END}`;
353
- CURSOR_LEARNINGS_FILENAME = "caliber-learnings.mdc";
354
- CURSOR_LEARNINGS_CONTENT = `---
355
- description: Reference session-learned patterns from CALIBER_LEARNINGS.md
356
- alwaysApply: true
357
- ---
358
- Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
359
- These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
360
- `;
361
- SYNC_BLOCK_START = "<!-- caliber:managed:sync -->";
362
- SYNC_BLOCK_END = "<!-- /caliber:managed:sync -->";
363
- CURSOR_SYNC_FILENAME = "caliber-sync.mdc";
364
- }
365
- });
366
-
367
- // src/lib/builtin-skills.ts
368
- var builtin_skills_exports = {};
369
- __export(builtin_skills_exports, {
370
- BUILTIN_SKILLS: () => BUILTIN_SKILLS,
371
- BUILTIN_SKILL_NAMES: () => BUILTIN_SKILL_NAMES,
372
- FIND_SKILLS_SKILL: () => FIND_SKILLS_SKILL,
373
- SAVE_LEARNING_SKILL: () => SAVE_LEARNING_SKILL,
374
- SETUP_CALIBER_SKILL: () => SETUP_CALIBER_SKILL,
375
- buildSkillContent: () => buildSkillContent,
376
- ensureBuiltinSkills: () => ensureBuiltinSkills
377
- });
378
- import fs17 from "fs";
379
- import path16 from "path";
380
- function buildSkillContent(skill) {
381
- const frontmatter = `---
382
- name: ${skill.name}
383
- description: ${skill.description}
384
- ---
385
-
386
- `;
387
- return frontmatter + skill.content;
388
- }
389
- function getFindSkillsContent() {
390
- const bin = resolveCaliber();
391
- return `# Find Skills
392
-
393
- Search the public skill registry for community-contributed skills
394
- relevant to the user's current task and install them into this project.
395
-
396
- ## Instructions
397
-
398
- 1. Identify the key technologies, frameworks, or task types from the
399
- user's request that might have community skills available
400
- 2. Ask the user: "Would you like me to search for community skills
401
- for [identified technologies]?"
402
- 3. If the user agrees, run:
403
- \`\`\`bash
404
- ${bin} skills --query "<relevant terms>"
405
- \`\`\`
406
- This outputs the top 5 matching skills with scores and descriptions.
407
- 4. Present the results to the user and ask which ones to install
408
- 5. Install the selected skills:
409
- \`\`\`bash
410
- ${bin} skills --install <slug1>,<slug2>
411
- \`\`\`
412
- 6. Read the installed SKILL.md files to load them into your current
413
- context so you can use them immediately in this session
414
- 7. Summarize what was installed and continue with the user's task
415
-
416
- ## Examples
417
-
418
- User: "let's build a web app using React"
419
- -> "I notice you want to work with React. Would you like me to search
420
- for community skills that could help with React development?"
421
- -> If yes: run \`${bin} skills --query "react frontend"\`
422
- -> Show the user the results, ask which to install
423
- -> Run \`${bin} skills --install <selected-slugs>\`
424
- -> Read the installed files and continue
120
+ User: "let's build a web app using React"
121
+ -> "I notice you want to work with React. Would you like me to search
122
+ for community skills that could help with React development?"
123
+ -> If yes: run \`${bin} skills --query "react frontend"\`
124
+ -> Show the user the results, ask which to install
125
+ -> Run \`${bin} skills --install <selected-slugs>\`
126
+ -> Read the installed files and continue
425
127
 
426
128
  User: "help me set up Docker for this project"
427
129
  -> "Would you like me to search for Docker-related skills?"
@@ -516,18 +218,19 @@ then only act if something is missing.
516
218
  command -v caliber >/dev/null 2>&1 && caliber --version || echo "NOT_INSTALLED"
517
219
  \`\`\`
518
220
 
519
- - If a version prints \u2192 Caliber is installed. Move to Step 2.
520
- - If NOT_INSTALLED \u2192 Tell the user: "Caliber isn't installed yet. I'll install it now."
521
- Then run:
221
+ - If a version prints \u2192 Caliber is installed globally. Set \`CALIBER="caliber"\` and move to Step 2.
222
+ - If NOT_INSTALLED \u2192 Install it globally (faster for daily use since the pre-commit hook runs on every commit):
522
223
  \`\`\`bash
523
224
  npm install -g @rely-ai/caliber
524
225
  \`\`\`
525
- If npm fails (permissions, no Node, etc.), try:
226
+ Set \`CALIBER="caliber"\`.
227
+
228
+ If npm fails (permissions, no sudo, etc.), fall back to npx:
526
229
  \`\`\`bash
527
- npx @rely-ai/caliber --version
230
+ npx @rely-ai/caliber --version 2>/dev/null || echo "NO_NODE"
528
231
  \`\`\`
529
- If npx works, use \`npx @rely-ai/caliber\` as the command prefix for all subsequent steps.
530
- If both fail, tell the user: "Caliber requires Node.js >= 20. Install Node first, then run /setup-caliber again."
232
+ - If npx works \u2192 Set \`CALIBER="npx @rely-ai/caliber"\`. This works but adds ~500ms per invocation.
233
+ - If NO_NODE \u2192 Tell the user: "Caliber requires Node.js >= 20. Install Node first, then run /setup-caliber again." Stop here.
531
234
 
532
235
  ### Step 2: Check if pre-commit hook is installed
533
236
 
@@ -538,35 +241,52 @@ grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "HOOK_ACTIVE" || ech
538
241
  - If HOOK_ACTIVE \u2192 Tell the user: "Pre-commit hook is active \u2014 configs sync on every commit." Move to Step 3.
539
242
  - If NO_HOOK \u2192 Tell the user: "I'll install the pre-commit hook so your agent configs sync automatically on every commit."
540
243
  \`\`\`bash
541
- caliber hooks --install
244
+ $CALIBER hooks --install
542
245
  \`\`\`
543
246
 
544
- ### Step 3: Check if agent configs exist
247
+ ### Step 3: Detect agents and check if configs exist
545
248
 
249
+ First, detect which coding agents are configured in this project:
250
+ \`\`\`bash
251
+ AGENTS=""
252
+ [ -d .claude ] && AGENTS="claude"
253
+ [ -d .cursor ] && AGENTS="\${AGENTS:+$AGENTS,}cursor"
254
+ [ -d .agents ] || [ -f AGENTS.md ] && AGENTS="\${AGENTS:+$AGENTS,}codex"
255
+ [ -f .github/copilot-instructions.md ] && AGENTS="\${AGENTS:+$AGENTS,}github-copilot"
256
+ echo "DETECTED_AGENTS=\${AGENTS:-none}"
257
+ \`\`\`
258
+
259
+ If no agents are detected, ask the user which coding agents they use (Claude Code, Cursor, Codex, GitHub Copilot).
260
+ Build the agent list from their answer as a comma-separated string (e.g. "claude,cursor").
261
+
262
+ Then check if agent configs exist:
546
263
  \`\`\`bash
547
264
  echo "CLAUDE_MD=$([ -f CLAUDE.md ] && echo exists || echo missing)"
548
265
  echo "CURSOR_RULES=$([ -d .cursor/rules ] && ls .cursor/rules/*.mdc 2>/dev/null | wc -l | tr -d ' ' || echo 0)"
549
266
  echo "AGENTS_MD=$([ -f AGENTS.md ] && echo exists || echo missing)"
267
+ echo "COPILOT=$([ -f .github/copilot-instructions.md ] && echo exists || echo missing)"
550
268
  \`\`\`
551
269
 
552
- - If configs exist \u2192 Tell the user which configs are present. Move to Step 4.
270
+ - If configs exist for the detected agents \u2192 Tell the user which configs are present. Move to Step 4.
553
271
  - If configs are missing \u2192 Tell the user: "No agent configs found. I'll generate them now."
272
+ Use the detected or user-selected agent list:
554
273
  \`\`\`bash
555
- caliber init --auto-approve
274
+ $CALIBER init --auto-approve --agent <comma-separated-agents>
556
275
  \`\`\`
557
- This generates CLAUDE.md, Cursor rules, AGENTS.md, and skills for all detected agents.
276
+ For example: \`$CALIBER init --auto-approve --agent claude,cursor\`
277
+ This generates CLAUDE.md, Cursor rules, AGENTS.md, skills, and sync infrastructure for the specified agents.
558
278
 
559
279
  ### Step 4: Check if configs are fresh
560
280
 
561
281
  \`\`\`bash
562
- caliber score --json --quiet 2>/dev/null | head -1
282
+ $CALIBER score --json --quiet 2>/dev/null | head -1
563
283
  \`\`\`
564
284
 
565
285
  - If score is 80+ \u2192 Tell the user: "Your configs are in good shape (score: X/100)."
566
286
  - If score is below 80 \u2192 Tell the user: "Your configs could be improved (score: X/100). Want me to run a refresh?"
567
287
  If yes:
568
288
  \`\`\`bash
569
- caliber refresh
289
+ $CALIBER refresh
570
290
  \`\`\`
571
291
 
572
292
  ### Step 5: Ask about team setup
@@ -577,20 +297,20 @@ Ask the user: "Are you setting up for yourself only, or for your team too?"
577
297
 
578
298
  Check if session learning is enabled:
579
299
  \`\`\`bash
580
- caliber learn status 2>/dev/null | head -3
300
+ $CALIBER learn status 2>/dev/null | head -3
581
301
  \`\`\`
582
302
  - If learning is already enabled \u2192 note it in the summary.
583
303
  - If not enabled \u2192 ask the user: "Caliber can learn from your coding sessions \u2014 when you correct a mistake or fix a pattern, it remembers for next time. Enable session learning?"
584
304
  If yes:
585
305
  \`\`\`bash
586
- caliber learn install
306
+ $CALIBER learn install
587
307
  \`\`\`
588
308
 
589
309
  Then tell the user:
590
310
  "You're all set! Here's what happens next:
591
311
  - Every time you commit, Caliber syncs your agent configs automatically
592
312
  - Your CLAUDE.md, Cursor rules, and AGENTS.md stay current with your code
593
- - Run \`caliber skills\` anytime to discover community skills for your stack"
313
+ - Run \`$CALIBER skills\` anytime to discover community skills for your stack"
594
314
 
595
315
  Then show the summary (see below) and stop.
596
316
 
@@ -625,7 +345,7 @@ Ask the user: "Are you setting up for yourself only, or for your team too?"
625
345
  \`\`\`
626
346
  Now determine which LLM provider the team uses. Check the local Caliber config:
627
347
  \`\`\`bash
628
- caliber config --show 2>/dev/null || echo "NO_CONFIG"
348
+ $CALIBER config --show 2>/dev/null || echo "NO_CONFIG"
629
349
  \`\`\`
630
350
 
631
351
  Based on the provider, the GitHub Action needs the corresponding secret:
@@ -691,11 +411,11 @@ From now on, every commit keeps all your agent configs in sync automatically.
691
411
  function ensureBuiltinSkills() {
692
412
  const written = [];
693
413
  for (const { platformDir, skillsDir } of PLATFORM_CONFIGS) {
694
- if (!fs17.existsSync(platformDir)) continue;
414
+ if (!fs3.existsSync(platformDir)) continue;
695
415
  for (const skill of BUILTIN_SKILLS) {
696
- const skillPath = path16.join(skillsDir, skill.name, "SKILL.md");
697
- fs17.mkdirSync(path16.dirname(skillPath), { recursive: true });
698
- fs17.writeFileSync(skillPath, buildSkillContent(skill));
416
+ const skillPath = path3.join(skillsDir, skill.name, "SKILL.md");
417
+ fs3.mkdirSync(path3.dirname(skillPath), { recursive: true });
418
+ fs3.writeFileSync(skillPath, buildSkillContent(skill));
699
419
  written.push(skillPath);
700
420
  }
701
421
  }
@@ -713,27 +433,394 @@ var init_builtin_skills = __esm({
713
433
  return getFindSkillsContent();
714
434
  }
715
435
  };
716
- SAVE_LEARNING_SKILL = {
717
- name: "save-learning",
718
- description: "Saves user instructions as persistent learnings for future sessions. Use when the user says 'remember this', 'always do X', 'from now on', 'never do Y', or gives any instruction they want persisted across sessions. Proactively suggest when the user states a preference, convention, or rule they clearly want followed in the future.",
719
- get content() {
720
- return getSaveLearningContent();
721
- }
436
+ SAVE_LEARNING_SKILL = {
437
+ name: "save-learning",
438
+ description: "Saves user instructions as persistent learnings for future sessions. Use when the user says 'remember this', 'always do X', 'from now on', 'never do Y', or gives any instruction they want persisted across sessions. Proactively suggest when the user states a preference, convention, or rule they clearly want followed in the future.",
439
+ get content() {
440
+ return getSaveLearningContent();
441
+ }
442
+ };
443
+ SETUP_CALIBER_SKILL = {
444
+ name: "setup-caliber",
445
+ description: "Sets up Caliber for automatic AI agent context sync. Installs pre-commit hooks so CLAUDE.md, Cursor rules, and Copilot instructions update automatically on every commit. Use when Caliber hooks are not yet installed or when the user asks about keeping agent configs in sync.",
446
+ get content() {
447
+ return getSetupCaliberContent();
448
+ }
449
+ };
450
+ BUILTIN_SKILLS = [FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL, SETUP_CALIBER_SKILL];
451
+ PLATFORM_CONFIGS = [
452
+ { platformDir: ".claude", skillsDir: path3.join(".claude", "skills") },
453
+ { platformDir: ".cursor", skillsDir: path3.join(".cursor", "skills") },
454
+ { platformDir: ".agents", skillsDir: path3.join(".agents", "skills") }
455
+ ];
456
+ BUILTIN_SKILL_NAMES = new Set(BUILTIN_SKILLS.map((s) => s.name));
457
+ }
458
+ });
459
+
460
+ // src/writers/pre-commit-block.ts
461
+ var pre_commit_block_exports = {};
462
+ __export(pre_commit_block_exports, {
463
+ appendLearningsBlock: () => appendLearningsBlock,
464
+ appendManagedBlocks: () => appendManagedBlocks,
465
+ appendPreCommitBlock: () => appendPreCommitBlock,
466
+ appendSyncBlock: () => appendSyncBlock,
467
+ getCursorLearningsRule: () => getCursorLearningsRule,
468
+ getCursorPreCommitRule: () => getCursorPreCommitRule,
469
+ getCursorSetupRule: () => getCursorSetupRule,
470
+ getCursorSyncRule: () => getCursorSyncRule,
471
+ hasLearningsBlock: () => hasLearningsBlock,
472
+ hasPreCommitBlock: () => hasPreCommitBlock,
473
+ hasSyncBlock: () => hasSyncBlock,
474
+ stripManagedBlocks: () => stripManagedBlocks
475
+ });
476
+ function getCaliberNotFoundFallback(platform, bin) {
477
+ switch (platform) {
478
+ case "claude":
479
+ return `If \`${bin}\` is not found, tell the user: "This project uses Caliber for agent config sync. Run /setup-caliber to get set up."`;
480
+ case "codex":
481
+ return `If \`${bin}\` is not found, read \`.agents/skills/setup-caliber/SKILL.md\` and follow its instructions to install Caliber.`;
482
+ case "copilot":
483
+ return `If \`${bin}\` is not found, tell the developer to set up Caliber by running \`/setup-caliber\` in Claude Code or Cursor. Alternatively, they can run these commands in their terminal:
484
+ \`\`\`
485
+ npx @rely-ai/caliber hooks --install
486
+ npx @rely-ai/caliber refresh
487
+ \`\`\``;
488
+ }
489
+ }
490
+ function getPreCommitBlock(platform = "claude") {
491
+ const bin = resolveCaliber();
492
+ return `${BLOCK_START}
493
+ ## Before Committing
494
+
495
+ **IMPORTANT:** Before every git commit, you MUST ensure Caliber syncs agent configs with the latest code changes.
496
+
497
+ First, check if the pre-commit hook is already installed:
498
+ \`\`\`bash
499
+ grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "hook-active" || echo "no-hook"
500
+ \`\`\`
501
+
502
+ - If **hook-active**: the hook handles sync automatically \u2014 just commit normally. Tell the user: "Caliber will sync your agent configs automatically via the pre-commit hook."
503
+ - If **no-hook**: run Caliber manually before committing:
504
+ 1. Tell the user: "Caliber: Syncing agent configs with your latest changes..."
505
+ 2. Run: \`${bin} refresh && git add ${MANAGED_DOC_PATHS} 2>/dev/null\`
506
+ 3. After it completes, briefly tell the user what Caliber updated. Then proceed with the commit.
507
+
508
+ ${getCaliberNotFoundFallback(platform, bin)}
509
+ ${BLOCK_END}`;
510
+ }
511
+ function getCursorRuleContent() {
512
+ const bin = resolveCaliber();
513
+ return `---
514
+ description: Run Caliber sync before git commits to keep agent configs up to date
515
+ alwaysApply: true
516
+ ---
517
+ Before every git commit, check if the Caliber pre-commit hook is installed:
518
+ Run: \`grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "hook-active" || echo "no-hook"\`
519
+
520
+ If hook-active: just commit normally \u2014 Caliber syncs automatically via the hook. Tell the user: "Caliber will sync your agent configs automatically."
521
+ If no-hook: run \`${bin} refresh && git add ${MANAGED_DOC_PATHS} 2>/dev/null\` before committing, and tell the user what was updated.
522
+
523
+ If \`${bin}\` is not found, read the setup-caliber skill from .cursor/skills/setup-caliber/SKILL.md and follow its instructions to install Caliber.
524
+ `;
525
+ }
526
+ function hasPreCommitBlock(content) {
527
+ return content.includes(BLOCK_START);
528
+ }
529
+ function appendPreCommitBlock(content, platform = "claude") {
530
+ if (hasPreCommitBlock(content)) return content;
531
+ const trimmed = content.trimEnd();
532
+ return trimmed + "\n\n" + getPreCommitBlock(platform) + "\n";
533
+ }
534
+ function getCursorPreCommitRule() {
535
+ return { filename: CURSOR_RULE_FILENAME, content: getCursorRuleContent() };
536
+ }
537
+ function hasLearningsBlock(content) {
538
+ return content.includes(LEARNINGS_BLOCK_START);
539
+ }
540
+ function appendLearningsBlock(content) {
541
+ if (hasLearningsBlock(content)) return content;
542
+ const trimmed = content.trimEnd();
543
+ return trimmed + "\n\n" + LEARNINGS_BLOCK + "\n";
544
+ }
545
+ function getCursorLearningsRule() {
546
+ return { filename: CURSOR_LEARNINGS_FILENAME, content: CURSOR_LEARNINGS_CONTENT };
547
+ }
548
+ function getSyncSetupInstruction(platform, bin) {
549
+ switch (platform) {
550
+ case "claude":
551
+ return `If the pre-commit hook is not set up, run \`/setup-caliber\` to configure everything automatically.`;
552
+ case "codex":
553
+ return `If the pre-commit hook is not set up, read \`.agents/skills/setup-caliber/SKILL.md\` and follow the setup instructions.`;
554
+ case "copilot":
555
+ return `If the pre-commit hook is not set up, the developer should run \`/setup-caliber\` in Claude Code or Cursor for automated setup. Alternatively, run in terminal:
556
+ \`\`\`bash
557
+ npx @rely-ai/caliber hooks --install
558
+ npx @rely-ai/caliber refresh
559
+ git add ${MANAGED_DOC_PATHS} 2>/dev/null
560
+ \`\`\``;
561
+ }
562
+ }
563
+ function getSyncBlock(platform = "claude") {
564
+ const bin = resolveCaliber();
565
+ return `${SYNC_BLOCK_START}
566
+ ## Context Sync
567
+
568
+ This project uses [Caliber](https://github.com/caliber-ai-org/ai-setup) to keep AI agent configs in sync across Claude Code, Cursor, Copilot, and Codex.
569
+ Configs update automatically before each commit via \`${bin} refresh\`.
570
+ ${getSyncSetupInstruction(platform, bin)}
571
+ ${SYNC_BLOCK_END}`;
572
+ }
573
+ function hasSyncBlock(content) {
574
+ return content.includes(SYNC_BLOCK_START);
575
+ }
576
+ function appendSyncBlock(content, platform = "claude") {
577
+ if (hasSyncBlock(content)) return content;
578
+ const trimmed = content.trimEnd();
579
+ return trimmed + "\n\n" + getSyncBlock(platform) + "\n";
580
+ }
581
+ function appendManagedBlocks(content, platform = "claude") {
582
+ return appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(content, platform)), platform);
583
+ }
584
+ function getCursorSyncContent() {
585
+ const bin = resolveCaliber();
586
+ return `---
587
+ description: This project uses Caliber for automatic AI agent context sync
588
+ alwaysApply: true
589
+ ---
590
+ This project uses Caliber to keep AI agent configs (CLAUDE.md, Cursor rules, Copilot instructions)
591
+ in sync with the codebase automatically. Before each commit, run \`${bin} refresh\` to sync configs.
592
+ If \`${bin}\` is not found, read the setup-caliber skill from .cursor/skills/setup-caliber/SKILL.md and follow its instructions to install Caliber.
593
+ `;
594
+ }
595
+ function getCursorSyncRule() {
596
+ return { filename: CURSOR_SYNC_FILENAME, content: getCursorSyncContent() };
597
+ }
598
+ function getCursorSetupContent() {
599
+ return `---
600
+ description: Check if Caliber agent config sync is set up and offer installation on new sessions
601
+ alwaysApply: true
602
+ ---
603
+ This project uses Caliber to keep AI agent configs in sync automatically.
604
+ At the start of each session, silently check if sync is active:
605
+
606
+ Run: \`grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "SYNC_ACTIVE" || echo "NO_SYNC"\`
607
+
608
+ If SYNC_ACTIVE: No action needed \u2014 Caliber syncs automatically on every commit.
609
+ If NO_SYNC: Tell the user: "This project uses Caliber for automatic agent config sync, but it's not set up on your machine yet. Want me to set it up? It takes about 30 seconds."
610
+
611
+ If the user agrees, read \`.cursor/skills/setup-caliber/SKILL.md\` and follow its instructions step by step.
612
+ `;
613
+ }
614
+ function getCursorSetupRule() {
615
+ return { filename: CURSOR_SETUP_FILENAME, content: getCursorSetupContent() };
616
+ }
617
+ function stripManagedBlocks(content) {
618
+ let result = content;
619
+ for (const [start, end] of MANAGED_BLOCK_PAIRS) {
620
+ const regex = new RegExp(`\\n?${start.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s\\S]*?${end.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\n?`, "g");
621
+ result = result.replace(regex, "\n");
622
+ }
623
+ return result.replace(/\n{3,}/g, "\n\n").trim() + "\n";
624
+ }
625
+ var BLOCK_START, BLOCK_END, MANAGED_DOC_PATHS, CURSOR_RULE_FILENAME, LEARNINGS_BLOCK_START, LEARNINGS_BLOCK_END, LEARNINGS_BLOCK, CURSOR_LEARNINGS_FILENAME, CURSOR_LEARNINGS_CONTENT, SYNC_BLOCK_START, SYNC_BLOCK_END, CURSOR_SYNC_FILENAME, CURSOR_SETUP_FILENAME, MANAGED_BLOCK_PAIRS;
626
+ var init_pre_commit_block = __esm({
627
+ "src/writers/pre-commit-block.ts"() {
628
+ "use strict";
629
+ init_resolve_caliber();
630
+ BLOCK_START = "<!-- caliber:managed:pre-commit -->";
631
+ BLOCK_END = "<!-- /caliber:managed:pre-commit -->";
632
+ MANAGED_DOC_PATHS = "CLAUDE.md .claude/ .cursor/ .cursorrules .github/copilot-instructions.md .github/instructions/ AGENTS.md CALIBER_LEARNINGS.md";
633
+ CURSOR_RULE_FILENAME = "caliber-pre-commit.mdc";
634
+ LEARNINGS_BLOCK_START = "<!-- caliber:managed:learnings -->";
635
+ LEARNINGS_BLOCK_END = "<!-- /caliber:managed:learnings -->";
636
+ LEARNINGS_BLOCK = `${LEARNINGS_BLOCK_START}
637
+ ## Session Learnings
638
+
639
+ Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
640
+ These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
641
+ ${LEARNINGS_BLOCK_END}`;
642
+ CURSOR_LEARNINGS_FILENAME = "caliber-learnings.mdc";
643
+ CURSOR_LEARNINGS_CONTENT = `---
644
+ description: Reference session-learned patterns from CALIBER_LEARNINGS.md
645
+ alwaysApply: true
646
+ ---
647
+ Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
648
+ These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
649
+ `;
650
+ SYNC_BLOCK_START = "<!-- caliber:managed:sync -->";
651
+ SYNC_BLOCK_END = "<!-- /caliber:managed:sync -->";
652
+ CURSOR_SYNC_FILENAME = "caliber-sync.mdc";
653
+ CURSOR_SETUP_FILENAME = "caliber-setup.mdc";
654
+ MANAGED_BLOCK_PAIRS = [
655
+ [BLOCK_START, BLOCK_END],
656
+ [LEARNINGS_BLOCK_START, LEARNINGS_BLOCK_END],
657
+ [SYNC_BLOCK_START, SYNC_BLOCK_END]
658
+ ];
659
+ }
660
+ });
661
+
662
+ // src/llm/config.ts
663
+ var config_exports = {};
664
+ __export(config_exports, {
665
+ DEFAULT_FAST_MODELS: () => DEFAULT_FAST_MODELS,
666
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
667
+ MODEL_CONTEXT_WINDOWS: () => MODEL_CONTEXT_WINDOWS,
668
+ getConfigFilePath: () => getConfigFilePath,
669
+ getDisplayModel: () => getDisplayModel,
670
+ getFastModel: () => getFastModel,
671
+ getMaxPromptTokens: () => getMaxPromptTokens,
672
+ loadConfig: () => loadConfig,
673
+ readConfigFile: () => readConfigFile,
674
+ resolveFromEnv: () => resolveFromEnv,
675
+ writeConfigFile: () => writeConfigFile
676
+ });
677
+ import fs6 from "fs";
678
+ import path6 from "path";
679
+ import os2 from "os";
680
+ function getMaxPromptTokens() {
681
+ const config = loadConfig();
682
+ const model = process.env.CALIBER_MODEL || config?.model;
683
+ const contextWindow = model ? MODEL_CONTEXT_WINDOWS[model] ?? DEFAULT_CONTEXT_WINDOW : DEFAULT_CONTEXT_WINDOW;
684
+ const budget = Math.floor(contextWindow * INPUT_BUDGET_FRACTION);
685
+ return Math.max(MIN_PROMPT_TOKENS, Math.min(budget, MAX_PROMPT_TOKENS_CAP));
686
+ }
687
+ function loadConfig() {
688
+ const envConfig = resolveFromEnv();
689
+ if (envConfig) return envConfig;
690
+ return readConfigFile();
691
+ }
692
+ function resolveFromEnv() {
693
+ if (process.env.ANTHROPIC_API_KEY) {
694
+ return {
695
+ provider: "anthropic",
696
+ apiKey: process.env.ANTHROPIC_API_KEY,
697
+ model: process.env.CALIBER_MODEL || DEFAULT_MODELS.anthropic
698
+ };
699
+ }
700
+ if (process.env.VERTEX_PROJECT_ID || process.env.GCP_PROJECT_ID) {
701
+ return {
702
+ provider: "vertex",
703
+ model: process.env.CALIBER_MODEL || DEFAULT_MODELS.vertex,
704
+ vertexProjectId: process.env.VERTEX_PROJECT_ID || process.env.GCP_PROJECT_ID,
705
+ vertexRegion: process.env.VERTEX_REGION || process.env.GCP_REGION || "us-east5",
706
+ vertexCredentials: process.env.VERTEX_SA_CREDENTIALS || process.env.GOOGLE_APPLICATION_CREDENTIALS
707
+ };
708
+ }
709
+ if (process.env.OPENAI_API_KEY) {
710
+ return {
711
+ provider: "openai",
712
+ apiKey: process.env.OPENAI_API_KEY,
713
+ model: process.env.CALIBER_MODEL || DEFAULT_MODELS.openai,
714
+ baseUrl: process.env.OPENAI_BASE_URL
715
+ };
716
+ }
717
+ if (process.env.CALIBER_USE_CURSOR_SEAT === "1" || process.env.CALIBER_USE_CURSOR_SEAT === "true") {
718
+ return {
719
+ provider: "cursor",
720
+ model: process.env.CALIBER_MODEL || DEFAULT_MODELS.cursor
721
+ };
722
+ }
723
+ if (process.env.CALIBER_USE_CLAUDE_CLI === "1" || process.env.CALIBER_USE_CLAUDE_CLI === "true") {
724
+ return {
725
+ provider: "claude-cli",
726
+ model: process.env.CALIBER_MODEL || DEFAULT_MODELS["claude-cli"]
727
+ };
728
+ }
729
+ return null;
730
+ }
731
+ function readConfigFile() {
732
+ try {
733
+ if (!fs6.existsSync(CONFIG_FILE)) return null;
734
+ const raw = fs6.readFileSync(CONFIG_FILE, "utf-8");
735
+ const parsed = JSON.parse(raw);
736
+ if (!parsed.provider || !["anthropic", "vertex", "openai", "cursor", "claude-cli"].includes(parsed.provider)) {
737
+ return null;
738
+ }
739
+ return parsed;
740
+ } catch {
741
+ return null;
742
+ }
743
+ }
744
+ function writeConfigFile(config) {
745
+ if (!fs6.existsSync(CONFIG_DIR)) {
746
+ fs6.mkdirSync(CONFIG_DIR, { recursive: true });
747
+ }
748
+ const sanitized = { ...config };
749
+ if (sanitized.apiKey) {
750
+ sanitized.apiKey = sanitized.apiKey.trim();
751
+ }
752
+ fs6.writeFileSync(CONFIG_FILE, JSON.stringify(sanitized, null, 2) + "\n", { mode: 384 });
753
+ }
754
+ function getConfigFilePath() {
755
+ return CONFIG_FILE;
756
+ }
757
+ function getDisplayModel(config) {
758
+ if (config.model === "default" && config.provider === "claude-cli") {
759
+ return process.env.ANTHROPIC_MODEL || "default (inherited from Claude Code)";
760
+ }
761
+ return config.model;
762
+ }
763
+ function getFastModel() {
764
+ if (process.env.CALIBER_FAST_MODEL) return process.env.CALIBER_FAST_MODEL;
765
+ const config = loadConfig();
766
+ const provider = config?.provider;
767
+ if (process.env.ANTHROPIC_SMALL_FAST_MODEL && (!provider || provider === "anthropic" || provider === "vertex" || provider === "claude-cli")) {
768
+ return process.env.ANTHROPIC_SMALL_FAST_MODEL;
769
+ }
770
+ if (config?.fastModel) return config.fastModel;
771
+ if (provider) return DEFAULT_FAST_MODELS[provider];
772
+ return void 0;
773
+ }
774
+ var CONFIG_DIR, CONFIG_FILE, DEFAULT_MODELS, MODEL_CONTEXT_WINDOWS, DEFAULT_CONTEXT_WINDOW, INPUT_BUDGET_FRACTION, MAX_PROMPT_TOKENS_CAP, MIN_PROMPT_TOKENS, DEFAULT_FAST_MODELS;
775
+ var init_config = __esm({
776
+ "src/llm/config.ts"() {
777
+ "use strict";
778
+ CONFIG_DIR = path6.join(os2.homedir(), ".caliber");
779
+ CONFIG_FILE = path6.join(CONFIG_DIR, "config.json");
780
+ DEFAULT_MODELS = {
781
+ anthropic: "claude-sonnet-4-6",
782
+ vertex: "claude-sonnet-4-6",
783
+ openai: "gpt-4.1",
784
+ cursor: "sonnet-4.6",
785
+ "claude-cli": "default"
786
+ };
787
+ MODEL_CONTEXT_WINDOWS = {
788
+ "claude-sonnet-4-6": 2e5,
789
+ "claude-opus-4-6": 2e5,
790
+ "claude-haiku-4-5-20251001": 2e5,
791
+ "claude-sonnet-4-5-20250514": 2e5,
792
+ "gpt-4.1": 1e6,
793
+ "gpt-4.1-mini": 1e6,
794
+ "gpt-4o": 128e3,
795
+ "gpt-4o-mini": 128e3,
796
+ "sonnet-4.6": 2e5
722
797
  };
723
- SETUP_CALIBER_SKILL = {
724
- name: "setup-caliber",
725
- description: "Sets up Caliber for automatic AI agent context sync. Installs pre-commit hooks so CLAUDE.md, Cursor rules, and Copilot instructions update automatically on every commit. Use when Caliber hooks are not yet installed or when the user asks about keeping agent configs in sync.",
726
- get content() {
727
- return getSetupCaliberContent();
728
- }
798
+ DEFAULT_CONTEXT_WINDOW = 2e5;
799
+ INPUT_BUDGET_FRACTION = 0.6;
800
+ MAX_PROMPT_TOKENS_CAP = 3e5;
801
+ MIN_PROMPT_TOKENS = 3e4;
802
+ DEFAULT_FAST_MODELS = {
803
+ anthropic: "claude-haiku-4-5-20251001",
804
+ vertex: "claude-haiku-4-5-20251001",
805
+ openai: "gpt-4.1-mini",
806
+ cursor: "gpt-5.3-codex-fast"
729
807
  };
730
- BUILTIN_SKILLS = [FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL, SETUP_CALIBER_SKILL];
731
- PLATFORM_CONFIGS = [
732
- { platformDir: ".claude", skillsDir: path16.join(".claude", "skills") },
733
- { platformDir: ".cursor", skillsDir: path16.join(".cursor", "skills") },
734
- { platformDir: ".agents", skillsDir: path16.join(".agents", "skills") }
735
- ];
736
- BUILTIN_SKILL_NAMES = new Set(BUILTIN_SKILLS.map((s) => s.name));
808
+ }
809
+ });
810
+
811
+ // src/llm/types.ts
812
+ var types_exports = {};
813
+ __export(types_exports, {
814
+ isSeatBased: () => isSeatBased
815
+ });
816
+ function isSeatBased(provider) {
817
+ return SEAT_BASED_PROVIDERS.has(provider);
818
+ }
819
+ var SEAT_BASED_PROVIDERS;
820
+ var init_types = __esm({
821
+ "src/llm/types.ts"() {
822
+ "use strict";
823
+ SEAT_BASED_PROVIDERS = /* @__PURE__ */ new Set(["cursor", "claude-cli"]);
737
824
  }
738
825
  });
739
826
 
@@ -1052,8 +1139,8 @@ var init_lock = __esm({
1052
1139
 
1053
1140
  // src/cli.ts
1054
1141
  import { Command } from "commander";
1055
- import fs47 from "fs";
1056
- import path38 from "path";
1142
+ import fs49 from "fs";
1143
+ import path39 from "path";
1057
1144
  import { fileURLToPath } from "url";
1058
1145
 
1059
1146
  // src/commands/init.ts
@@ -1062,8 +1149,8 @@ import chalk14 from "chalk";
1062
1149
  import fs33 from "fs";
1063
1150
 
1064
1151
  // src/fingerprint/index.ts
1065
- import fs8 from "fs";
1066
- import path7 from "path";
1152
+ import fs9 from "fs";
1153
+ import path8 from "path";
1067
1154
 
1068
1155
  // src/fingerprint/git.ts
1069
1156
  import { execSync } from "child_process";
@@ -1171,8 +1258,8 @@ function scan(base, rel, depth, maxDepth, result) {
1171
1258
  }
1172
1259
 
1173
1260
  // src/fingerprint/existing-config.ts
1174
- import fs2 from "fs";
1175
- import path3 from "path";
1261
+ import fs4 from "fs";
1262
+ import path4 from "path";
1176
1263
 
1177
1264
  // src/constants.ts
1178
1265
  import path2 from "path";
@@ -1207,97 +1294,121 @@ var LEARNING_LAST_ERROR_FILE = "last-error.json";
1207
1294
  var MIN_SESSIONS_FOR_COMPARISON = 3;
1208
1295
 
1209
1296
  // src/fingerprint/existing-config.ts
1297
+ init_builtin_skills();
1298
+ init_pre_commit_block();
1299
+ var MANAGED_CURSOR_RULES = /* @__PURE__ */ new Set([
1300
+ getCursorPreCommitRule().filename,
1301
+ getCursorLearningsRule().filename,
1302
+ getCursorSyncRule().filename,
1303
+ getCursorSetupRule().filename
1304
+ ]);
1210
1305
  function readExistingConfigs(dir) {
1211
1306
  const configs = {};
1212
- const readmeMdPath = path3.join(dir, "README.md");
1213
- if (fs2.existsSync(readmeMdPath)) {
1214
- configs.readmeMd = fs2.readFileSync(readmeMdPath, "utf-8");
1307
+ const readmeMdPath = path4.join(dir, "README.md");
1308
+ if (fs4.existsSync(readmeMdPath)) {
1309
+ configs.readmeMd = fs4.readFileSync(readmeMdPath, "utf-8");
1215
1310
  }
1216
- const agentsMdPath = path3.join(dir, "AGENTS.md");
1217
- if (fs2.existsSync(agentsMdPath)) {
1218
- configs.agentsMd = fs2.readFileSync(agentsMdPath, "utf-8");
1311
+ const agentsMdPath = path4.join(dir, "AGENTS.md");
1312
+ if (fs4.existsSync(agentsMdPath)) {
1313
+ configs.agentsMd = fs4.readFileSync(agentsMdPath, "utf-8");
1219
1314
  }
1220
- const claudeMdPath = path3.join(dir, "CLAUDE.md");
1221
- if (fs2.existsSync(claudeMdPath)) {
1222
- configs.claudeMd = fs2.readFileSync(claudeMdPath, "utf-8");
1315
+ const claudeMdPath = path4.join(dir, "CLAUDE.md");
1316
+ if (fs4.existsSync(claudeMdPath)) {
1317
+ configs.claudeMd = fs4.readFileSync(claudeMdPath, "utf-8");
1223
1318
  }
1224
- const claudeSettingsPath = path3.join(dir, ".claude", "settings.json");
1225
- if (fs2.existsSync(claudeSettingsPath)) {
1319
+ const claudeSettingsPath = path4.join(dir, ".claude", "settings.json");
1320
+ if (fs4.existsSync(claudeSettingsPath)) {
1226
1321
  try {
1227
- configs.claudeSettings = JSON.parse(fs2.readFileSync(claudeSettingsPath, "utf-8"));
1322
+ configs.claudeSettings = JSON.parse(fs4.readFileSync(claudeSettingsPath, "utf-8"));
1228
1323
  } catch {
1229
1324
  }
1230
1325
  }
1231
- const skillsDir = path3.join(dir, ".claude", "skills");
1232
- if (fs2.existsSync(skillsDir)) {
1326
+ const skillsDir = path4.join(dir, ".claude", "skills");
1327
+ if (fs4.existsSync(skillsDir)) {
1233
1328
  try {
1234
- const entries = fs2.readdirSync(skillsDir);
1329
+ const entries = fs4.readdirSync(skillsDir);
1235
1330
  const skills = [];
1236
1331
  for (const entry of entries) {
1237
- const entryPath = path3.join(skillsDir, entry);
1238
- const skillMdPath = path3.join(entryPath, "SKILL.md");
1239
- if (fs2.statSync(entryPath).isDirectory() && fs2.existsSync(skillMdPath)) {
1240
- skills.push({ filename: `${entry}/SKILL.md`, content: fs2.readFileSync(skillMdPath, "utf-8") });
1332
+ if (BUILTIN_SKILL_NAMES.has(entry)) continue;
1333
+ const entryPath = path4.join(skillsDir, entry);
1334
+ const skillMdPath = path4.join(entryPath, "SKILL.md");
1335
+ if (fs4.statSync(entryPath).isDirectory() && fs4.existsSync(skillMdPath)) {
1336
+ skills.push({ filename: `${entry}/SKILL.md`, content: fs4.readFileSync(skillMdPath, "utf-8") });
1241
1337
  } else if (entry.endsWith(".md")) {
1242
- skills.push({ filename: entry, content: fs2.readFileSync(entryPath, "utf-8") });
1338
+ skills.push({ filename: entry, content: fs4.readFileSync(entryPath, "utf-8") });
1243
1339
  }
1244
1340
  }
1245
1341
  if (skills.length > 0) configs.claudeSkills = skills;
1246
1342
  } catch {
1247
1343
  }
1248
1344
  }
1249
- const cursorrulesPath = path3.join(dir, ".cursorrules");
1250
- if (fs2.existsSync(cursorrulesPath)) {
1251
- configs.cursorrules = fs2.readFileSync(cursorrulesPath, "utf-8");
1345
+ const cursorrulesPath = path4.join(dir, ".cursorrules");
1346
+ if (fs4.existsSync(cursorrulesPath)) {
1347
+ configs.cursorrules = fs4.readFileSync(cursorrulesPath, "utf-8");
1252
1348
  }
1253
- const cursorRulesDir = path3.join(dir, ".cursor", "rules");
1254
- if (fs2.existsSync(cursorRulesDir)) {
1349
+ const cursorRulesDir = path4.join(dir, ".cursor", "rules");
1350
+ if (fs4.existsSync(cursorRulesDir)) {
1255
1351
  try {
1256
- const files = fs2.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"));
1352
+ const files = fs4.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc") && !MANAGED_CURSOR_RULES.has(f));
1257
1353
  configs.cursorRules = files.map((f) => ({
1258
1354
  filename: f,
1259
- content: fs2.readFileSync(path3.join(cursorRulesDir, f), "utf-8")
1355
+ content: fs4.readFileSync(path4.join(cursorRulesDir, f), "utf-8")
1260
1356
  }));
1261
1357
  } catch {
1262
1358
  }
1263
1359
  }
1264
- const cursorSkillsDir = path3.join(dir, ".cursor", "skills");
1265
- if (fs2.existsSync(cursorSkillsDir)) {
1360
+ const cursorSkillsDir = path4.join(dir, ".cursor", "skills");
1361
+ if (fs4.existsSync(cursorSkillsDir)) {
1266
1362
  try {
1267
- const slugs = fs2.readdirSync(cursorSkillsDir).filter((f) => {
1268
- return fs2.statSync(path3.join(cursorSkillsDir, f)).isDirectory();
1363
+ const slugs = fs4.readdirSync(cursorSkillsDir).filter((f) => {
1364
+ return fs4.statSync(path4.join(cursorSkillsDir, f)).isDirectory() && !BUILTIN_SKILL_NAMES.has(f);
1269
1365
  });
1270
- configs.cursorSkills = slugs.filter((slug) => fs2.existsSync(path3.join(cursorSkillsDir, slug, "SKILL.md"))).map((name) => ({
1366
+ configs.cursorSkills = slugs.filter((slug) => fs4.existsSync(path4.join(cursorSkillsDir, slug, "SKILL.md"))).map((name) => ({
1271
1367
  name,
1272
1368
  filename: "SKILL.md",
1273
- content: fs2.readFileSync(path3.join(cursorSkillsDir, name, "SKILL.md"), "utf-8")
1369
+ content: fs4.readFileSync(path4.join(cursorSkillsDir, name, "SKILL.md"), "utf-8")
1274
1370
  }));
1275
1371
  } catch {
1276
1372
  }
1277
1373
  }
1278
- const mcpJsonPath = path3.join(dir, ".mcp.json");
1279
- if (fs2.existsSync(mcpJsonPath)) {
1374
+ const mcpJsonPath = path4.join(dir, ".mcp.json");
1375
+ if (fs4.existsSync(mcpJsonPath)) {
1280
1376
  try {
1281
- const mcpJson = JSON.parse(fs2.readFileSync(mcpJsonPath, "utf-8"));
1377
+ const mcpJson = JSON.parse(fs4.readFileSync(mcpJsonPath, "utf-8"));
1282
1378
  if (mcpJson.mcpServers) {
1283
1379
  configs.claudeMcpServers = mcpJson.mcpServers;
1284
1380
  }
1285
1381
  } catch {
1286
1382
  }
1287
1383
  }
1288
- const cursorMcpPath = path3.join(dir, ".cursor", "mcp.json");
1289
- if (fs2.existsSync(cursorMcpPath)) {
1384
+ const cursorMcpPath = path4.join(dir, ".cursor", "mcp.json");
1385
+ if (fs4.existsSync(cursorMcpPath)) {
1290
1386
  try {
1291
- const cursorMcpJson = JSON.parse(fs2.readFileSync(cursorMcpPath, "utf-8"));
1387
+ const cursorMcpJson = JSON.parse(fs4.readFileSync(cursorMcpPath, "utf-8"));
1292
1388
  if (cursorMcpJson.mcpServers) {
1293
1389
  configs.cursorMcpServers = cursorMcpJson.mcpServers;
1294
1390
  }
1295
1391
  } catch {
1296
1392
  }
1297
1393
  }
1298
- if (fs2.existsSync(PERSONAL_LEARNINGS_FILE)) {
1394
+ const copilotPath = path4.join(dir, ".github", "copilot-instructions.md");
1395
+ if (fs4.existsSync(copilotPath)) {
1396
+ configs.copilotInstructions = fs4.readFileSync(copilotPath, "utf-8");
1397
+ }
1398
+ const instructionsDir = path4.join(dir, ".github", "instructions");
1399
+ if (fs4.existsSync(instructionsDir)) {
1400
+ try {
1401
+ const files = fs4.readdirSync(instructionsDir).filter((f) => f.endsWith(".md"));
1402
+ configs.copilotInstructionFiles = files.map((f) => ({
1403
+ filename: f,
1404
+ content: fs4.readFileSync(path4.join(instructionsDir, f), "utf-8")
1405
+ }));
1406
+ } catch {
1407
+ }
1408
+ }
1409
+ if (fs4.existsSync(PERSONAL_LEARNINGS_FILE)) {
1299
1410
  try {
1300
- const content = fs2.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
1411
+ const content = fs4.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
1301
1412
  const bullets = content.split("\n").filter((l) => l.startsWith("- ")).join("\n");
1302
1413
  if (bullets) configs.personalLearnings = bullets;
1303
1414
  } catch {
@@ -1307,9 +1418,9 @@ function readExistingConfigs(dir) {
1307
1418
  }
1308
1419
 
1309
1420
  // src/fingerprint/code-analysis.ts
1310
- import fs3 from "fs";
1311
- import path4 from "path";
1312
- import { execSync as execSync3 } from "child_process";
1421
+ import fs5 from "fs";
1422
+ import path5 from "path";
1423
+ import { execSync as execSync4 } from "child_process";
1313
1424
 
1314
1425
  // src/lib/sanitize.ts
1315
1426
  var KNOWN_PREFIX_PATTERNS = [
@@ -1653,18 +1764,18 @@ function extractSkeletonIndentBased(lines, ext) {
1653
1764
  }
1654
1765
  function extractImports(content, filePath) {
1655
1766
  const imports = [];
1656
- const dir = path4.dirname(filePath);
1767
+ const dir = path5.dirname(filePath);
1657
1768
  for (const line of content.split("\n")) {
1658
1769
  const trimmed = line.trim();
1659
1770
  const jsMatch = trimmed.match(/(?:from|require\()\s*['"]([^'"]+)['"]/);
1660
1771
  if (jsMatch && jsMatch[1].startsWith(".")) {
1661
- imports.push(path4.normalize(path4.join(dir, jsMatch[1])));
1772
+ imports.push(path5.normalize(path5.join(dir, jsMatch[1])));
1662
1773
  continue;
1663
1774
  }
1664
1775
  const pyMatch = trimmed.match(/^from\s+(\.[.\w]*)\s+import/);
1665
1776
  if (pyMatch) {
1666
1777
  const modulePath = pyMatch[1].replace(/\./g, "/");
1667
- imports.push(path4.normalize(path4.join(dir, modulePath)));
1778
+ imports.push(path5.normalize(path5.join(dir, modulePath)));
1668
1779
  continue;
1669
1780
  }
1670
1781
  const goMatch = trimmed.match(/^\s*"([^"]+)"/);
@@ -1677,7 +1788,7 @@ function extractImports(content, filePath) {
1677
1788
  function buildImportCounts(files) {
1678
1789
  const counts = /* @__PURE__ */ new Map();
1679
1790
  for (const [filePath, content] of files) {
1680
- const ext = path4.extname(filePath).toLowerCase();
1791
+ const ext = path5.extname(filePath).toLowerCase();
1681
1792
  if (!SOURCE_EXTENSIONS.has(ext)) continue;
1682
1793
  const imports = extractImports(content, filePath);
1683
1794
  for (const imp of imports) {
@@ -1696,7 +1807,7 @@ function buildImportCounts(files) {
1696
1807
  function getGitFrequency(dir) {
1697
1808
  const freq = /* @__PURE__ */ new Map();
1698
1809
  try {
1699
- const output = execSync3(
1810
+ const output = execSync4(
1700
1811
  'git log --since="6 months ago" --format="" --name-only --diff-filter=ACMR 2>/dev/null | head -10000',
1701
1812
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 5e3 }
1702
1813
  );
@@ -1711,7 +1822,7 @@ function getGitFrequency(dir) {
1711
1822
  function groupByDirectory(files) {
1712
1823
  const groups = /* @__PURE__ */ new Map();
1713
1824
  for (const f of files) {
1714
- const dir = path4.dirname(f.path);
1825
+ const dir = path5.dirname(f.path);
1715
1826
  const group = groups.get(dir) || [];
1716
1827
  group.push(f);
1717
1828
  groups.set(dir, group);
@@ -1732,14 +1843,14 @@ function analyzeCode(dir) {
1732
1843
  let totalChars = 0;
1733
1844
  for (const relPath of allPaths) {
1734
1845
  try {
1735
- totalChars += fs3.statSync(path4.join(dir, relPath)).size;
1846
+ totalChars += fs5.statSync(path5.join(dir, relPath)).size;
1736
1847
  } catch {
1737
1848
  }
1738
1849
  }
1739
1850
  const fileContents = /* @__PURE__ */ new Map();
1740
1851
  for (const relPath of allPaths) {
1741
1852
  try {
1742
- const content = fs3.readFileSync(path4.join(dir, relPath), "utf-8");
1853
+ const content = fs5.readFileSync(path5.join(dir, relPath), "utf-8");
1743
1854
  if (content.split("\n").length <= 500) fileContents.set(relPath, content);
1744
1855
  } catch {
1745
1856
  }
@@ -1749,7 +1860,7 @@ function analyzeCode(dir) {
1749
1860
  const scored = [];
1750
1861
  let compressedChars = 0;
1751
1862
  for (const [relPath, rawContent] of fileContents) {
1752
- const ext = path4.extname(relPath).toLowerCase();
1863
+ const ext = path5.extname(relPath).toLowerCase();
1753
1864
  const compressed = compressContent(rawContent, ext);
1754
1865
  const skeleton = extractSkeleton(compressed, ext);
1755
1866
  compressedChars += compressed.length;
@@ -1778,7 +1889,7 @@ function analyzeCode(dir) {
1778
1889
  }
1779
1890
  if (similar.length > 0) {
1780
1891
  dupGroups++;
1781
- const names = similar.map((f) => path4.basename(f.path));
1892
+ const names = similar.map((f) => path5.basename(f.path));
1782
1893
  const summary = `(${similar.length} similar file${similar.length === 1 ? "" : "s"} in ${dirPath}/: ${names.join(", ")})`;
1783
1894
  const summarySize = summary.length + 30;
1784
1895
  if (includedChars + summarySize <= CHAR_BUDGET) {
@@ -1815,10 +1926,10 @@ function analyzeCode(dir) {
1815
1926
  }
1816
1927
  function walkDir(base, rel, depth, maxDepth, files) {
1817
1928
  if (depth > maxDepth) return;
1818
- const fullPath = path4.join(base, rel);
1929
+ const fullPath = path5.join(base, rel);
1819
1930
  let entries;
1820
1931
  try {
1821
- entries = fs3.readdirSync(fullPath, { withFileTypes: true });
1932
+ entries = fs5.readdirSync(fullPath, { withFileTypes: true });
1822
1933
  } catch {
1823
1934
  return;
1824
1935
  }
@@ -1831,7 +1942,7 @@ function walkDir(base, rel, depth, maxDepth, files) {
1831
1942
  } else if (entry.isFile()) {
1832
1943
  if (SKIP_FILES.has(entry.name)) continue;
1833
1944
  if (SKIP_PATTERNS.some((p) => p.test(entry.name))) continue;
1834
- const ext = path4.extname(entry.name).toLowerCase();
1945
+ const ext = path5.extname(entry.name).toLowerCase();
1835
1946
  if (TEXT_EXTENSIONS.has(ext) || depth === 0 && !ext && !entry.name.startsWith(".")) {
1836
1947
  files.push(relPath);
1837
1948
  }
@@ -1839,7 +1950,7 @@ function walkDir(base, rel, depth, maxDepth, files) {
1839
1950
  }
1840
1951
  }
1841
1952
  function filePriority(filePath) {
1842
- const base = path4.basename(filePath);
1953
+ const base = path5.basename(filePath);
1843
1954
  const entryPoints = /* @__PURE__ */ new Set([
1844
1955
  "index.ts",
1845
1956
  "index.js",
@@ -1971,7 +2082,7 @@ var AnthropicProvider = class {
1971
2082
  };
1972
2083
 
1973
2084
  // src/llm/vertex.ts
1974
- import fs5 from "fs";
2085
+ import fs7 from "fs";
1975
2086
  import { AnthropicVertex } from "@anthropic-ai/vertex-sdk";
1976
2087
  import { GoogleAuth } from "google-auth-library";
1977
2088
  var VertexProvider = class {
@@ -1994,7 +2105,7 @@ var VertexProvider = class {
1994
2105
  }
1995
2106
  } else {
1996
2107
  try {
1997
- creds = JSON.parse(fs5.readFileSync(raw, "utf-8"));
2108
+ creds = JSON.parse(fs7.readFileSync(raw, "utf-8"));
1998
2109
  } catch {
1999
2110
  throw new Error(`Cannot read credentials file: ${raw}`);
2000
2111
  }
@@ -3163,6 +3274,11 @@ CONSERVATIVE UPDATE means:
3163
3274
  - NEVER remove code blocks, backtick references, or architecture paths unless the diff deleted them
3164
3275
  - NEVER replace specific paths/commands with generic prose
3165
3276
 
3277
+ Cross-agent sync:
3278
+ - When a change affects CLAUDE.md, apply the same semantic update to AGENTS.md and copilot instructions if they exist
3279
+ - Each file uses its own format and conventions \u2014 do NOT copy content verbatim between them
3280
+ - The goal is consistency: all agent config files should reflect the same project state after refresh
3281
+
3166
3282
  Quality constraints (the output is scored deterministically):
3167
3283
  - CLAUDE.md / AGENTS.md: MUST stay under 400 lines. If the diff adds content, trim the least important lines elsewhere.
3168
3284
  - Keep 3+ code blocks with executable commands \u2014 do not remove code blocks
@@ -3171,16 +3287,17 @@ Quality constraints (the output is scored deterministically):
3171
3287
  - Preserve the existing structure (headings, bullet style, formatting)
3172
3288
 
3173
3289
  Managed content:
3174
- - Keep managed blocks (<!-- caliber:managed --> ... <!-- /caliber:managed -->) intact
3175
- - Keep context sync blocks (<!-- caliber:managed:sync --> ... <!-- /caliber:managed:sync -->) intact
3290
+ - All blocks between <!-- caliber:managed:* --> and <!-- /caliber:managed:* --> are managed by Caliber \u2014 copy them verbatim, do NOT modify their contents
3291
+ - This includes: pre-commit, sync, and learnings blocks
3176
3292
  - Do NOT modify CALIBER_LEARNINGS.md \u2014 it is managed separately
3177
- - Preserve any references to CALIBER_LEARNINGS.md in CLAUDE.md
3293
+ - Preserve any references to CALIBER_LEARNINGS.md
3178
3294
 
3179
3295
  Return a JSON object with this exact shape:
3180
3296
  {
3181
3297
  "updatedDocs": {
3182
3298
  "claudeMd": "<updated content or null>",
3183
3299
  "readmeMd": "<updated content or null>",
3300
+ "agentsMd": "<updated content or null>",
3184
3301
  "cursorrules": "<updated content or null>",
3185
3302
  "cursorRules": [{"filename": "name.mdc", "content": "..."}] or null,
3186
3303
  "claudeSkills": [{"filename": "name.md", "content": "..."}] or null,
@@ -3342,15 +3459,15 @@ async function detectProjectStack(fileTree, suffixCounts) {
3342
3459
  init_config();
3343
3460
 
3344
3461
  // src/fingerprint/cache.ts
3345
- import fs7 from "fs";
3346
- import path6 from "path";
3462
+ import fs8 from "fs";
3463
+ import path7 from "path";
3347
3464
  import crypto from "crypto";
3348
3465
  import { execSync as execSync7 } from "child_process";
3349
3466
  var CACHE_VERSION = 1;
3350
3467
  var CACHE_DIR = ".caliber/cache";
3351
3468
  var CACHE_FILE = "fingerprint.json";
3352
3469
  function getCachePath(dir) {
3353
- return path6.join(dir, CACHE_DIR, CACHE_FILE);
3470
+ return path7.join(dir, CACHE_DIR, CACHE_FILE);
3354
3471
  }
3355
3472
  function getGitHead(dir) {
3356
3473
  try {
@@ -3387,8 +3504,8 @@ function computeTreeSignature(fileTree, dir) {
3387
3504
  function loadFingerprintCache(dir, fileTree) {
3388
3505
  const cachePath = getCachePath(dir);
3389
3506
  try {
3390
- if (!fs7.existsSync(cachePath)) return null;
3391
- const raw = fs7.readFileSync(cachePath, "utf-8");
3507
+ if (!fs8.existsSync(cachePath)) return null;
3508
+ const raw = fs8.readFileSync(cachePath, "utf-8");
3392
3509
  const cache = JSON.parse(raw);
3393
3510
  if (cache.version !== CACHE_VERSION) return null;
3394
3511
  const currentHead = getGitHead(dir);
@@ -3409,9 +3526,9 @@ function loadFingerprintCache(dir, fileTree) {
3409
3526
  function saveFingerprintCache(dir, fileTree, codeAnalysis, languages, frameworks, tools, workspaces) {
3410
3527
  const cachePath = getCachePath(dir);
3411
3528
  try {
3412
- const cacheDir = path6.dirname(cachePath);
3413
- if (!fs7.existsSync(cacheDir)) {
3414
- fs7.mkdirSync(cacheDir, { recursive: true });
3529
+ const cacheDir = path7.dirname(cachePath);
3530
+ if (!fs8.existsSync(cacheDir)) {
3531
+ fs8.mkdirSync(cacheDir, { recursive: true });
3415
3532
  }
3416
3533
  const cache = {
3417
3534
  version: CACHE_VERSION,
@@ -3423,15 +3540,15 @@ function saveFingerprintCache(dir, fileTree, codeAnalysis, languages, frameworks
3423
3540
  tools,
3424
3541
  workspaces
3425
3542
  };
3426
- fs7.writeFileSync(cachePath, JSON.stringify(cache), "utf-8");
3543
+ fs8.writeFileSync(cachePath, JSON.stringify(cache), "utf-8");
3427
3544
  } catch {
3428
3545
  }
3429
3546
  }
3430
3547
  function getDetectedWorkspaces(dir) {
3431
3548
  const cachePath = getCachePath(dir);
3432
3549
  try {
3433
- if (!fs7.existsSync(cachePath)) return [];
3434
- const raw = fs7.readFileSync(cachePath, "utf-8");
3550
+ if (!fs8.existsSync(cachePath)) return [];
3551
+ const raw = fs8.readFileSync(cachePath, "utf-8");
3435
3552
  const cache = JSON.parse(raw);
3436
3553
  return cache.workspaces ?? [];
3437
3554
  } catch {
@@ -3483,9 +3600,9 @@ async function collectFingerprint(dir) {
3483
3600
  }
3484
3601
  function readPackageName(dir) {
3485
3602
  try {
3486
- const pkgPath = path7.join(dir, "package.json");
3487
- if (!fs8.existsSync(pkgPath)) return void 0;
3488
- const pkg3 = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
3603
+ const pkgPath = path8.join(dir, "package.json");
3604
+ if (!fs9.existsSync(pkgPath)) return void 0;
3605
+ const pkg3 = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
3489
3606
  return pkg3.name;
3490
3607
  } catch {
3491
3608
  return void 0;
@@ -3499,7 +3616,7 @@ async function enrichWithLLM(fingerprint) {
3499
3616
  const suffixCounts = {};
3500
3617
  for (const entry of fingerprint.fileTree) {
3501
3618
  if (entry.endsWith("/")) continue;
3502
- const ext = path7.extname(entry).toLowerCase();
3619
+ const ext = path8.extname(entry).toLowerCase();
3503
3620
  if (ext) {
3504
3621
  suffixCounts[ext] = (suffixCounts[ext] || 0) + 1;
3505
3622
  }
@@ -3515,22 +3632,22 @@ async function enrichWithLLM(fingerprint) {
3515
3632
  }
3516
3633
 
3517
3634
  // src/scanner/index.ts
3518
- import fs9 from "fs";
3519
- import path8 from "path";
3635
+ import fs10 from "fs";
3636
+ import path9 from "path";
3520
3637
  import crypto2 from "crypto";
3521
3638
  import os4 from "os";
3522
3639
  function detectPlatforms() {
3523
3640
  const home = os4.homedir();
3524
3641
  return {
3525
- claude: fs9.existsSync(path8.join(home, ".claude")),
3526
- cursor: fs9.existsSync(getCursorConfigDir()),
3527
- codex: fs9.existsSync(path8.join(home, ".codex"))
3642
+ claude: fs10.existsSync(path9.join(home, ".claude")),
3643
+ cursor: fs10.existsSync(getCursorConfigDir()),
3644
+ codex: fs10.existsSync(path9.join(home, ".codex"))
3528
3645
  };
3529
3646
  }
3530
3647
  function scanLocalState(dir) {
3531
3648
  const items = [];
3532
- const claudeMdPath = path8.join(dir, "CLAUDE.md");
3533
- if (fs9.existsSync(claudeMdPath)) {
3649
+ const claudeMdPath = path9.join(dir, "CLAUDE.md");
3650
+ if (fs10.existsSync(claudeMdPath)) {
3534
3651
  items.push({
3535
3652
  type: "rule",
3536
3653
  platform: "claude",
@@ -3539,10 +3656,10 @@ function scanLocalState(dir) {
3539
3656
  path: claudeMdPath
3540
3657
  });
3541
3658
  }
3542
- const skillsDir = path8.join(dir, ".claude", "skills");
3543
- if (fs9.existsSync(skillsDir)) {
3544
- for (const file of fs9.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
3545
- const filePath = path8.join(skillsDir, file);
3659
+ const skillsDir = path9.join(dir, ".claude", "skills");
3660
+ if (fs10.existsSync(skillsDir)) {
3661
+ for (const file of fs10.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
3662
+ const filePath = path9.join(skillsDir, file);
3546
3663
  items.push({
3547
3664
  type: "skill",
3548
3665
  platform: "claude",
@@ -3552,10 +3669,10 @@ function scanLocalState(dir) {
3552
3669
  });
3553
3670
  }
3554
3671
  }
3555
- const mcpJsonPath = path8.join(dir, ".mcp.json");
3556
- if (fs9.existsSync(mcpJsonPath)) {
3672
+ const mcpJsonPath = path9.join(dir, ".mcp.json");
3673
+ if (fs10.existsSync(mcpJsonPath)) {
3557
3674
  try {
3558
- const mcpJson = JSON.parse(fs9.readFileSync(mcpJsonPath, "utf-8"));
3675
+ const mcpJson = JSON.parse(fs10.readFileSync(mcpJsonPath, "utf-8"));
3559
3676
  if (mcpJson.mcpServers) {
3560
3677
  for (const name of Object.keys(mcpJson.mcpServers)) {
3561
3678
  items.push({
@@ -3571,8 +3688,8 @@ function scanLocalState(dir) {
3571
3688
  warnScanSkip(".mcp.json", error);
3572
3689
  }
3573
3690
  }
3574
- const agentsMdPath = path8.join(dir, "AGENTS.md");
3575
- if (fs9.existsSync(agentsMdPath)) {
3691
+ const agentsMdPath = path9.join(dir, "AGENTS.md");
3692
+ if (fs10.existsSync(agentsMdPath)) {
3576
3693
  items.push({
3577
3694
  type: "rule",
3578
3695
  platform: "codex",
@@ -3581,12 +3698,12 @@ function scanLocalState(dir) {
3581
3698
  path: agentsMdPath
3582
3699
  });
3583
3700
  }
3584
- const codexSkillsDir = path8.join(dir, ".agents", "skills");
3585
- if (fs9.existsSync(codexSkillsDir)) {
3701
+ const codexSkillsDir = path9.join(dir, ".agents", "skills");
3702
+ if (fs10.existsSync(codexSkillsDir)) {
3586
3703
  try {
3587
- for (const name of fs9.readdirSync(codexSkillsDir)) {
3588
- const skillFile = path8.join(codexSkillsDir, name, "SKILL.md");
3589
- if (fs9.existsSync(skillFile)) {
3704
+ for (const name of fs10.readdirSync(codexSkillsDir)) {
3705
+ const skillFile = path9.join(codexSkillsDir, name, "SKILL.md");
3706
+ if (fs10.existsSync(skillFile)) {
3590
3707
  items.push({
3591
3708
  type: "skill",
3592
3709
  platform: "codex",
@@ -3600,8 +3717,8 @@ function scanLocalState(dir) {
3600
3717
  warnScanSkip(".agents/skills", error);
3601
3718
  }
3602
3719
  }
3603
- const cursorrulesPath = path8.join(dir, ".cursorrules");
3604
- if (fs9.existsSync(cursorrulesPath)) {
3720
+ const cursorrulesPath = path9.join(dir, ".cursorrules");
3721
+ if (fs10.existsSync(cursorrulesPath)) {
3605
3722
  items.push({
3606
3723
  type: "rule",
3607
3724
  platform: "cursor",
@@ -3610,10 +3727,10 @@ function scanLocalState(dir) {
3610
3727
  path: cursorrulesPath
3611
3728
  });
3612
3729
  }
3613
- const cursorRulesDir = path8.join(dir, ".cursor", "rules");
3614
- if (fs9.existsSync(cursorRulesDir)) {
3615
- for (const file of fs9.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
3616
- const filePath = path8.join(cursorRulesDir, file);
3730
+ const cursorRulesDir = path9.join(dir, ".cursor", "rules");
3731
+ if (fs10.existsSync(cursorRulesDir)) {
3732
+ for (const file of fs10.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
3733
+ const filePath = path9.join(cursorRulesDir, file);
3617
3734
  items.push({
3618
3735
  type: "rule",
3619
3736
  platform: "cursor",
@@ -3623,12 +3740,12 @@ function scanLocalState(dir) {
3623
3740
  });
3624
3741
  }
3625
3742
  }
3626
- const cursorSkillsDir = path8.join(dir, ".cursor", "skills");
3627
- if (fs9.existsSync(cursorSkillsDir)) {
3743
+ const cursorSkillsDir = path9.join(dir, ".cursor", "skills");
3744
+ if (fs10.existsSync(cursorSkillsDir)) {
3628
3745
  try {
3629
- for (const name of fs9.readdirSync(cursorSkillsDir)) {
3630
- const skillFile = path8.join(cursorSkillsDir, name, "SKILL.md");
3631
- if (fs9.existsSync(skillFile)) {
3746
+ for (const name of fs10.readdirSync(cursorSkillsDir)) {
3747
+ const skillFile = path9.join(cursorSkillsDir, name, "SKILL.md");
3748
+ if (fs10.existsSync(skillFile)) {
3632
3749
  items.push({
3633
3750
  type: "skill",
3634
3751
  platform: "cursor",
@@ -3642,10 +3759,10 @@ function scanLocalState(dir) {
3642
3759
  warnScanSkip(".cursor/skills", error);
3643
3760
  }
3644
3761
  }
3645
- const cursorMcpPath = path8.join(dir, ".cursor", "mcp.json");
3646
- if (fs9.existsSync(cursorMcpPath)) {
3762
+ const cursorMcpPath = path9.join(dir, ".cursor", "mcp.json");
3763
+ if (fs10.existsSync(cursorMcpPath)) {
3647
3764
  try {
3648
- const mcpJson = JSON.parse(fs9.readFileSync(cursorMcpPath, "utf-8"));
3765
+ const mcpJson = JSON.parse(fs10.readFileSync(cursorMcpPath, "utf-8"));
3649
3766
  if (mcpJson.mcpServers) {
3650
3767
  for (const name of Object.keys(mcpJson.mcpServers)) {
3651
3768
  items.push({
@@ -3664,7 +3781,7 @@ function scanLocalState(dir) {
3664
3781
  return items;
3665
3782
  }
3666
3783
  function hashFile(filePath) {
3667
- const text = fs9.readFileSync(filePath, "utf-8");
3784
+ const text = fs10.readFileSync(filePath, "utf-8");
3668
3785
  return crypto2.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
3669
3786
  }
3670
3787
  function hashJson(obj) {
@@ -3677,37 +3794,37 @@ function warnScanSkip(target, error) {
3677
3794
  function getCursorConfigDir() {
3678
3795
  const home = os4.homedir();
3679
3796
  if (process.platform === "darwin") {
3680
- return path8.join(home, "Library", "Application Support", "Cursor");
3797
+ return path9.join(home, "Library", "Application Support", "Cursor");
3681
3798
  }
3682
3799
  if (process.platform === "win32") {
3683
- return path8.join(home, "AppData", "Roaming", "Cursor");
3800
+ return path9.join(home, "AppData", "Roaming", "Cursor");
3684
3801
  }
3685
- return path8.join(home, ".config", "Cursor");
3802
+ return path9.join(home, ".config", "Cursor");
3686
3803
  }
3687
3804
 
3688
3805
  // src/lib/hooks.ts
3689
3806
  init_resolve_caliber();
3690
- import fs10 from "fs";
3691
- import path9 from "path";
3807
+ import fs11 from "fs";
3808
+ import path10 from "path";
3692
3809
  import { execSync as execSync8 } from "child_process";
3693
- var SETTINGS_PATH = path9.join(".claude", "settings.json");
3810
+ var SETTINGS_PATH = path10.join(".claude", "settings.json");
3694
3811
  var REFRESH_TAIL = "refresh --quiet";
3695
3812
  var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
3696
3813
  function getHookCommand() {
3697
3814
  return `${resolveCaliber()} ${REFRESH_TAIL}`;
3698
3815
  }
3699
3816
  function readSettings() {
3700
- if (!fs10.existsSync(SETTINGS_PATH)) return {};
3817
+ if (!fs11.existsSync(SETTINGS_PATH)) return {};
3701
3818
  try {
3702
- return JSON.parse(fs10.readFileSync(SETTINGS_PATH, "utf-8"));
3819
+ return JSON.parse(fs11.readFileSync(SETTINGS_PATH, "utf-8"));
3703
3820
  } catch {
3704
3821
  return {};
3705
3822
  }
3706
3823
  }
3707
3824
  function writeSettings(settings) {
3708
- const dir = path9.dirname(SETTINGS_PATH);
3709
- if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
3710
- fs10.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
3825
+ const dir = path10.dirname(SETTINGS_PATH);
3826
+ if (!fs11.existsSync(dir)) fs11.mkdirSync(dir, { recursive: true });
3827
+ fs11.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
3711
3828
  }
3712
3829
  function findHookIndex(sessionEnd) {
3713
3830
  return sessionEnd.findIndex(
@@ -3773,19 +3890,19 @@ ${PRECOMMIT_END}`;
3773
3890
  function getGitHooksDir() {
3774
3891
  try {
3775
3892
  const gitDir = execSync8("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
3776
- return path9.join(gitDir, "hooks");
3893
+ return path10.join(gitDir, "hooks");
3777
3894
  } catch {
3778
3895
  return null;
3779
3896
  }
3780
3897
  }
3781
3898
  function getPreCommitPath() {
3782
3899
  const hooksDir = getGitHooksDir();
3783
- return hooksDir ? path9.join(hooksDir, "pre-commit") : null;
3900
+ return hooksDir ? path10.join(hooksDir, "pre-commit") : null;
3784
3901
  }
3785
3902
  function isPreCommitHookInstalled() {
3786
3903
  const hookPath = getPreCommitPath();
3787
- if (!hookPath || !fs10.existsSync(hookPath)) return false;
3788
- const content = fs10.readFileSync(hookPath, "utf-8");
3904
+ if (!hookPath || !fs11.existsSync(hookPath)) return false;
3905
+ const content = fs11.readFileSync(hookPath, "utf-8");
3789
3906
  return content.includes(PRECOMMIT_START);
3790
3907
  }
3791
3908
  function installPreCommitHook() {
@@ -3794,42 +3911,42 @@ function installPreCommitHook() {
3794
3911
  }
3795
3912
  const hookPath = getPreCommitPath();
3796
3913
  if (!hookPath) return { installed: false, alreadyInstalled: false };
3797
- const hooksDir = path9.dirname(hookPath);
3798
- if (!fs10.existsSync(hooksDir)) fs10.mkdirSync(hooksDir, { recursive: true });
3914
+ const hooksDir = path10.dirname(hookPath);
3915
+ if (!fs11.existsSync(hooksDir)) fs11.mkdirSync(hooksDir, { recursive: true });
3799
3916
  let content = "";
3800
- if (fs10.existsSync(hookPath)) {
3801
- content = fs10.readFileSync(hookPath, "utf-8");
3917
+ if (fs11.existsSync(hookPath)) {
3918
+ content = fs11.readFileSync(hookPath, "utf-8");
3802
3919
  if (!content.endsWith("\n")) content += "\n";
3803
3920
  content += "\n" + getPrecommitBlock() + "\n";
3804
3921
  } else {
3805
3922
  content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
3806
3923
  }
3807
- fs10.writeFileSync(hookPath, content);
3808
- fs10.chmodSync(hookPath, 493);
3924
+ fs11.writeFileSync(hookPath, content);
3925
+ fs11.chmodSync(hookPath, 493);
3809
3926
  return { installed: true, alreadyInstalled: false };
3810
3927
  }
3811
3928
  function removePreCommitHook() {
3812
3929
  const hookPath = getPreCommitPath();
3813
- if (!hookPath || !fs10.existsSync(hookPath)) {
3930
+ if (!hookPath || !fs11.existsSync(hookPath)) {
3814
3931
  return { removed: false, notFound: true };
3815
3932
  }
3816
- let content = fs10.readFileSync(hookPath, "utf-8");
3933
+ let content = fs11.readFileSync(hookPath, "utf-8");
3817
3934
  if (!content.includes(PRECOMMIT_START)) {
3818
3935
  return { removed: false, notFound: true };
3819
3936
  }
3820
3937
  const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
3821
3938
  content = content.replace(regex, "\n");
3822
3939
  if (content.trim() === "#!/bin/sh" || content.trim() === "") {
3823
- fs10.unlinkSync(hookPath);
3940
+ fs11.unlinkSync(hookPath);
3824
3941
  } else {
3825
- fs10.writeFileSync(hookPath, content);
3942
+ fs11.writeFileSync(hookPath, content);
3826
3943
  }
3827
3944
  return { removed: true, notFound: false };
3828
3945
  }
3829
3946
 
3830
3947
  // src/fingerprint/sources.ts
3831
- import fs11 from "fs";
3832
- import path10 from "path";
3948
+ import fs12 from "fs";
3949
+ import path11 from "path";
3833
3950
 
3834
3951
  // src/scoring/utils.ts
3835
3952
  import { existsSync as existsSync2, readFileSync, readdirSync } from "fs";
@@ -4148,7 +4265,7 @@ var SOURCE_CONTENT_LIMIT = 2e3;
4148
4265
  var README_CONTENT_LIMIT = 1e3;
4149
4266
  var ORIGIN_PRIORITY = { cli: 0, config: 1, workspace: 2 };
4150
4267
  function loadSourcesConfig(dir) {
4151
- const configPath = path10.join(dir, ".caliber", "sources.json");
4268
+ const configPath = path11.join(dir, ".caliber", "sources.json");
4152
4269
  const content = readFileOrNull(configPath);
4153
4270
  if (!content) return [];
4154
4271
  try {
@@ -4166,29 +4283,29 @@ function loadSourcesConfig(dir) {
4166
4283
  }
4167
4284
  }
4168
4285
  function writeSourcesConfig(dir, sources2) {
4169
- const configDir = path10.join(dir, ".caliber");
4170
- if (!fs11.existsSync(configDir)) {
4171
- fs11.mkdirSync(configDir, { recursive: true });
4286
+ const configDir = path11.join(dir, ".caliber");
4287
+ if (!fs12.existsSync(configDir)) {
4288
+ fs12.mkdirSync(configDir, { recursive: true });
4172
4289
  }
4173
- const configPath = path10.join(configDir, "sources.json");
4174
- fs11.writeFileSync(configPath, JSON.stringify({ sources: sources2 }, null, 2) + "\n", "utf-8");
4290
+ const configPath = path11.join(configDir, "sources.json");
4291
+ fs12.writeFileSync(configPath, JSON.stringify({ sources: sources2 }, null, 2) + "\n", "utf-8");
4175
4292
  }
4176
4293
  function detectSourceType(absPath) {
4177
4294
  try {
4178
- return fs11.statSync(absPath).isDirectory() ? "repo" : "file";
4295
+ return fs12.statSync(absPath).isDirectory() ? "repo" : "file";
4179
4296
  } catch {
4180
4297
  return "file";
4181
4298
  }
4182
4299
  }
4183
4300
  function isInsideDir(childPath, parentDir) {
4184
- const relative2 = path10.relative(parentDir, childPath);
4185
- return !relative2.startsWith("..") && !path10.isAbsolute(relative2);
4301
+ const relative2 = path11.relative(parentDir, childPath);
4302
+ return !relative2.startsWith("..") && !path11.isAbsolute(relative2);
4186
4303
  }
4187
4304
  function resolveAllSources(dir, cliSources, workspaces) {
4188
4305
  const seen = /* @__PURE__ */ new Map();
4189
- const projectRoot = path10.resolve(dir);
4306
+ const projectRoot = path11.resolve(dir);
4190
4307
  for (const src of cliSources) {
4191
- const absPath = path10.resolve(dir, src);
4308
+ const absPath = path11.resolve(dir, src);
4192
4309
  if (seen.has(absPath)) continue;
4193
4310
  const type = detectSourceType(absPath);
4194
4311
  seen.set(absPath, {
@@ -4201,12 +4318,12 @@ function resolveAllSources(dir, cliSources, workspaces) {
4201
4318
  for (const cfg of configSources) {
4202
4319
  if (cfg.type === "url") continue;
4203
4320
  if (!cfg.path) continue;
4204
- const absPath = path10.resolve(dir, cfg.path);
4321
+ const absPath = path11.resolve(dir, cfg.path);
4205
4322
  if (seen.has(absPath)) continue;
4206
4323
  seen.set(absPath, { absPath, config: cfg, origin: "config" });
4207
4324
  }
4208
4325
  for (const ws of workspaces) {
4209
- const absPath = path10.resolve(dir, ws);
4326
+ const absPath = path11.resolve(dir, ws);
4210
4327
  if (seen.has(absPath)) continue;
4211
4328
  if (!isInsideDir(absPath, projectRoot)) continue;
4212
4329
  seen.set(absPath, {
@@ -4219,7 +4336,7 @@ function resolveAllSources(dir, cliSources, workspaces) {
4219
4336
  for (const [absPath, resolved] of seen) {
4220
4337
  let stat;
4221
4338
  try {
4222
- stat = fs11.statSync(absPath);
4339
+ stat = fs12.statSync(absPath);
4223
4340
  } catch {
4224
4341
  console.warn(`Source ${resolved.config.path || absPath} not found, skipping`);
4225
4342
  continue;
@@ -4248,13 +4365,13 @@ function collectSourceSummary(resolved, projectDir) {
4248
4365
  if (config.type === "file") {
4249
4366
  return collectFileSummary(resolved, projectDir);
4250
4367
  }
4251
- const summaryPath = path10.join(absPath, ".caliber", "summary.json");
4368
+ const summaryPath = path11.join(absPath, ".caliber", "summary.json");
4252
4369
  const summaryContent = readFileOrNull(summaryPath);
4253
4370
  if (summaryContent) {
4254
4371
  try {
4255
4372
  const published = JSON.parse(summaryContent);
4256
4373
  return {
4257
- name: published.name || path10.basename(absPath),
4374
+ name: published.name || path11.basename(absPath),
4258
4375
  type: "repo",
4259
4376
  role: config.role || published.role || "related-repo",
4260
4377
  description: config.description || published.description || "",
@@ -4274,18 +4391,18 @@ function collectRepoSummary(resolved, projectDir) {
4274
4391
  let topLevelDirs;
4275
4392
  let keyFiles;
4276
4393
  try {
4277
- const entries = fs11.readdirSync(absPath, { withFileTypes: true });
4394
+ const entries = fs12.readdirSync(absPath, { withFileTypes: true });
4278
4395
  topLevelDirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name).slice(0, 20);
4279
4396
  keyFiles = entries.filter((e) => e.isFile() && !e.name.startsWith(".")).map((e) => e.name).slice(0, 15);
4280
4397
  } catch {
4281
4398
  }
4282
- const claudeMdContent = readFileOrNull(path10.join(absPath, "CLAUDE.md"));
4399
+ const claudeMdContent = readFileOrNull(path11.join(absPath, "CLAUDE.md"));
4283
4400
  const existingClaudeMd = claudeMdContent ? claudeMdContent.slice(0, SOURCE_CONTENT_LIMIT) : void 0;
4284
- const readmeContent = readFileOrNull(path10.join(absPath, "README.md"));
4401
+ const readmeContent = readFileOrNull(path11.join(absPath, "README.md"));
4285
4402
  const readmeExcerpt = readmeContent ? readmeContent.slice(0, README_CONTENT_LIMIT) : void 0;
4286
4403
  const gitRemoteUrl = getGitRemoteUrl(absPath);
4287
4404
  return {
4288
- name: packageName || path10.basename(absPath),
4405
+ name: packageName || path11.basename(absPath),
4289
4406
  type: "repo",
4290
4407
  role: config.role || "related-repo",
4291
4408
  description: config.description || "",
@@ -4302,7 +4419,7 @@ function collectFileSummary(resolved, projectDir) {
4302
4419
  const { config, origin, absPath } = resolved;
4303
4420
  const content = readFileOrNull(absPath);
4304
4421
  return {
4305
- name: path10.basename(absPath),
4422
+ name: path11.basename(absPath),
4306
4423
  type: "file",
4307
4424
  role: config.role || "reference-doc",
4308
4425
  description: config.description || content?.slice(0, 100).split("\n")[0] || "",
@@ -4346,15 +4463,15 @@ init_config();
4346
4463
  // src/utils/dependencies.ts
4347
4464
  import { readFileSync as readFileSync2 } from "fs";
4348
4465
  import { join as join2 } from "path";
4349
- function readFileOrNull2(path40) {
4466
+ function readFileOrNull2(path41) {
4350
4467
  try {
4351
- return readFileSync2(path40, "utf-8");
4468
+ return readFileSync2(path41, "utf-8");
4352
4469
  } catch {
4353
4470
  return null;
4354
4471
  }
4355
4472
  }
4356
- function readJsonOrNull(path40) {
4357
- const content = readFileOrNull2(path40);
4473
+ function readJsonOrNull(path41) {
4474
+ const content = readFileOrNull2(path41);
4358
4475
  if (!content) return null;
4359
4476
  try {
4360
4477
  return JSON.parse(content);
@@ -4945,17 +5062,17 @@ import fs19 from "fs";
4945
5062
 
4946
5063
  // src/writers/claude/index.ts
4947
5064
  init_pre_commit_block();
4948
- import fs12 from "fs";
4949
- import path11 from "path";
5065
+ import fs13 from "fs";
5066
+ import path12 from "path";
4950
5067
  function writeClaudeConfig(config) {
4951
5068
  const written = [];
4952
- fs12.writeFileSync("CLAUDE.md", appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(config.claudeMd))));
5069
+ fs13.writeFileSync("CLAUDE.md", appendManagedBlocks(config.claudeMd, "claude"));
4953
5070
  written.push("CLAUDE.md");
4954
5071
  if (config.skills?.length) {
4955
5072
  for (const skill of config.skills) {
4956
- const skillDir = path11.join(".claude", "skills", skill.name);
4957
- if (!fs12.existsSync(skillDir)) fs12.mkdirSync(skillDir, { recursive: true });
4958
- const skillPath = path11.join(skillDir, "SKILL.md");
5073
+ const skillDir = path12.join(".claude", "skills", skill.name);
5074
+ if (!fs13.existsSync(skillDir)) fs13.mkdirSync(skillDir, { recursive: true });
5075
+ const skillPath = path12.join(skillDir, "SKILL.md");
4959
5076
  const frontmatter = [
4960
5077
  "---",
4961
5078
  `name: ${skill.name}`,
@@ -4963,21 +5080,21 @@ function writeClaudeConfig(config) {
4963
5080
  "---",
4964
5081
  ""
4965
5082
  ].join("\n");
4966
- fs12.writeFileSync(skillPath, frontmatter + skill.content);
5083
+ fs13.writeFileSync(skillPath, frontmatter + skill.content);
4967
5084
  written.push(skillPath);
4968
5085
  }
4969
5086
  }
4970
5087
  if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
4971
5088
  let existingServers = {};
4972
5089
  try {
4973
- if (fs12.existsSync(".mcp.json")) {
4974
- const existing = JSON.parse(fs12.readFileSync(".mcp.json", "utf-8"));
5090
+ if (fs13.existsSync(".mcp.json")) {
5091
+ const existing = JSON.parse(fs13.readFileSync(".mcp.json", "utf-8"));
4975
5092
  if (existing.mcpServers) existingServers = existing.mcpServers;
4976
5093
  }
4977
5094
  } catch {
4978
5095
  }
4979
5096
  const mergedServers = { ...existingServers, ...config.mcpServers };
4980
- fs12.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
5097
+ fs13.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
4981
5098
  written.push(".mcp.json");
4982
5099
  }
4983
5100
  return written;
@@ -4985,30 +5102,31 @@ function writeClaudeConfig(config) {
4985
5102
 
4986
5103
  // src/writers/cursor/index.ts
4987
5104
  init_pre_commit_block();
4988
- import fs13 from "fs";
4989
- import path12 from "path";
5105
+ import fs14 from "fs";
5106
+ import path13 from "path";
4990
5107
  function writeCursorConfig(config) {
4991
5108
  const written = [];
4992
5109
  if (config.cursorrules) {
4993
- fs13.writeFileSync(".cursorrules", config.cursorrules);
5110
+ fs14.writeFileSync(".cursorrules", config.cursorrules);
4994
5111
  written.push(".cursorrules");
4995
5112
  }
4996
5113
  const preCommitRule = getCursorPreCommitRule();
4997
5114
  const learningsRule = getCursorLearningsRule();
4998
5115
  const syncRule = getCursorSyncRule();
4999
- const allRules = [...config.rules || [], preCommitRule, learningsRule, syncRule];
5000
- const rulesDir = path12.join(".cursor", "rules");
5001
- if (!fs13.existsSync(rulesDir)) fs13.mkdirSync(rulesDir, { recursive: true });
5116
+ const setupRule = getCursorSetupRule();
5117
+ const allRules = [...config.rules || [], preCommitRule, learningsRule, syncRule, setupRule];
5118
+ const rulesDir = path13.join(".cursor", "rules");
5119
+ if (!fs14.existsSync(rulesDir)) fs14.mkdirSync(rulesDir, { recursive: true });
5002
5120
  for (const rule of allRules) {
5003
- const rulePath = path12.join(rulesDir, rule.filename);
5004
- fs13.writeFileSync(rulePath, rule.content);
5121
+ const rulePath = path13.join(rulesDir, rule.filename);
5122
+ fs14.writeFileSync(rulePath, rule.content);
5005
5123
  written.push(rulePath);
5006
5124
  }
5007
5125
  if (config.skills?.length) {
5008
5126
  for (const skill of config.skills) {
5009
- const skillDir = path12.join(".cursor", "skills", skill.name);
5010
- if (!fs13.existsSync(skillDir)) fs13.mkdirSync(skillDir, { recursive: true });
5011
- const skillPath = path12.join(skillDir, "SKILL.md");
5127
+ const skillDir = path13.join(".cursor", "skills", skill.name);
5128
+ if (!fs14.existsSync(skillDir)) fs14.mkdirSync(skillDir, { recursive: true });
5129
+ const skillPath = path13.join(skillDir, "SKILL.md");
5012
5130
  const frontmatter = [
5013
5131
  "---",
5014
5132
  `name: ${skill.name}`,
@@ -5016,24 +5134,24 @@ function writeCursorConfig(config) {
5016
5134
  "---",
5017
5135
  ""
5018
5136
  ].join("\n");
5019
- fs13.writeFileSync(skillPath, frontmatter + skill.content);
5137
+ fs14.writeFileSync(skillPath, frontmatter + skill.content);
5020
5138
  written.push(skillPath);
5021
5139
  }
5022
5140
  }
5023
5141
  if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
5024
5142
  const cursorDir = ".cursor";
5025
- if (!fs13.existsSync(cursorDir)) fs13.mkdirSync(cursorDir, { recursive: true });
5026
- const mcpPath = path12.join(cursorDir, "mcp.json");
5143
+ if (!fs14.existsSync(cursorDir)) fs14.mkdirSync(cursorDir, { recursive: true });
5144
+ const mcpPath = path13.join(cursorDir, "mcp.json");
5027
5145
  let existingServers = {};
5028
5146
  try {
5029
- if (fs13.existsSync(mcpPath)) {
5030
- const existing = JSON.parse(fs13.readFileSync(mcpPath, "utf-8"));
5147
+ if (fs14.existsSync(mcpPath)) {
5148
+ const existing = JSON.parse(fs14.readFileSync(mcpPath, "utf-8"));
5031
5149
  if (existing.mcpServers) existingServers = existing.mcpServers;
5032
5150
  }
5033
5151
  } catch {
5034
5152
  }
5035
5153
  const mergedServers = { ...existingServers, ...config.mcpServers };
5036
- fs13.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
5154
+ fs14.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
5037
5155
  written.push(mcpPath);
5038
5156
  }
5039
5157
  return written;
@@ -5041,17 +5159,17 @@ function writeCursorConfig(config) {
5041
5159
 
5042
5160
  // src/writers/codex/index.ts
5043
5161
  init_pre_commit_block();
5044
- import fs14 from "fs";
5045
- import path13 from "path";
5162
+ import fs15 from "fs";
5163
+ import path14 from "path";
5046
5164
  function writeCodexConfig(config) {
5047
5165
  const written = [];
5048
- fs14.writeFileSync("AGENTS.md", appendLearningsBlock(appendPreCommitBlock(config.agentsMd)));
5166
+ fs15.writeFileSync("AGENTS.md", appendManagedBlocks(config.agentsMd, "codex"));
5049
5167
  written.push("AGENTS.md");
5050
5168
  if (config.skills?.length) {
5051
5169
  for (const skill of config.skills) {
5052
- const skillDir = path13.join(".agents", "skills", skill.name);
5053
- if (!fs14.existsSync(skillDir)) fs14.mkdirSync(skillDir, { recursive: true });
5054
- const skillPath = path13.join(skillDir, "SKILL.md");
5170
+ const skillDir = path14.join(".agents", "skills", skill.name);
5171
+ if (!fs15.existsSync(skillDir)) fs15.mkdirSync(skillDir, { recursive: true });
5172
+ const skillPath = path14.join(skillDir, "SKILL.md");
5055
5173
  const frontmatter = [
5056
5174
  "---",
5057
5175
  `name: ${skill.name}`,
@@ -5059,7 +5177,7 @@ function writeCodexConfig(config) {
5059
5177
  "---",
5060
5178
  ""
5061
5179
  ].join("\n");
5062
- fs14.writeFileSync(skillPath, frontmatter + skill.content);
5180
+ fs15.writeFileSync(skillPath, frontmatter + skill.content);
5063
5181
  written.push(skillPath);
5064
5182
  }
5065
5183
  }
@@ -5068,20 +5186,20 @@ function writeCodexConfig(config) {
5068
5186
 
5069
5187
  // src/writers/github-copilot/index.ts
5070
5188
  init_pre_commit_block();
5071
- import fs15 from "fs";
5072
- import path14 from "path";
5189
+ import fs16 from "fs";
5190
+ import path15 from "path";
5073
5191
  function writeGithubCopilotConfig(config) {
5074
5192
  const written = [];
5075
5193
  if (config.instructions) {
5076
- fs15.mkdirSync(".github", { recursive: true });
5077
- fs15.writeFileSync(path14.join(".github", "copilot-instructions.md"), appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(config.instructions))));
5194
+ fs16.mkdirSync(".github", { recursive: true });
5195
+ fs16.writeFileSync(path15.join(".github", "copilot-instructions.md"), appendManagedBlocks(config.instructions, "copilot"));
5078
5196
  written.push(".github/copilot-instructions.md");
5079
5197
  }
5080
5198
  if (config.instructionFiles?.length) {
5081
- const instructionsDir = path14.join(".github", "instructions");
5082
- fs15.mkdirSync(instructionsDir, { recursive: true });
5199
+ const instructionsDir = path15.join(".github", "instructions");
5200
+ fs16.mkdirSync(instructionsDir, { recursive: true });
5083
5201
  for (const file of config.instructionFiles) {
5084
- fs15.writeFileSync(path14.join(instructionsDir, file.filename), file.content);
5202
+ fs16.writeFileSync(path15.join(instructionsDir, file.filename), file.content);
5085
5203
  written.push(`.github/instructions/${file.filename}`);
5086
5204
  }
5087
5205
  }
@@ -5089,30 +5207,30 @@ function writeGithubCopilotConfig(config) {
5089
5207
  }
5090
5208
 
5091
5209
  // src/writers/backup.ts
5092
- import fs16 from "fs";
5093
- import path15 from "path";
5210
+ import fs17 from "fs";
5211
+ import path16 from "path";
5094
5212
  function createBackup(files) {
5095
5213
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
5096
- const backupDir = path15.join(BACKUPS_DIR, timestamp);
5214
+ const backupDir = path16.join(BACKUPS_DIR, timestamp);
5097
5215
  for (const file of files) {
5098
- if (!fs16.existsSync(file)) continue;
5099
- const dest = path15.join(backupDir, file);
5100
- const destDir = path15.dirname(dest);
5101
- if (!fs16.existsSync(destDir)) {
5102
- fs16.mkdirSync(destDir, { recursive: true });
5216
+ if (!fs17.existsSync(file)) continue;
5217
+ const dest = path16.join(backupDir, file);
5218
+ const destDir = path16.dirname(dest);
5219
+ if (!fs17.existsSync(destDir)) {
5220
+ fs17.mkdirSync(destDir, { recursive: true });
5103
5221
  }
5104
- fs16.copyFileSync(file, dest);
5222
+ fs17.copyFileSync(file, dest);
5105
5223
  }
5106
5224
  return backupDir;
5107
5225
  }
5108
5226
  function restoreBackup(backupDir, file) {
5109
- const backupFile = path15.join(backupDir, file);
5110
- if (!fs16.existsSync(backupFile)) return false;
5111
- const destDir = path15.dirname(file);
5112
- if (!fs16.existsSync(destDir)) {
5113
- fs16.mkdirSync(destDir, { recursive: true });
5227
+ const backupFile = path16.join(backupDir, file);
5228
+ if (!fs17.existsSync(backupFile)) return false;
5229
+ const destDir = path16.dirname(file);
5230
+ if (!fs17.existsSync(destDir)) {
5231
+ fs17.mkdirSync(destDir, { recursive: true });
5114
5232
  }
5115
- fs16.copyFileSync(backupFile, file);
5233
+ fs17.copyFileSync(backupFile, file);
5116
5234
  return true;
5117
5235
  }
5118
5236
 
@@ -5674,6 +5792,9 @@ async function runInteractiveProviderSetup(options) {
5674
5792
  return config;
5675
5793
  }
5676
5794
 
5795
+ // src/commands/init.ts
5796
+ import confirm2 from "@inquirer/confirm";
5797
+
5677
5798
  // src/scoring/index.ts
5678
5799
  import { existsSync as existsSync7 } from "fs";
5679
5800
  import { join as join9 } from "path";
@@ -6672,8 +6793,8 @@ function detectTargetAgent(dir) {
6672
6793
  const agents = [];
6673
6794
  if (existsSync7(join9(dir, "CLAUDE.md")) || existsSync7(join9(dir, ".claude", "skills"))) agents.push("claude");
6674
6795
  if (existsSync7(join9(dir, ".cursorrules")) || existsSync7(join9(dir, ".cursor", "rules"))) agents.push("cursor");
6675
- if (existsSync7(join9(dir, ".codex")) || existsSync7(join9(dir, ".agents", "skills"))) agents.push("codex");
6676
- if (existsSync7(join9(dir, ".github", "copilot-instructions.md"))) agents.push("github-copilot");
6796
+ if (existsSync7(join9(dir, ".codex")) || existsSync7(join9(dir, ".agents", "skills")) || existsSync7(join9(dir, "AGENTS.md"))) agents.push("codex");
6797
+ if (existsSync7(join9(dir, ".github", "copilot-instructions.md")) || existsSync7(join9(dir, ".github", "instructions"))) agents.push("github-copilot");
6677
6798
  return agents.length > 0 ? agents : ["claude"];
6678
6799
  }
6679
6800
  function computeLocalScore(dir, targetAgent) {
@@ -6927,11 +7048,57 @@ function getMachineId() {
6927
7048
  return machineId;
6928
7049
  }
6929
7050
  var EMAIL_HASH_KEY = "caliber-telemetry-v1";
6930
- function getGitEmailHash() {
7051
+ var PERSONAL_DOMAINS = /* @__PURE__ */ new Set([
7052
+ "gmail.com",
7053
+ "googlemail.com",
7054
+ "outlook.com",
7055
+ "hotmail.com",
7056
+ "live.com",
7057
+ "yahoo.com",
7058
+ "yahoo.co.uk",
7059
+ "icloud.com",
7060
+ "me.com",
7061
+ "mac.com",
7062
+ "aol.com",
7063
+ "protonmail.com",
7064
+ "proton.me",
7065
+ "pm.me",
7066
+ "mail.com",
7067
+ "zoho.com",
7068
+ "yandex.com",
7069
+ "gmx.com",
7070
+ "gmx.net",
7071
+ "fastmail.com",
7072
+ "tutanota.com",
7073
+ "tuta.io"
7074
+ ]);
7075
+ function getGitEmail() {
6931
7076
  try {
6932
7077
  const email = execSync13("git config user.email", { encoding: "utf-8" }).trim();
6933
- if (!email) return void 0;
6934
- return crypto4.createHmac("sha256", EMAIL_HASH_KEY).update(email).digest("hex");
7078
+ return email || void 0;
7079
+ } catch {
7080
+ return void 0;
7081
+ }
7082
+ }
7083
+ function getGitEmailInfo() {
7084
+ const email = getGitEmail();
7085
+ if (!email) return {};
7086
+ const hash = crypto4.createHmac("sha256", EMAIL_HASH_KEY).update(email).digest("hex");
7087
+ let domain;
7088
+ if (email.includes("@")) {
7089
+ const d = email.split("@")[1].toLowerCase();
7090
+ if (!PERSONAL_DOMAINS.has(d)) domain = d;
7091
+ }
7092
+ return { hash, domain };
7093
+ }
7094
+ function getRepoHash() {
7095
+ try {
7096
+ const remote = execSync13("git remote get-url origin || git rev-parse --show-toplevel", {
7097
+ encoding: "utf-8",
7098
+ stdio: ["pipe", "pipe", "pipe"]
7099
+ }).trim();
7100
+ if (!remote) return void 0;
7101
+ return crypto4.createHmac("sha256", EMAIL_HASH_KEY).update(remote).digest("hex").slice(0, 16);
6935
7102
  } catch {
6936
7103
  return void 0;
6937
7104
  }
@@ -6956,6 +7123,7 @@ function markNoticeShown() {
6956
7123
  var POSTHOG_KEY = "phc_XXrV0pSX4s2QVxVoOaeuyXDvtlRwPAjovt1ttMGVMPp";
6957
7124
  var client = null;
6958
7125
  var distinctId = null;
7126
+ var superProperties = {};
6959
7127
  function initTelemetry() {
6960
7128
  if (isTelemetryDisabled()) return;
6961
7129
  const machineId = getMachineId();
@@ -6971,11 +7139,17 @@ function initTelemetry() {
6971
7139
  );
6972
7140
  markNoticeShown();
6973
7141
  }
6974
- const gitEmailHash = getGitEmailHash();
7142
+ const { hash: gitEmailHash, domain: emailDomain } = getGitEmailInfo();
7143
+ const repoHash = getRepoHash();
7144
+ superProperties = {
7145
+ ...repoHash ? { repo_hash: repoHash } : {},
7146
+ ...emailDomain ? { email_domain: emailDomain } : {}
7147
+ };
6975
7148
  client.identify({
6976
7149
  distinctId: machineId,
6977
7150
  properties: {
6978
- ...gitEmailHash ? { git_email_hash: gitEmailHash } : {}
7151
+ ...gitEmailHash ? { git_email_hash: gitEmailHash } : {},
7152
+ ...emailDomain ? { email_domain: emailDomain } : {}
6979
7153
  }
6980
7154
  });
6981
7155
  }
@@ -6984,7 +7158,7 @@ function trackEvent(name, properties) {
6984
7158
  client.capture({
6985
7159
  distinctId,
6986
7160
  event: name,
6987
- properties: properties ?? {}
7161
+ properties: { ...superProperties, ...properties }
6988
7162
  });
6989
7163
  }
6990
7164
  async function flushTelemetry() {
@@ -7033,11 +7207,14 @@ function trackInitSkillsSearch(searched, installedCount) {
7033
7207
  function trackInitScoreRegression(oldScore, newScore) {
7034
7208
  trackEvent("init_score_regression", { old_score: oldScore, new_score: newScore });
7035
7209
  }
7210
+ function trackInitCompleted(path41, score) {
7211
+ trackEvent("init_completed", { path: path41, score });
7212
+ }
7036
7213
  function trackRegenerateCompleted(action, durationMs) {
7037
7214
  trackEvent("regenerate_completed", { action, duration_ms: durationMs });
7038
7215
  }
7039
- function trackRefreshCompleted(changesCount, durationMs) {
7040
- trackEvent("refresh_completed", { changes_count: changesCount, duration_ms: durationMs });
7216
+ function trackRefreshCompleted(changesCount, durationMs, trigger) {
7217
+ trackEvent("refresh_completed", { changes_count: changesCount, duration_ms: durationMs, trigger: trigger ?? "manual" });
7041
7218
  }
7042
7219
  function trackScoreComputed(score, agent) {
7043
7220
  trackEvent("score_computed", { score, agent });
@@ -7051,6 +7228,9 @@ function trackSkillsInstalled(count) {
7051
7228
  function trackUndoExecuted() {
7052
7229
  trackEvent("undo_executed");
7053
7230
  }
7231
+ function trackUninstallExecuted() {
7232
+ trackEvent("uninstall_executed");
7233
+ }
7054
7234
  function trackInitLearnEnabled(enabled) {
7055
7235
  trackEvent("init_learn_enabled", { enabled });
7056
7236
  }
@@ -7985,11 +8165,11 @@ function countIssuePoints(issues) {
7985
8165
  }
7986
8166
  async function scoreAndRefine(setup, dir, sessionHistory, callbacks) {
7987
8167
  const existsCache = /* @__PURE__ */ new Map();
7988
- const cachedExists = (path40) => {
7989
- const cached = existsCache.get(path40);
8168
+ const cachedExists = (path41) => {
8169
+ const cached = existsCache.get(path41);
7990
8170
  if (cached !== void 0) return cached;
7991
- const result = existsSync9(path40);
7992
- existsCache.set(path40, result);
8171
+ const result = existsSync9(path41);
8172
+ existsCache.set(path41, result);
7993
8173
  return result;
7994
8174
  };
7995
8175
  const projectStructure = collectProjectStructure(dir);
@@ -8769,25 +8949,6 @@ async function promptAgent(detected) {
8769
8949
  });
8770
8950
  return selected;
8771
8951
  }
8772
- async function promptLearnInstall(targetAgent) {
8773
- const hasClaude = targetAgent.includes("claude");
8774
- const hasCursor = targetAgent.includes("cursor");
8775
- const agentName = hasClaude && hasCursor ? "Claude and Cursor" : hasClaude ? "Claude" : "Cursor";
8776
- console.log(chalk11.bold(`
8777
- Session Learning
8778
- `));
8779
- console.log(chalk11.dim(` Caliber can learn from your ${agentName} sessions \u2014 when a tool fails`));
8780
- console.log(chalk11.dim(` or you correct a mistake, it captures the lesson so it won't`));
8781
- console.log(chalk11.dim(` happen again. Runs once at session end using the fast model.
8782
- `));
8783
- return select5({
8784
- message: "Enable session learning?",
8785
- choices: [
8786
- { name: "Enable session learning (recommended)", value: true },
8787
- { name: "Skip for now", value: false }
8788
- ]
8789
- });
8790
- }
8791
8952
  async function promptReviewAction(hasSkillResults, hasChanges, staged) {
8792
8953
  if (!hasChanges && !hasSkillResults) return "accept";
8793
8954
  const choices = [];
@@ -9246,10 +9407,9 @@ async function initCommand(options) {
9246
9407
  console.log(chalk14.dim(" Keep your AI agent configs in sync \u2014 automatically."));
9247
9408
  console.log(chalk14.dim(" Works across Claude Code, Cursor, Codex, and GitHub Copilot.\n"));
9248
9409
  console.log(title.bold(" How it works:\n"));
9249
- console.log(chalk14.dim(" 1. Connect Link your LLM provider and select your agents"));
9250
- console.log(chalk14.dim(" 2. Setup Detect stack, install sync hooks & skills"));
9251
- console.log(chalk14.dim(" 3. Generate Audit existing config or generate from scratch"));
9252
- console.log(chalk14.dim(" 4. Finalize Review changes and score your setup\n"));
9410
+ console.log(chalk14.dim(" 1. Connect Auto-detect your LLM provider and agents"));
9411
+ console.log(chalk14.dim(" 2. Build Install sync, scan your project, generate configs"));
9412
+ console.log(chalk14.dim(" 3. Done Review score and start syncing\n"));
9253
9413
  } else {
9254
9414
  console.log(brand.bold("\n CALIBER") + chalk14.dim(" \u2014 setting up continuous sync\n"));
9255
9415
  }
@@ -9259,19 +9419,50 @@ async function initCommand(options) {
9259
9419
  console.log(chalk14.yellow(" Caliber will still generate config files, but they won't be auto-installed.\n"));
9260
9420
  }
9261
9421
  const report = options.debugReport ? new DebugReport() : null;
9262
- console.log(title.bold(" Step 1/4 \u2014 Connect\n"));
9422
+ console.log(title.bold(" Step 1/3 \u2014 Connect\n"));
9263
9423
  let config = loadConfig();
9424
+ if (!config && !options.autoApprove) {
9425
+ if (isClaudeCliAvailable()) {
9426
+ console.log(chalk14.dim(" Detected: Claude Code CLI (uses your Pro/Max/Team subscription)\n"));
9427
+ const useIt = await confirm2({ message: "Use Claude Code as your LLM provider?" });
9428
+ if (useIt) {
9429
+ const autoConfig = { provider: "claude-cli", model: "default" };
9430
+ writeConfigFile(autoConfig);
9431
+ config = autoConfig;
9432
+ }
9433
+ } else if (isCursorAgentAvailable() && isCursorLoggedIn()) {
9434
+ console.log(chalk14.dim(" Detected: Cursor (uses your existing subscription)\n"));
9435
+ const useIt = await confirm2({ message: "Use Cursor as your LLM provider?" });
9436
+ if (useIt) {
9437
+ const autoConfig = { provider: "cursor", model: "sonnet-4.6" };
9438
+ writeConfigFile(autoConfig);
9439
+ config = autoConfig;
9440
+ }
9441
+ }
9442
+ }
9264
9443
  if (!config) {
9265
- console.log(chalk14.dim(" No LLM provider configured yet.\n"));
9266
- await runInteractiveProviderSetup({
9267
- selectMessage: "How do you want to use Caliber? (choose LLM provider)"
9268
- });
9269
- config = loadConfig();
9444
+ if (options.autoApprove) {
9445
+ if (isClaudeCliAvailable()) {
9446
+ const autoConfig = { provider: "claude-cli", model: "default" };
9447
+ writeConfigFile(autoConfig);
9448
+ config = autoConfig;
9449
+ } else if (isCursorAgentAvailable() && isCursorLoggedIn()) {
9450
+ const autoConfig = { provider: "cursor", model: "sonnet-4.6" };
9451
+ writeConfigFile(autoConfig);
9452
+ config = autoConfig;
9453
+ }
9454
+ }
9270
9455
  if (!config) {
9271
- console.log(chalk14.red(" Configuration cancelled or failed.\n"));
9272
- throw new Error("__exit__");
9456
+ console.log(chalk14.dim(" No LLM provider detected.\n"));
9457
+ await runInteractiveProviderSetup({
9458
+ selectMessage: "How do you want to use Caliber? (choose LLM provider)"
9459
+ });
9460
+ config = loadConfig();
9461
+ if (!config) {
9462
+ console.log(chalk14.red(" Configuration cancelled or failed.\n"));
9463
+ throw new Error("__exit__");
9464
+ }
9273
9465
  }
9274
- console.log(chalk14.green(" \u2713 Provider saved\n"));
9275
9466
  }
9276
9467
  trackInitProviderSelected(config.provider, config.model, firstRun);
9277
9468
  const displayModel = getDisplayModel(config);
@@ -9289,21 +9480,28 @@ async function initCommand(options) {
9289
9480
  const agentAutoDetected = !options.agent;
9290
9481
  if (options.agent) {
9291
9482
  targetAgent = options.agent;
9292
- } else if (options.autoApprove) {
9293
- targetAgent = ["claude"];
9294
- log(options.verbose, "Auto-approve: defaulting to claude agent");
9295
9483
  } else {
9296
9484
  const detected = detectAgents(process.cwd());
9297
- targetAgent = await promptAgent(detected.length > 0 ? detected : void 0);
9485
+ if (detected.length > 0 && (options.autoApprove || firstRun)) {
9486
+ targetAgent = detected;
9487
+ console.log(chalk14.dim(` Detected agents: ${detected.join(", ")}
9488
+ `));
9489
+ } else if (detected.length > 0) {
9490
+ console.log(chalk14.dim(` Detected agents: ${detected.join(", ")}
9491
+ `));
9492
+ const useDetected = await confirm2({ message: "Use detected agents?" });
9493
+ targetAgent = useDetected ? detected : await promptAgent();
9494
+ } else {
9495
+ targetAgent = options.autoApprove ? ["claude"] : await promptAgent();
9496
+ }
9298
9497
  }
9299
9498
  console.log(chalk14.dim(` Target: ${targetAgent.join(", ")}
9300
9499
  `));
9301
9500
  trackInitAgentSelected(targetAgent, agentAutoDetected);
9302
- console.log(title.bold(" Step 2/4 \u2014 Setup\n"));
9303
- console.log(chalk14.dim(" Installing sync infrastructure...\n"));
9501
+ console.log(title.bold(" Step 2/3 \u2014 Build\n"));
9304
9502
  const hookResult = installPreCommitHook();
9305
9503
  if (hookResult.installed) {
9306
- console.log(` ${chalk14.green("\u2713")} Pre-commit hook installed \u2014 configs sync on every commit`);
9504
+ console.log(` ${chalk14.green("\u2713")} Pre-commit hook installed`);
9307
9505
  } else if (hookResult.alreadyInstalled) {
9308
9506
  console.log(` ${chalk14.green("\u2713")} Pre-commit hook \u2014 active`);
9309
9507
  }
@@ -9315,21 +9513,18 @@ async function initCommand(options) {
9315
9513
  }
9316
9514
  const skillsWritten = ensureBuiltinSkills2();
9317
9515
  if (skillsWritten.length > 0) {
9318
- console.log(` ${chalk14.green("\u2713")} Agent skills installed \u2014 /setup-caliber, /find-skills, /save-learning`);
9319
- } else {
9320
- console.log(` ${chalk14.green("\u2713")} Agent skills \u2014 already installed`);
9516
+ console.log(` ${chalk14.green("\u2713")} Agent skills installed`);
9517
+ }
9518
+ const hasLearnableAgent = targetAgent.includes("claude") || targetAgent.includes("cursor");
9519
+ if (hasLearnableAgent) {
9520
+ if (targetAgent.includes("claude")) installLearningHooks();
9521
+ if (targetAgent.includes("cursor")) installCursorLearningHooks();
9522
+ console.log(` ${chalk14.green("\u2713")} Session learning enabled`);
9523
+ trackInitLearnEnabled(true);
9321
9524
  }
9322
9525
  console.log("");
9323
- console.log(chalk14.dim(" New team members can run /setup-caliber inside their coding agent"));
9324
- console.log(chalk14.dim(" (Claude Code or Cursor) to get set up automatically.\n"));
9325
9526
  let baselineScore = computeLocalScore(process.cwd(), targetAgent);
9326
- console.log(chalk14.dim(" Current config score:"));
9327
- displayScoreSummary(baselineScore);
9328
- if (options.verbose) {
9329
- for (const c of baselineScore.checks) {
9330
- log(options.verbose, ` ${c.passed ? "\u2713" : "\u2717"} ${c.name}: ${c.earnedPoints}/${c.maxPoints}${c.suggestion ? ` \u2014 ${c.suggestion}` : ""}`);
9331
- }
9332
- }
9527
+ log(options.verbose, `Baseline score: ${baselineScore.score}/100`);
9333
9528
  if (report) {
9334
9529
  report.markStep("Baseline scoring");
9335
9530
  report.addSection("Scoring: Baseline", `**Score**: ${baselineScore.score}/100
@@ -9350,34 +9545,23 @@ async function initCommand(options) {
9350
9545
  ]);
9351
9546
  const passingCount = baselineScore.checks.filter((c) => c.passed).length;
9352
9547
  const failingCount = baselineScore.checks.filter((c) => !c.passed).length;
9548
+ trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
9353
9549
  let skipGeneration = false;
9354
- if (hasExistingConfig && baselineScore.score === 100) {
9355
- trackInitScoreComputed(baselineScore.score, passingCount, failingCount, true);
9356
- console.log(chalk14.bold.green("\n Your config is already optimal.\n"));
9357
- skipGeneration = !options.force;
9550
+ if (hasExistingConfig && baselineScore.score === 100 && !options.force) {
9551
+ skipGeneration = true;
9358
9552
  } else if (hasExistingConfig && !options.force && !options.autoApprove) {
9359
- trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
9360
- console.log(chalk14.dim("\n Sync infrastructure is ready. Caliber can also audit your existing"));
9361
- console.log(chalk14.dim(" configs and improve them using AI.\n"));
9362
- const auditAnswer = await promptInput(" Audit and improve your existing config? (Y/n) ");
9363
- skipGeneration = auditAnswer.toLowerCase() === "n";
9364
- } else if (!hasExistingConfig && !options.force && !options.autoApprove) {
9365
- trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
9366
- console.log(chalk14.dim("\n Sync infrastructure is ready. Caliber can also generate tailored"));
9367
- console.log(chalk14.dim(" CLAUDE.md, Cursor rules, and Codex configs for your project.\n"));
9368
- const generateAnswer = await promptInput(" Generate agent configs? (Y/n) ");
9369
- skipGeneration = generateAnswer.toLowerCase() === "n";
9370
- } else {
9371
- trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
9553
+ console.log(chalk14.dim(` Config score: ${baselineScore.score}/100 \u2014 Caliber can improve this.
9554
+ `));
9555
+ const improveAnswer = await confirm2({ message: "Improve your existing configs?" });
9556
+ skipGeneration = !improveAnswer;
9372
9557
  }
9373
9558
  if (skipGeneration) {
9374
9559
  const {
9375
- appendPreCommitBlock: appendPreCommitBlock2,
9376
- appendLearningsBlock: appendLearningsBlock2,
9377
- appendSyncBlock: appendSyncBlock2,
9560
+ appendManagedBlocks: appendManagedBlocks2,
9378
9561
  getCursorPreCommitRule: getCursorPreCommitRule2,
9379
9562
  getCursorLearningsRule: getCursorLearningsRule2,
9380
- getCursorSyncRule: getCursorSyncRule2
9563
+ getCursorSyncRule: getCursorSyncRule2,
9564
+ getCursorSetupRule: getCursorSetupRule2
9381
9565
  } = await Promise.resolve().then(() => (init_pre_commit_block(), pre_commit_block_exports));
9382
9566
  const claudeMdPath = "CLAUDE.md";
9383
9567
  let claudeContent = "";
@@ -9389,7 +9573,7 @@ async function initCommand(options) {
9389
9573
  claudeContent = `# ${path26.basename(process.cwd())}
9390
9574
  `;
9391
9575
  }
9392
- const updatedClaude = appendSyncBlock2(appendLearningsBlock2(appendPreCommitBlock2(claudeContent)));
9576
+ const updatedClaude = appendManagedBlocks2(claudeContent, "claude");
9393
9577
  if (updatedClaude !== claudeContent || !fs33.existsSync(claudeMdPath)) {
9394
9578
  fs33.writeFileSync(claudeMdPath, updatedClaude);
9395
9579
  console.log(` ${chalk14.green("\u2713")} CLAUDE.md \u2014 added Caliber sync instructions`);
@@ -9397,7 +9581,7 @@ async function initCommand(options) {
9397
9581
  if (targetAgent.includes("cursor")) {
9398
9582
  const rulesDir = path26.join(".cursor", "rules");
9399
9583
  if (!fs33.existsSync(rulesDir)) fs33.mkdirSync(rulesDir, { recursive: true });
9400
- for (const rule of [getCursorPreCommitRule2(), getCursorLearningsRule2(), getCursorSyncRule2()]) {
9584
+ for (const rule of [getCursorPreCommitRule2(), getCursorLearningsRule2(), getCursorSyncRule2(), getCursorSetupRule2()]) {
9401
9585
  fs33.writeFileSync(path26.join(rulesDir, rule.filename), rule.content);
9402
9586
  }
9403
9587
  console.log(` ${chalk14.green("\u2713")} Cursor rules \u2014 added Caliber sync rules`);
@@ -9414,7 +9598,7 @@ async function initCommand(options) {
9414
9598
  copilotContent = `# ${path26.basename(process.cwd())}
9415
9599
  `;
9416
9600
  }
9417
- const updatedCopilot = appendSyncBlock2(appendLearningsBlock2(appendPreCommitBlock2(copilotContent)));
9601
+ const updatedCopilot = appendManagedBlocks2(copilotContent, "copilot");
9418
9602
  if (updatedCopilot !== copilotContent) {
9419
9603
  fs33.writeFileSync(copilotPath, updatedCopilot);
9420
9604
  console.log(` ${chalk14.green("\u2713")} Copilot instructions \u2014 added Caliber sync instructions`);
@@ -9426,6 +9610,7 @@ async function initCommand(options) {
9426
9610
  lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString(),
9427
9611
  targetAgent
9428
9612
  });
9613
+ trackInitCompleted("sync-only", baselineScore.score);
9429
9614
  console.log(chalk14.bold.green("\n Caliber sync is set up!\n"));
9430
9615
  console.log(chalk14.dim(" Your agent configs will sync automatically on every commit."));
9431
9616
  console.log(chalk14.dim(" Run ") + title(`${bin} init --force`) + chalk14.dim(" anytime to generate or improve configs.\n"));
@@ -9433,7 +9618,6 @@ async function initCommand(options) {
9433
9618
  }
9434
9619
  const allFailingChecks = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
9435
9620
  const llmFixableChecks = allFailingChecks.filter((c) => !NON_LLM_CHECKS.has(c.id));
9436
- console.log(title.bold("\n Step 3/4 \u2014 Generate\n"));
9437
9621
  const genModelInfo = fastModel ? ` Using ${displayModel} for docs, ${fastModel} for skills` : ` Using ${displayModel}`;
9438
9622
  console.log(chalk14.dim(genModelInfo + "\n"));
9439
9623
  if (report) report.markStep("Generation");
@@ -9632,7 +9816,7 @@ async function initCommand(options) {
9632
9816
  report.addJson("Generation: Parsed Config", generatedSetup);
9633
9817
  }
9634
9818
  log(options.verbose, `Generation completed: ${elapsedMs}ms, stopReason: ${genStopReason || "end_turn"}`);
9635
- console.log(title.bold(" Step 4/4 \u2014 Finalize\n"));
9819
+ console.log(title.bold(" Step 3/3 \u2014 Done\n"));
9636
9820
  const setupFiles = collectSetupFiles(generatedSetup, targetAgent);
9637
9821
  const staged = stageFiles(setupFiles, process.cwd());
9638
9822
  const totalChanges = staged.newFiles + staged.modifiedFiles;
@@ -9776,6 +9960,7 @@ ${agentRefs.join(" ")}
9776
9960
  ` + afterScore.checks.map((c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.earnedPoints} | ${c.maxPoints} |`).join("\n"));
9777
9961
  }
9778
9962
  recordScore(afterScore, "init");
9963
+ trackInitCompleted("full-generation", afterScore.score);
9779
9964
  displayScoreDelta(baselineScore, afterScore);
9780
9965
  if (options.verbose) {
9781
9966
  log(options.verbose, `Final score: ${afterScore.score}/100`);
@@ -9793,64 +9978,26 @@ ${agentRefs.join(" ")}
9793
9978
  communitySkillsInstalled = selected.length;
9794
9979
  }
9795
9980
  }
9796
- console.log("");
9797
- console.log(` ${chalk14.green("\u2713")} Docs auto-refresh ${chalk14.dim(`agents run ${resolveCaliber()} refresh before commits`)}`);
9798
9981
  trackInitHookSelected("config-instructions");
9799
- const hasLearnableAgent = targetAgent.includes("claude") || targetAgent.includes("cursor");
9800
- let enableLearn = false;
9801
- if (hasLearnableAgent) {
9802
- if (!options.autoApprove) {
9803
- enableLearn = await promptLearnInstall(targetAgent);
9804
- trackInitLearnEnabled(enableLearn);
9805
- if (enableLearn) {
9806
- if (targetAgent.includes("claude")) {
9807
- const r = installLearningHooks();
9808
- if (r.installed) console.log(` ${chalk14.green("\u2713")} Learning hooks installed for Claude Code`);
9809
- else if (r.alreadyInstalled) console.log(chalk14.dim(" Claude Code learning hooks already installed"));
9810
- }
9811
- if (targetAgent.includes("cursor")) {
9812
- const r = installCursorLearningHooks();
9813
- if (r.installed) console.log(` ${chalk14.green("\u2713")} Learning hooks installed for Cursor`);
9814
- else if (r.alreadyInstalled) console.log(chalk14.dim(" Cursor learning hooks already installed"));
9815
- }
9816
- console.log(chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} learn status`) + chalk14.dim(" to see insights"));
9817
- } else {
9818
- console.log(chalk14.dim(" Skipped. Run ") + chalk14.hex("#83D1EB")(`${bin} learn install`) + chalk14.dim(" later to enable."));
9819
- }
9820
- } else {
9821
- enableLearn = true;
9822
- if (targetAgent.includes("claude")) installLearningHooks();
9823
- if (targetAgent.includes("cursor")) installCursorLearningHooks();
9824
- }
9825
- }
9826
- console.log(chalk14.bold.green("\n Caliber is set up!"));
9827
- console.log(chalk14.dim(" Your AI agent configs will now stay in sync with your codebase automatically."));
9828
- console.log(chalk14.dim(" Every commit refreshes configs for all your agents. All changes are backed up.\n"));
9829
9982
  const done = chalk14.green("\u2713");
9830
- const skip = chalk14.dim("\u2013");
9831
- console.log(chalk14.bold(" What was configured:\n"));
9832
- console.log(` ${done} Continuous sync ${chalk14.dim("pre-commit hook keeps all agent configs in sync")}`);
9833
- console.log(` ${done} Config generated ${title(`${bin} score`)} ${chalk14.dim("for full breakdown")}`);
9834
- console.log(` ${done} Agent skills ${chalk14.dim("/setup-caliber for new team members")}`);
9983
+ console.log(chalk14.bold.green("\n Caliber is set up!\n"));
9984
+ console.log(chalk14.bold(" What's configured:\n"));
9985
+ console.log(` ${done} Continuous sync ${chalk14.dim("pre-commit hook keeps all agent configs in sync")}`);
9986
+ console.log(` ${done} Config generated ${chalk14.dim(`score: ${afterScore.score}/100`)}`);
9987
+ console.log(` ${done} Agent skills ${chalk14.dim("/setup-caliber for new team members")}`);
9835
9988
  if (hasLearnableAgent) {
9836
- if (enableLearn) {
9837
- console.log(` ${done} Session learning ${chalk14.dim("agent learns from your feedback")}`);
9838
- } else {
9839
- console.log(` ${skip} Session learning ${title(`${bin} learn install`)} ${chalk14.dim("to enable later")}`);
9840
- }
9989
+ console.log(` ${done} Session learning ${chalk14.dim("learns from your corrections")}`);
9841
9990
  }
9842
9991
  if (communitySkillsInstalled > 0) {
9843
- console.log(` ${done} Community skills ${chalk14.dim(`${communitySkillsInstalled} skill${communitySkillsInstalled > 1 ? "s" : ""} installed for your stack`)}`);
9844
- } else if (skillSearchResult.results.length > 0) {
9845
- console.log(` ${skip} Community skills ${chalk14.dim("available but skipped")}`);
9992
+ console.log(` ${done} Community skills ${chalk14.dim(`${communitySkillsInstalled} installed for your stack`)}`);
9846
9993
  }
9847
9994
  console.log(chalk14.bold("\n What happens next:\n"));
9848
- console.log(chalk14.dim(" Every commit will automatically sync your agent configs."));
9849
- console.log(chalk14.dim(" New team members can run /setup-caliber to get set up instantly.\n"));
9850
- console.log(chalk14.bold(" Explore:\n"));
9851
- console.log(` ${title(`${bin} score`)} Full scoring breakdown with improvement tips`);
9852
- console.log(` ${title(`${bin} skills`)} Find community skills for your stack`);
9853
- console.log(` ${title(`${bin} undo`)} Revert all changes from this run`);
9995
+ console.log(chalk14.dim(" Every commit syncs your agent configs automatically."));
9996
+ console.log(chalk14.dim(" New team members run /setup-caliber to get set up instantly.\n"));
9997
+ console.log(` ${title(`${bin} score`)} Full scoring breakdown`);
9998
+ console.log(` ${title(`${bin} skills`)} Find community skills`);
9999
+ console.log(` ${title(`${bin} undo`)} Revert changes`);
10000
+ console.log(` ${title(`${bin} uninstall`)} Remove Caliber completely`);
9854
10001
  console.log("");
9855
10002
  if (options.showTokens) {
9856
10003
  displayTokenUsage();
@@ -10193,14 +10340,24 @@ var MAX_DIFF_BYTES = 1e5;
10193
10340
  var DOC_PATTERNS = [
10194
10341
  "CLAUDE.md",
10195
10342
  "README.md",
10343
+ "AGENTS.md",
10196
10344
  ".cursorrules",
10197
10345
  ".cursor/rules/",
10346
+ ".cursor/skills/",
10198
10347
  ".claude/skills/",
10348
+ ".agents/skills/",
10349
+ ".github/copilot-instructions.md",
10350
+ ".github/instructions/",
10199
10351
  "CALIBER_LEARNINGS.md"
10200
10352
  ];
10201
10353
  function excludeArgs() {
10202
10354
  return DOC_PATTERNS.flatMap((p) => ["--", `:!${p}`]);
10203
10355
  }
10356
+ function truncateAtLine(text, maxBytes) {
10357
+ if (text.length <= maxBytes) return text;
10358
+ const cut = text.lastIndexOf("\n", maxBytes);
10359
+ return cut > 0 ? text.slice(0, cut) : text.slice(0, maxBytes);
10360
+ }
10204
10361
  function safeExec(cmd) {
10205
10362
  try {
10206
10363
  return execSync15(cmd, {
@@ -10246,9 +10403,9 @@ function collectDiff(lastSha) {
10246
10403
  const totalSize = committedDiff.length + stagedDiff.length + unstagedDiff.length;
10247
10404
  if (totalSize > MAX_DIFF_BYTES) {
10248
10405
  const ratio = MAX_DIFF_BYTES / totalSize;
10249
- committedDiff = committedDiff.slice(0, Math.floor(committedDiff.length * ratio));
10250
- stagedDiff = stagedDiff.slice(0, Math.floor(stagedDiff.length * ratio));
10251
- unstagedDiff = unstagedDiff.slice(0, Math.floor(unstagedDiff.length * ratio));
10406
+ committedDiff = truncateAtLine(committedDiff, Math.floor(committedDiff.length * ratio));
10407
+ stagedDiff = truncateAtLine(stagedDiff, Math.floor(stagedDiff.length * ratio));
10408
+ unstagedDiff = truncateAtLine(unstagedDiff, Math.floor(unstagedDiff.length * ratio));
10252
10409
  }
10253
10410
  const hasChanges = !!(committedDiff || stagedDiff || unstagedDiff || changedFiles.length);
10254
10411
  const parts = [];
@@ -10267,13 +10424,17 @@ import path28 from "path";
10267
10424
  function writeRefreshDocs(docs) {
10268
10425
  const written = [];
10269
10426
  if (docs.claudeMd) {
10270
- fs36.writeFileSync("CLAUDE.md", appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(docs.claudeMd))));
10427
+ fs36.writeFileSync("CLAUDE.md", appendManagedBlocks(docs.claudeMd, "claude"));
10271
10428
  written.push("CLAUDE.md");
10272
10429
  }
10273
10430
  if (docs.readmeMd) {
10274
10431
  fs36.writeFileSync("README.md", docs.readmeMd);
10275
10432
  written.push("README.md");
10276
10433
  }
10434
+ if (docs.agentsMd) {
10435
+ fs36.writeFileSync("AGENTS.md", appendManagedBlocks(docs.agentsMd, "codex"));
10436
+ written.push("AGENTS.md");
10437
+ }
10277
10438
  if (docs.cursorrules) {
10278
10439
  fs36.writeFileSync(".cursorrules", docs.cursorrules);
10279
10440
  written.push(".cursorrules");
@@ -10296,7 +10457,7 @@ function writeRefreshDocs(docs) {
10296
10457
  }
10297
10458
  if (docs.copilotInstructions) {
10298
10459
  fs36.mkdirSync(".github", { recursive: true });
10299
- fs36.writeFileSync(path28.join(".github", "copilot-instructions.md"), appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(docs.copilotInstructions))));
10460
+ fs36.writeFileSync(path28.join(".github", "copilot-instructions.md"), appendManagedBlocks(docs.copilotInstructions, "copilot"));
10300
10461
  written.push(".github/copilot-instructions.md");
10301
10462
  }
10302
10463
  if (docs.copilotInstructionFiles) {
@@ -10315,10 +10476,18 @@ init_config();
10315
10476
  async function refreshDocs(diff, existingDocs, projectContext, learnedSection, sources2) {
10316
10477
  const prompt = buildRefreshPrompt(diff, existingDocs, projectContext, learnedSection, sources2);
10317
10478
  const fastModel = getFastModel();
10479
+ const docCount = [
10480
+ existingDocs.claudeMd,
10481
+ existingDocs.readmeMd,
10482
+ existingDocs.agentsMd,
10483
+ existingDocs.cursorrules,
10484
+ existingDocs.copilotInstructions
10485
+ ].filter(Boolean).length + (existingDocs.cursorRules?.length ?? 0) + (existingDocs.claudeSkills?.length ?? 0);
10486
+ const maxTokens = Math.min(32768, Math.max(8192, docCount * 4096));
10318
10487
  const raw = await llmCall({
10319
10488
  system: REFRESH_SYSTEM_PROMPT,
10320
10489
  prompt,
10321
- maxTokens: 16384,
10490
+ maxTokens,
10322
10491
  ...fastModel ? { model: fastModel } : {}
10323
10492
  });
10324
10493
  return parseJsonResponse(raw);
@@ -10377,6 +10546,21 @@ Changed files: ${diff.changedFiles.join(", ")}`);
10377
10546
  parts.push(rule.content);
10378
10547
  }
10379
10548
  }
10549
+ if (existingDocs.agentsMd) {
10550
+ parts.push("\n[AGENTS.md]");
10551
+ parts.push(existingDocs.agentsMd);
10552
+ }
10553
+ if (existingDocs.copilotInstructions) {
10554
+ parts.push("\n[.github/copilot-instructions.md]");
10555
+ parts.push(existingDocs.copilotInstructions);
10556
+ }
10557
+ if (existingDocs.copilotInstructionFiles?.length) {
10558
+ for (const file of existingDocs.copilotInstructionFiles) {
10559
+ parts.push(`
10560
+ [.github/instructions/${file.filename}]`);
10561
+ parts.push(file.content);
10562
+ }
10563
+ }
10380
10564
  if (learnedSection) {
10381
10565
  parts.push("\n--- Learned Patterns (from session learning) ---");
10382
10566
  parts.push("Consider these accumulated learnings when deciding what to update:");
@@ -10699,7 +10883,8 @@ async function refreshSingleRepo(repoDir, options) {
10699
10883
  }
10700
10884
  }
10701
10885
  const written = writeRefreshDocs(response.updatedDocs);
10702
- trackRefreshCompleted(written.length, Date.now());
10886
+ const trigger = quiet ? "hook" : "manual";
10887
+ trackRefreshCompleted(written.length, Date.now(), trigger);
10703
10888
  const postScore = computeLocalScore(repoDir, targetAgent);
10704
10889
  if (postScore.score < preScore.score) {
10705
10890
  for (const [filePath, content] of preRefreshContents) {
@@ -12403,10 +12588,199 @@ async function publishCommand() {
12403
12588
  }
12404
12589
  }
12405
12590
 
12591
+ // src/commands/bootstrap.ts
12592
+ init_builtin_skills();
12593
+ import fs47 from "fs";
12594
+ import chalk27 from "chalk";
12595
+ var PLATFORM_SKILL_DIRS = {
12596
+ claude: ".claude/skills",
12597
+ cursor: ".cursor/skills",
12598
+ codex: ".agents/skills"
12599
+ };
12600
+ async function bootstrapCommand() {
12601
+ const platforms = detectPlatforms();
12602
+ const detected = [];
12603
+ if (platforms.claude) detected.push("claude");
12604
+ if (platforms.cursor) detected.push("cursor");
12605
+ if (platforms.codex) detected.push("codex");
12606
+ if (detected.length === 0) detected.push("claude");
12607
+ const written = [];
12608
+ for (const platform of detected) {
12609
+ const skillsDir = PLATFORM_SKILL_DIRS[platform];
12610
+ if (!skillsDir) continue;
12611
+ for (const skill of BUILTIN_SKILLS) {
12612
+ const skillDir = `${skillsDir}/${skill.name}`;
12613
+ const skillPath = `${skillDir}/SKILL.md`;
12614
+ fs47.mkdirSync(skillDir, { recursive: true });
12615
+ fs47.writeFileSync(skillPath, buildSkillContent(skill));
12616
+ written.push(skillPath);
12617
+ }
12618
+ }
12619
+ if (written.length === 0) {
12620
+ console.log(chalk27.yellow("No skills were written."));
12621
+ return;
12622
+ }
12623
+ console.log(chalk27.bold.green("\n Caliber skills installed!\n"));
12624
+ for (const file of written) {
12625
+ console.log(` ${chalk27.green("\u2713")} ${file}`);
12626
+ }
12627
+ console.log(chalk27.dim("\n Your agent can now run /setup-caliber to complete the setup."));
12628
+ console.log(chalk27.dim(' Just tell your agent: "Run /setup-caliber"\n'));
12629
+ }
12630
+
12631
+ // src/commands/uninstall.ts
12632
+ import fs48 from "fs";
12633
+ import path38 from "path";
12634
+ import chalk28 from "chalk";
12635
+ import confirm3 from "@inquirer/confirm";
12636
+ init_pre_commit_block();
12637
+ init_builtin_skills();
12638
+ init_config();
12639
+ var MANAGED_DOC_FILES = [
12640
+ "CLAUDE.md",
12641
+ path38.join(".github", "copilot-instructions.md"),
12642
+ "AGENTS.md"
12643
+ ];
12644
+ var SKILL_DIRS = PLATFORM_CONFIGS.map((c) => c.skillsDir);
12645
+ var CURSOR_RULES_DIR = path38.join(".cursor", "rules");
12646
+ function removeCaliberCursorRules() {
12647
+ const removed = [];
12648
+ if (!fs48.existsSync(CURSOR_RULES_DIR)) return removed;
12649
+ for (const file of fs48.readdirSync(CURSOR_RULES_DIR)) {
12650
+ if (file.startsWith("caliber-") && file.endsWith(".mdc")) {
12651
+ const fullPath = path38.join(CURSOR_RULES_DIR, file);
12652
+ fs48.unlinkSync(fullPath);
12653
+ removed.push(fullPath);
12654
+ }
12655
+ }
12656
+ return removed;
12657
+ }
12658
+ function removeBuiltinSkills() {
12659
+ const removed = [];
12660
+ for (const skillsDir of SKILL_DIRS) {
12661
+ if (!fs48.existsSync(skillsDir)) continue;
12662
+ for (const name of BUILTIN_SKILL_NAMES) {
12663
+ const skillDir = path38.join(skillsDir, name);
12664
+ if (fs48.existsSync(skillDir)) {
12665
+ fs48.rmSync(skillDir, { recursive: true });
12666
+ removed.push(skillDir);
12667
+ }
12668
+ }
12669
+ }
12670
+ return removed;
12671
+ }
12672
+ function stripManagedBlocksFromFiles() {
12673
+ const modified = [];
12674
+ for (const filePath of MANAGED_DOC_FILES) {
12675
+ if (!fs48.existsSync(filePath)) continue;
12676
+ const original = fs48.readFileSync(filePath, "utf-8");
12677
+ const stripped = stripManagedBlocks(original);
12678
+ if (stripped !== original) {
12679
+ const trimmed = stripped.trim();
12680
+ if (!trimmed || /^#\s*\S*$/.test(trimmed)) {
12681
+ fs48.unlinkSync(filePath);
12682
+ } else {
12683
+ fs48.writeFileSync(filePath, stripped);
12684
+ }
12685
+ modified.push(filePath);
12686
+ }
12687
+ }
12688
+ return modified;
12689
+ }
12690
+ function removeDirectory(dir) {
12691
+ if (!fs48.existsSync(dir)) return false;
12692
+ fs48.rmSync(dir, { recursive: true });
12693
+ return true;
12694
+ }
12695
+ async function uninstallCommand(options) {
12696
+ console.log(chalk28.bold("\n Caliber Uninstall\n"));
12697
+ console.log(chalk28.dim(" This will remove all Caliber resources from this project:\n"));
12698
+ console.log(chalk28.dim(" \u2022 Pre-commit hook"));
12699
+ console.log(chalk28.dim(" \u2022 Session learning hooks"));
12700
+ console.log(chalk28.dim(" \u2022 Managed blocks in CLAUDE.md, AGENTS.md, copilot-instructions.md"));
12701
+ console.log(chalk28.dim(" \u2022 Cursor rules (caliber-*.mdc)"));
12702
+ console.log(chalk28.dim(" \u2022 Built-in skills (setup-caliber, find-skills, save-learning)"));
12703
+ console.log(chalk28.dim(" \u2022 CALIBER_LEARNINGS.md"));
12704
+ console.log(chalk28.dim(" \u2022 .caliber/ directory (backups, cache, state)\n"));
12705
+ if (!options.force) {
12706
+ const proceed = await confirm3({ message: "Continue with uninstall?" });
12707
+ if (!proceed) {
12708
+ console.log(chalk28.dim("\n Cancelled.\n"));
12709
+ return;
12710
+ }
12711
+ }
12712
+ console.log("");
12713
+ const actions = [];
12714
+ const hookResult = removePreCommitHook();
12715
+ if (hookResult.removed) {
12716
+ console.log(` ${chalk28.red("\u2717")} Pre-commit hook removed`);
12717
+ actions.push("pre-commit hook");
12718
+ }
12719
+ const learnResult = removeLearningHooks();
12720
+ if (learnResult.removed) {
12721
+ console.log(` ${chalk28.red("\u2717")} Claude Code learning hooks removed`);
12722
+ actions.push("claude learning hooks");
12723
+ }
12724
+ const cursorLearnResult = removeCursorLearningHooks();
12725
+ if (cursorLearnResult.removed) {
12726
+ console.log(` ${chalk28.red("\u2717")} Cursor learning hooks removed`);
12727
+ actions.push("cursor learning hooks");
12728
+ }
12729
+ const strippedFiles = stripManagedBlocksFromFiles();
12730
+ for (const file of strippedFiles) {
12731
+ console.log(` ${chalk28.yellow("~")} ${file} \u2014 managed blocks removed`);
12732
+ actions.push(file);
12733
+ }
12734
+ const removedRules = removeCaliberCursorRules();
12735
+ for (const rule of removedRules) {
12736
+ console.log(` ${chalk28.red("\u2717")} ${rule}`);
12737
+ }
12738
+ if (removedRules.length > 0) actions.push("cursor rules");
12739
+ const removedSkills = removeBuiltinSkills();
12740
+ for (const skill of removedSkills) {
12741
+ console.log(` ${chalk28.red("\u2717")} ${skill}/`);
12742
+ }
12743
+ if (removedSkills.length > 0) actions.push("builtin skills");
12744
+ if (fs48.existsSync("CALIBER_LEARNINGS.md")) {
12745
+ fs48.unlinkSync("CALIBER_LEARNINGS.md");
12746
+ console.log(` ${chalk28.red("\u2717")} CALIBER_LEARNINGS.md`);
12747
+ actions.push("learnings file");
12748
+ }
12749
+ if (removeDirectory(CALIBER_DIR)) {
12750
+ console.log(` ${chalk28.red("\u2717")} .caliber/ directory`);
12751
+ actions.push(".caliber directory");
12752
+ }
12753
+ if (actions.length === 0) {
12754
+ console.log(chalk28.dim(" Nothing to remove \u2014 Caliber is not installed in this project.\n"));
12755
+ return;
12756
+ }
12757
+ trackUninstallExecuted();
12758
+ const configPath = getConfigFilePath();
12759
+ if (fs48.existsSync(configPath)) {
12760
+ console.log("");
12761
+ const removeConfig = options.force || await confirm3({
12762
+ message: `Remove global config (~/.caliber/config.json)? This affects all projects.`
12763
+ });
12764
+ if (removeConfig) {
12765
+ fs48.unlinkSync(configPath);
12766
+ console.log(` ${chalk28.red("\u2717")} ${configPath}`);
12767
+ const configDir = path38.dirname(configPath);
12768
+ try {
12769
+ const remaining = fs48.readdirSync(configDir);
12770
+ if (remaining.length === 0) fs48.rmdirSync(configDir);
12771
+ } catch {
12772
+ }
12773
+ }
12774
+ }
12775
+ console.log(chalk28.bold.green(`
12776
+ Caliber has been removed from this project.`));
12777
+ console.log(chalk28.dim(" Your code is untouched \u2014 only Caliber config files were removed.\n"));
12778
+ }
12779
+
12406
12780
  // src/cli.ts
12407
- var __dirname = path38.dirname(fileURLToPath(import.meta.url));
12781
+ var __dirname = path39.dirname(fileURLToPath(import.meta.url));
12408
12782
  var pkg = JSON.parse(
12409
- fs47.readFileSync(path38.resolve(__dirname, "..", "package.json"), "utf-8")
12783
+ fs49.readFileSync(path39.resolve(__dirname, "..", "package.json"), "utf-8")
12410
12784
  );
12411
12785
  var program = new Command();
12412
12786
  var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
@@ -12414,9 +12788,11 @@ program.name(process.env.CALIBER_LOCAL ? "caloc" : "caliber").description("AI co
12414
12788
  function tracked(commandName, handler) {
12415
12789
  const wrapper = async (...args) => {
12416
12790
  const start = Date.now();
12791
+ const isCI = !!(process.env.CI || process.env.GITHUB_ACTIONS);
12417
12792
  trackEvent("command_started", {
12418
12793
  command: commandName,
12419
- cli_version: pkg.version
12794
+ cli_version: pkg.version,
12795
+ is_ci: isCI
12420
12796
  });
12421
12797
  try {
12422
12798
  await handler(...args);
@@ -12468,7 +12844,9 @@ function parseAgentOption(value) {
12468
12844
  return agents;
12469
12845
  }
12470
12846
  program.command("init").description("Initialize your project for AI-assisted development").option("--agent <type>", "Target agents (comma-separated): claude, cursor, codex, github-copilot", parseAgentOption).option("--source <paths...>", "Related source paths to include as context").option("--dry-run", "Preview changes without writing files").option("--force", "Overwrite existing config without prompting").option("--debug-report", void 0, false).option("--show-tokens", "Show token usage summary at the end").option("--auto-approve", "Run without interactive prompts (auto-accept all)").option("--verbose", "Show detailed logs of each step").action(tracked("init", initCommand));
12847
+ program.command("bootstrap").description("Install agent skills (/setup-caliber, /find-skills, /save-learning) without running init").action(tracked("bootstrap", bootstrapCommand));
12471
12848
  program.command("undo").description("Revert all config changes made by Caliber").action(tracked("undo", undoCommand));
12849
+ program.command("uninstall").description("Remove all Caliber resources from this project").option("--force", "Skip confirmation prompt").action(tracked("uninstall", (options) => uninstallCommand(options)));
12472
12850
  program.command("status").description("Show current Caliber config status").option("--json", "Output as JSON").action(tracked("status", statusCommand));
12473
12851
  program.command("regenerate").alias("regen").alias("re").description("Re-analyze project and regenerate config").option("--dry-run", "Preview changes without writing files").action(tracked("regenerate", regenerateCommand));
12474
12852
  program.command("config").description("Configure LLM provider, API key, and model").action(tracked("config", configCommand));
@@ -12493,16 +12871,16 @@ learn.command("delete <index>").description("Delete a learning by its index numb
12493
12871
  learn.command("add <content>").description("Add a learning directly (used by agent skills)").option("--personal", "Save as a personal learning instead of project-level").action(tracked("learn:add", learnAddCommand));
12494
12872
 
12495
12873
  // src/utils/version-check.ts
12496
- import fs48 from "fs";
12497
- import path39 from "path";
12874
+ import fs50 from "fs";
12875
+ import path40 from "path";
12498
12876
  import { fileURLToPath as fileURLToPath2 } from "url";
12499
12877
  import { execSync as execSync16, execFileSync as execFileSync3 } from "child_process";
12500
- import chalk27 from "chalk";
12878
+ import chalk29 from "chalk";
12501
12879
  import ora8 from "ora";
12502
- import confirm2 from "@inquirer/confirm";
12503
- var __dirname_vc = path39.dirname(fileURLToPath2(import.meta.url));
12880
+ import confirm4 from "@inquirer/confirm";
12881
+ var __dirname_vc = path40.dirname(fileURLToPath2(import.meta.url));
12504
12882
  var pkg2 = JSON.parse(
12505
- fs48.readFileSync(path39.resolve(__dirname_vc, "..", "package.json"), "utf-8")
12883
+ fs50.readFileSync(path40.resolve(__dirname_vc, "..", "package.json"), "utf-8")
12506
12884
  );
12507
12885
  function getChannel(version) {
12508
12886
  const match = version.match(/-(dev|next)\./);
@@ -12527,8 +12905,8 @@ function isNewer(registry, current) {
12527
12905
  function getInstalledVersion() {
12528
12906
  try {
12529
12907
  const globalRoot = execSync16("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
12530
- const pkgPath = path39.join(globalRoot, "@rely-ai", "caliber", "package.json");
12531
- return JSON.parse(fs48.readFileSync(pkgPath, "utf-8")).version;
12908
+ const pkgPath = path40.join(globalRoot, "@rely-ai", "caliber", "package.json");
12909
+ return JSON.parse(fs50.readFileSync(pkgPath, "utf-8")).version;
12532
12910
  } catch {
12533
12911
  return null;
12534
12912
  }
@@ -12553,20 +12931,20 @@ async function checkForUpdates() {
12553
12931
  if (!isInteractive) {
12554
12932
  const installTag = channel === "latest" ? "" : `@${channel}`;
12555
12933
  console.log(
12556
- chalk27.yellow(
12934
+ chalk29.yellow(
12557
12935
  `
12558
12936
  Update available: ${current} -> ${latest}
12559
- Run ${chalk27.bold(`npm install -g @rely-ai/caliber${installTag}`)} to upgrade.
12937
+ Run ${chalk29.bold(`npm install -g @rely-ai/caliber${installTag}`)} to upgrade.
12560
12938
  `
12561
12939
  )
12562
12940
  );
12563
12941
  return;
12564
12942
  }
12565
12943
  console.log(
12566
- chalk27.yellow(`
12944
+ chalk29.yellow(`
12567
12945
  Update available: ${current} -> ${latest}`)
12568
12946
  );
12569
- const shouldUpdate = await confirm2({ message: "Would you like to update now? (Y/n)", default: true });
12947
+ const shouldUpdate = await confirm4({ message: "Would you like to update now? (Y/n)", default: true });
12570
12948
  if (!shouldUpdate) {
12571
12949
  console.log();
12572
12950
  return;
@@ -12583,13 +12961,13 @@ Update available: ${current} -> ${latest}`)
12583
12961
  const installed = getInstalledVersion();
12584
12962
  if (installed !== latest) {
12585
12963
  spinner.fail(`Update incomplete \u2014 got ${installed ?? "unknown"}, expected ${latest}`);
12586
- console.log(chalk27.yellow(`Run ${chalk27.bold(`npm install -g @rely-ai/caliber@${tag}`)} manually.
12964
+ console.log(chalk29.yellow(`Run ${chalk29.bold(`npm install -g @rely-ai/caliber@${tag}`)} manually.
12587
12965
  `));
12588
12966
  return;
12589
12967
  }
12590
- spinner.succeed(chalk27.green(`Updated to ${latest}`));
12968
+ spinner.succeed(chalk29.green(`Updated to ${latest}`));
12591
12969
  const args = process.argv.slice(2);
12592
- console.log(chalk27.dim(`
12970
+ console.log(chalk29.dim(`
12593
12971
  Restarting: caliber ${args.join(" ")}
12594
12972
  `));
12595
12973
  execFileSync3("caliber", args, {
@@ -12602,11 +12980,11 @@ Restarting: caliber ${args.join(" ")}
12602
12980
  if (err instanceof Error) {
12603
12981
  const stderr = err.stderr;
12604
12982
  const errMsg = stderr ? String(stderr).trim().split("\n").pop() : err.message.split("\n")[0];
12605
- if (errMsg && !errMsg.includes("SIGTERM")) console.log(chalk27.dim(` ${errMsg}`));
12983
+ if (errMsg && !errMsg.includes("SIGTERM")) console.log(chalk29.dim(` ${errMsg}`));
12606
12984
  }
12607
12985
  console.log(
12608
- chalk27.yellow(
12609
- `Run ${chalk27.bold(`npm install -g @rely-ai/caliber@${tag}`)} manually to upgrade.
12986
+ chalk29.yellow(
12987
+ `Run ${chalk29.bold(`npm install -g @rely-ai/caliber@${tag}`)} manually to upgrade.
12610
12988
  `
12611
12989
  )
12612
12990
  );