@rely-ai/caliber 0.2.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +23 -12
  2. package/dist/bin.js +676 -326
  3. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -56,10 +56,10 @@ import path21 from "path";
56
56
  import { fileURLToPath } from "url";
57
57
 
58
58
  // src/commands/init.ts
59
- import chalk3 from "chalk";
59
+ import chalk4 from "chalk";
60
60
  import ora from "ora";
61
- import readline from "readline";
62
- import select from "@inquirer/select";
61
+ import readline3 from "readline";
62
+ import select2 from "@inquirer/select";
63
63
  import fs17 from "fs";
64
64
 
65
65
  // src/fingerprint/index.ts
@@ -326,6 +326,7 @@ var IGNORE_DIRS2 = /* @__PURE__ */ new Set([
326
326
  ]);
327
327
  var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".py"]);
328
328
  var CONFIG_FILE_NAMES = /* @__PURE__ */ new Set([
329
+ "package.json",
329
330
  "Dockerfile",
330
331
  "docker-compose.yml",
331
332
  "docker-compose.yaml",
@@ -333,6 +334,10 @@ var CONFIG_FILE_NAMES = /* @__PURE__ */ new Set([
333
334
  "tsconfig.json",
334
335
  "pyproject.toml",
335
336
  "turbo.json",
337
+ "requirements.txt",
338
+ "go.mod",
339
+ "Cargo.toml",
340
+ "Gemfile",
336
341
  "next.config.js",
337
342
  "next.config.mjs",
338
343
  "next.config.ts",
@@ -586,7 +591,9 @@ var CONFIG_FILE = path6.join(CONFIG_DIR, "config.json");
586
591
  var DEFAULT_MODELS = {
587
592
  anthropic: "claude-sonnet-4-6",
588
593
  vertex: "claude-sonnet-4-6",
589
- openai: "gpt-4.1"
594
+ openai: "gpt-4.1",
595
+ cursor: "default",
596
+ "claude-cli": "default"
590
597
  };
591
598
  function loadConfig() {
592
599
  const envConfig = resolveFromEnv();
@@ -618,6 +625,18 @@ function resolveFromEnv() {
618
625
  baseUrl: process.env.OPENAI_BASE_URL
619
626
  };
620
627
  }
628
+ if (process.env.CALIBER_USE_CURSOR_SEAT === "1" || process.env.CALIBER_USE_CURSOR_SEAT === "true") {
629
+ return {
630
+ provider: "cursor",
631
+ model: DEFAULT_MODELS.cursor
632
+ };
633
+ }
634
+ if (process.env.CALIBER_USE_CLAUDE_CLI === "1" || process.env.CALIBER_USE_CLAUDE_CLI === "true") {
635
+ return {
636
+ provider: "claude-cli",
637
+ model: DEFAULT_MODELS["claude-cli"]
638
+ };
639
+ }
621
640
  return null;
622
641
  }
623
642
  function readConfigFile() {
@@ -625,7 +644,7 @@ function readConfigFile() {
625
644
  if (!fs5.existsSync(CONFIG_FILE)) return null;
626
645
  const raw = fs5.readFileSync(CONFIG_FILE, "utf-8");
627
646
  const parsed = JSON.parse(raw);
628
- if (!parsed.provider || !["anthropic", "vertex", "openai"].includes(parsed.provider)) {
647
+ if (!parsed.provider || !["anthropic", "vertex", "openai", "cursor", "claude-cli"].includes(parsed.provider)) {
629
648
  return null;
630
649
  }
631
650
  return parsed;
@@ -817,6 +836,252 @@ var OpenAICompatProvider = class {
817
836
  }
818
837
  };
819
838
 
839
+ // src/llm/cursor-acp.ts
840
+ import { spawn, execSync as execSync2 } from "child_process";
841
+ import readline from "readline";
842
+ var ACP_AGENT_BIN = "agent";
843
+ var CursorAcpProvider = class {
844
+ defaultModel;
845
+ cursorApiKey;
846
+ constructor(config) {
847
+ this.defaultModel = config.model || "default";
848
+ this.cursorApiKey = process.env.CURSOR_API_KEY ?? process.env.CURSOR_AUTH_TOKEN;
849
+ }
850
+ async call(options) {
851
+ const chunks = [];
852
+ await this.runAcpPrompt(options, {
853
+ onText: (text) => chunks.push(text),
854
+ onEnd: () => {
855
+ },
856
+ onError: () => {
857
+ }
858
+ });
859
+ return chunks.join("");
860
+ }
861
+ async stream(options, callbacks) {
862
+ await this.runAcpPrompt(options, callbacks);
863
+ }
864
+ async runAcpPrompt(options, callbacks) {
865
+ const combinedPrompt = this.buildCombinedPrompt(options);
866
+ const args = ["acp"];
867
+ if (this.cursorApiKey) {
868
+ args.unshift("--api-key", this.cursorApiKey);
869
+ }
870
+ const agent = spawn(ACP_AGENT_BIN, args, {
871
+ stdio: ["pipe", "pipe", "inherit"],
872
+ cwd: process.cwd(),
873
+ env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } }
874
+ });
875
+ const pending = /* @__PURE__ */ new Map();
876
+ let nextId = 1;
877
+ let sessionId = null;
878
+ const send = (method, params) => {
879
+ return new Promise((resolve2, reject) => {
880
+ const id = nextId++;
881
+ pending.set(id, { resolve: resolve2, reject });
882
+ const msg = { jsonrpc: "2.0", id, method, params };
883
+ agent.stdin.write(JSON.stringify(msg) + "\n", (err) => {
884
+ if (err) {
885
+ pending.delete(id);
886
+ reject(err);
887
+ }
888
+ });
889
+ });
890
+ };
891
+ const rl = readline.createInterface({ input: agent.stdout, crlfDelay: Infinity });
892
+ rl.on("line", (line) => {
893
+ let msg;
894
+ try {
895
+ msg = JSON.parse(line);
896
+ } catch {
897
+ return;
898
+ }
899
+ if (msg.id != null && (msg.result !== void 0 || msg.error !== void 0)) {
900
+ const waiter = pending.get(msg.id);
901
+ if (waiter) {
902
+ pending.delete(msg.id);
903
+ if (msg.error) {
904
+ waiter.reject(new Error(msg.error.message || "ACP error"));
905
+ } else {
906
+ waiter.resolve(msg.result);
907
+ }
908
+ }
909
+ if (msg.result && typeof msg.result === "object" && "sessionId" in msg.result) {
910
+ sessionId = msg.result.sessionId;
911
+ }
912
+ if (msg.result && typeof msg.result === "object" && "stopReason" in msg.result) {
913
+ callbacks.onEnd({
914
+ stopReason: msg.result.stopReason
915
+ });
916
+ }
917
+ return;
918
+ }
919
+ if (msg.method === "session/update" && msg.params?.update) {
920
+ const update = msg.params.update;
921
+ if (update.sessionUpdate === "agent_message_chunk" && update.content?.text) {
922
+ callbacks.onText(update.content.text);
923
+ }
924
+ return;
925
+ }
926
+ if (msg.method === "session/request_permission" && msg.id != null) {
927
+ const response = JSON.stringify({
928
+ jsonrpc: "2.0",
929
+ id: msg.id,
930
+ result: { outcome: { outcome: "selected", optionId: "allow-once" } }
931
+ }) + "\n";
932
+ agent.stdin.write(response);
933
+ }
934
+ });
935
+ agent.on("error", (err) => {
936
+ for (const w of pending.values()) w.reject(err);
937
+ callbacks.onError(err);
938
+ });
939
+ agent.on("close", (code) => {
940
+ if (code !== 0 && code !== null) {
941
+ const err = new Error(`Cursor agent exited with code ${code}`);
942
+ for (const w of pending.values()) w.reject(err);
943
+ callbacks.onError(err);
944
+ }
945
+ });
946
+ try {
947
+ await send("initialize", {
948
+ protocolVersion: 1,
949
+ clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false },
950
+ clientInfo: { name: "caliber", version: "1.0.0" }
951
+ });
952
+ await send("authenticate", { methodId: "cursor_login" });
953
+ const sessionResult = await send("session/new", {
954
+ cwd: process.cwd(),
955
+ mcpServers: []
956
+ });
957
+ sessionId = sessionResult.sessionId;
958
+ await send("session/prompt", {
959
+ sessionId,
960
+ prompt: [{ type: "text", text: combinedPrompt }]
961
+ });
962
+ } catch (err) {
963
+ const error = err instanceof Error ? err : new Error(String(err));
964
+ callbacks.onError(error);
965
+ throw error;
966
+ } finally {
967
+ agent.stdin?.end();
968
+ agent.kill("SIGTERM");
969
+ }
970
+ }
971
+ buildCombinedPrompt(options) {
972
+ const streamOpts = options;
973
+ const hasHistory = streamOpts.messages && streamOpts.messages.length > 0;
974
+ let combined = "";
975
+ combined += "[[System]]\n" + options.system + "\n\n";
976
+ if (hasHistory) {
977
+ for (const msg of streamOpts.messages) {
978
+ combined += `[[${msg.role === "user" ? "User" : "Assistant"}]]
979
+ ${msg.content}
980
+
981
+ `;
982
+ }
983
+ }
984
+ combined += "[[User]]\n" + options.prompt;
985
+ return combined;
986
+ }
987
+ };
988
+ function isCursorAgentAvailable() {
989
+ try {
990
+ execSync2(`which ${ACP_AGENT_BIN}`, { stdio: "ignore" });
991
+ return true;
992
+ } catch {
993
+ return false;
994
+ }
995
+ }
996
+
997
+ // src/llm/claude-cli.ts
998
+ import { spawn as spawn2, execSync as execSync3 } from "child_process";
999
+ var CLAUDE_CLI_BIN = "claude";
1000
+ var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
1001
+ var ClaudeCliProvider = class {
1002
+ defaultModel;
1003
+ timeoutMs;
1004
+ constructor(config) {
1005
+ this.defaultModel = config.model || "default";
1006
+ const envTimeout = process.env.CALIBER_CLAUDE_CLI_TIMEOUT_MS;
1007
+ this.timeoutMs = envTimeout ? parseInt(envTimeout, 10) : DEFAULT_TIMEOUT_MS;
1008
+ if (!Number.isFinite(this.timeoutMs) || this.timeoutMs < 1e3) {
1009
+ this.timeoutMs = DEFAULT_TIMEOUT_MS;
1010
+ }
1011
+ }
1012
+ async call(options) {
1013
+ const combined = this.buildCombinedPrompt(options);
1014
+ return this.runClaudePrint(combined);
1015
+ }
1016
+ async stream(options, callbacks) {
1017
+ try {
1018
+ const text = await this.call(options);
1019
+ if (text) callbacks.onText(text);
1020
+ callbacks.onEnd({ stopReason: "end_turn" });
1021
+ } catch (err) {
1022
+ callbacks.onError(err instanceof Error ? err : new Error(String(err)));
1023
+ }
1024
+ }
1025
+ buildCombinedPrompt(options) {
1026
+ const streamOpts = options;
1027
+ const hasHistory = streamOpts.messages && streamOpts.messages.length > 0;
1028
+ let combined = "";
1029
+ combined += "[[System]]\n" + options.system + "\n\n";
1030
+ if (hasHistory) {
1031
+ for (const msg of streamOpts.messages) {
1032
+ combined += `[[${msg.role === "user" ? "User" : "Assistant"}]]
1033
+ ${msg.content}
1034
+
1035
+ `;
1036
+ }
1037
+ }
1038
+ combined += "[[User]]\n" + options.prompt;
1039
+ return combined;
1040
+ }
1041
+ runClaudePrint(combinedPrompt) {
1042
+ return new Promise((resolve2, reject) => {
1043
+ const child = spawn2(CLAUDE_CLI_BIN, ["-p", combinedPrompt], {
1044
+ cwd: process.cwd(),
1045
+ stdio: ["ignore", "pipe", "inherit"],
1046
+ env: process.env
1047
+ });
1048
+ const chunks = [];
1049
+ child.stdout.on("data", (chunk) => chunks.push(chunk));
1050
+ child.on("error", (err) => {
1051
+ clearTimeout(timer);
1052
+ reject(err);
1053
+ });
1054
+ child.on("close", (code, signal) => {
1055
+ clearTimeout(timer);
1056
+ const stdout = Buffer.concat(chunks).toString("utf-8").trim();
1057
+ if (code === 0) {
1058
+ resolve2(stdout);
1059
+ } else {
1060
+ const msg = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
1061
+ reject(new Error(stdout ? `${msg}. Output: ${stdout.slice(0, 200)}` : msg));
1062
+ }
1063
+ });
1064
+ const timer = setTimeout(() => {
1065
+ child.kill("SIGTERM");
1066
+ reject(
1067
+ new Error(
1068
+ `Claude CLI timed out after ${this.timeoutMs / 1e3}s. Set CALIBER_CLAUDE_CLI_TIMEOUT_MS to increase.`
1069
+ )
1070
+ );
1071
+ }, this.timeoutMs);
1072
+ });
1073
+ }
1074
+ };
1075
+ function isClaudeCliAvailable() {
1076
+ try {
1077
+ const cmd = process.platform === "win32" ? `where ${CLAUDE_CLI_BIN}` : `which ${CLAUDE_CLI_BIN}`;
1078
+ execSync3(cmd, { stdio: "ignore" });
1079
+ return true;
1080
+ } catch {
1081
+ return false;
1082
+ }
1083
+ }
1084
+
820
1085
  // src/llm/utils.ts
821
1086
  function extractJson(text) {
822
1087
  const startIdx = text.search(/[\[{]/);
@@ -877,6 +1142,22 @@ function createProvider(config) {
877
1142
  return new VertexProvider(config);
878
1143
  case "openai":
879
1144
  return new OpenAICompatProvider(config);
1145
+ case "cursor": {
1146
+ if (!isCursorAgentAvailable()) {
1147
+ throw new Error(
1148
+ "Cursor provider requires the Cursor Agent CLI. Install it from https://cursor.com/install then run `agent login`. Alternatively set ANTHROPIC_API_KEY or another provider."
1149
+ );
1150
+ }
1151
+ return new CursorAcpProvider(config);
1152
+ }
1153
+ case "claude-cli": {
1154
+ if (!isClaudeCliAvailable()) {
1155
+ throw new Error(
1156
+ "Claude Code provider requires the Claude Code CLI. Install it from https://claude.ai/install (or run `claude` once and log in). Alternatively set ANTHROPIC_API_KEY or choose another provider."
1157
+ );
1158
+ }
1159
+ return new ClaudeCliProvider(config);
1160
+ }
880
1161
  default:
881
1162
  throw new Error(`Unknown provider: ${config.provider}`);
882
1163
  }
@@ -886,7 +1167,7 @@ function getProvider() {
886
1167
  const config = loadConfig();
887
1168
  if (!config) {
888
1169
  throw new Error(
889
- "No LLM provider configured. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or VERTEX_PROJECT_ID as an environment variable, or run `caliber config` to configure."
1170
+ "No LLM provider configured. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or VERTEX_PROJECT_ID; or run `caliber config` and choose Cursor or Claude Code; or set CALIBER_USE_CURSOR_SEAT=1 / CALIBER_USE_CLAUDE_CLI=1."
890
1171
  );
891
1172
  }
892
1173
  cachedConfig = config;
@@ -1265,9 +1546,9 @@ function isTransientError2(error) {
1265
1546
  const msg = error.message.toLowerCase();
1266
1547
  return TRANSIENT_ERRORS.some((e) => msg.includes(e.toLowerCase()));
1267
1548
  }
1268
- async function generateSetup(fingerprint, targetAgent, prompt, callbacks) {
1549
+ async function generateSetup(fingerprint, targetAgent, prompt, callbacks, failingChecks, currentScore) {
1269
1550
  const provider = getProvider();
1270
- const userMessage = buildGeneratePrompt(fingerprint, targetAgent, prompt);
1551
+ const userMessage = buildGeneratePrompt(fingerprint, targetAgent, prompt, failingChecks, currentScore);
1271
1552
  let attempt = 0;
1272
1553
  const attemptGeneration = async () => {
1273
1554
  attempt++;
@@ -1367,7 +1648,7 @@ var LIMITS = {
1367
1648
  SKILLS_MAX: 10,
1368
1649
  SKILL_CHARS: 3e3,
1369
1650
  RULES_MAX: 10,
1370
- CONFIG_FILES_MAX: 8,
1651
+ CONFIG_FILES_MAX: 15,
1371
1652
  CONFIG_FILE_CHARS: 3e3,
1372
1653
  ROUTES_MAX: 50,
1373
1654
  FILE_SUMMARIES_MAX: 60
@@ -1377,11 +1658,21 @@ function truncate(text, maxChars) {
1377
1658
  return text.slice(0, maxChars) + `
1378
1659
  ... (truncated at ${maxChars} chars)`;
1379
1660
  }
1380
- function buildGeneratePrompt(fingerprint, targetAgent, prompt) {
1661
+ function buildGeneratePrompt(fingerprint, targetAgent, prompt, failingChecks, currentScore) {
1381
1662
  const parts = [];
1382
1663
  const existing = fingerprint.existingConfigs;
1383
1664
  const hasExistingConfigs = !!(existing.claudeMd || existing.claudeSettings || existing.claudeSkills?.length || existing.readmeMd || existing.cursorrules || existing.cursorRules?.length);
1384
- if (hasExistingConfigs) {
1665
+ const isTargetedFix = failingChecks && failingChecks.length > 0 && currentScore !== void 0 && currentScore >= 95;
1666
+ if (isTargetedFix) {
1667
+ parts.push(`TARGETED FIX MODE \u2014 current score: ${currentScore}/100, target: ${targetAgent}`);
1668
+ parts.push(`
1669
+ The existing config is already high quality. ONLY fix these specific failing checks:`);
1670
+ for (const check of failingChecks) {
1671
+ parts.push(`- ${check.name}${check.suggestion ? `: ${check.suggestion}` : ""}`);
1672
+ }
1673
+ parts.push(`
1674
+ IMPORTANT: Return the existing CLAUDE.md and skills with MINIMAL changes \u2014 only the edits needed to fix the above checks. Do NOT rewrite, restructure, rephrase, or make cosmetic changes. Preserve the existing content as-is except for targeted fixes.`);
1675
+ } else if (hasExistingConfigs) {
1385
1676
  parts.push(`Audit and improve the existing coding agent configuration for target: ${targetAgent}`);
1386
1677
  } else {
1387
1678
  parts.push(`Generate an initial coding agent configuration for target: ${targetAgent}`);
@@ -1818,10 +2109,10 @@ function cleanupStaging() {
1818
2109
  }
1819
2110
 
1820
2111
  // src/utils/editor.ts
1821
- import { execSync as execSync2, spawn } from "child_process";
2112
+ import { execSync as execSync4, spawn as spawn3 } from "child_process";
1822
2113
  function commandExists(cmd) {
1823
2114
  try {
1824
- execSync2(`which ${cmd}`, { stdio: "ignore" });
2115
+ execSync4(`which ${cmd}`, { stdio: "ignore" });
1825
2116
  return true;
1826
2117
  } catch {
1827
2118
  return false;
@@ -1839,12 +2130,12 @@ function openDiffsInEditor(editor, files) {
1839
2130
  for (const file of files) {
1840
2131
  try {
1841
2132
  if (file.originalPath) {
1842
- spawn(cmd, ["--diff", file.originalPath, file.proposedPath], {
2133
+ spawn3(cmd, ["--diff", file.originalPath, file.proposedPath], {
1843
2134
  stdio: "ignore",
1844
2135
  detached: true
1845
2136
  }).unref();
1846
2137
  } else {
1847
- spawn(cmd, [file.proposedPath], {
2138
+ spawn3(cmd, [file.proposedPath], {
1848
2139
  stdio: "ignore",
1849
2140
  detached: true
1850
2141
  }).unref();
@@ -1861,7 +2152,7 @@ import { createTwoFilesPatch } from "diff";
1861
2152
  // src/lib/hooks.ts
1862
2153
  import fs14 from "fs";
1863
2154
  import path13 from "path";
1864
- import { execSync as execSync3 } from "child_process";
2155
+ import { execSync as execSync5 } from "child_process";
1865
2156
  var SETTINGS_PATH = path13.join(".claude", "settings.json");
1866
2157
  var HOOK_COMMAND = "caliber refresh --quiet";
1867
2158
  var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
@@ -1933,7 +2224,7 @@ fi
1933
2224
  ${PRECOMMIT_END}`;
1934
2225
  function getGitHooksDir() {
1935
2226
  try {
1936
- const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
2227
+ const gitDir = execSync5("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
1937
2228
  return path13.join(gitDir, "hooks");
1938
2229
  } catch {
1939
2230
  return null;
@@ -2079,7 +2370,7 @@ function removeLearningHooks() {
2079
2370
  init_constants();
2080
2371
  import fs16 from "fs";
2081
2372
  import path15 from "path";
2082
- import { execSync as execSync4 } from "child_process";
2373
+ import { execSync as execSync6 } from "child_process";
2083
2374
  var STATE_FILE = path15.join(CALIBER_DIR, ".caliber-state.json");
2084
2375
  function readState() {
2085
2376
  try {
@@ -2097,7 +2388,7 @@ function writeState(state) {
2097
2388
  }
2098
2389
  function getCurrentHeadSha() {
2099
2390
  try {
2100
- return execSync4("git rev-parse HEAD", {
2391
+ return execSync6("git rev-parse HEAD", {
2101
2392
  encoding: "utf-8",
2102
2393
  stdio: ["pipe", "pipe", "pipe"]
2103
2394
  }).trim();
@@ -2193,6 +2484,80 @@ var SpinnerMessages = class {
2193
2484
  }
2194
2485
  };
2195
2486
 
2487
+ // src/commands/interactive-provider-setup.ts
2488
+ import chalk2 from "chalk";
2489
+ import readline2 from "readline";
2490
+ import select from "@inquirer/select";
2491
+ function promptInput(question) {
2492
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
2493
+ return new Promise((resolve2) => {
2494
+ rl.question(chalk2.cyan(`${question} `), (answer) => {
2495
+ rl.close();
2496
+ resolve2(answer.trim());
2497
+ });
2498
+ });
2499
+ }
2500
+ var PROVIDER_CHOICES = [
2501
+ { name: "Claude Code (use my app login \u2014 Pro/Max/Team, no API key)", value: "claude-cli" },
2502
+ { name: "Cursor (use my Cursor subscription \u2014 no API key)", value: "cursor" },
2503
+ { name: "Anthropic (Claude) \u2014 API key from console.anthropic.com", value: "anthropic" },
2504
+ { name: "Google Vertex AI (Claude)", value: "vertex" },
2505
+ { name: "OpenAI / OpenAI-compatible", value: "openai" }
2506
+ ];
2507
+ async function runInteractiveProviderSetup(options) {
2508
+ const message = options?.selectMessage ?? "Select LLM provider";
2509
+ const provider = await select({
2510
+ message,
2511
+ choices: PROVIDER_CHOICES
2512
+ });
2513
+ const config = { provider, model: "" };
2514
+ switch (provider) {
2515
+ case "claude-cli": {
2516
+ config.model = "default";
2517
+ console.log(chalk2.dim(" Run `claude` once and log in with your Pro/Max/Team account if you haven't."));
2518
+ break;
2519
+ }
2520
+ case "cursor": {
2521
+ config.model = "default";
2522
+ console.log(chalk2.dim(" Run `agent login` if you haven't, or set CURSOR_API_KEY."));
2523
+ break;
2524
+ }
2525
+ case "anthropic": {
2526
+ console.log(chalk2.dim(" Get a key at https://console.anthropic.com (same account as Claude Pro/Team/Max)."));
2527
+ config.apiKey = await promptInput("Anthropic API key:");
2528
+ if (!config.apiKey) {
2529
+ console.log(chalk2.red("API key is required."));
2530
+ throw new Error("__exit__");
2531
+ }
2532
+ config.model = await promptInput(`Model (default: ${DEFAULT_MODELS.anthropic}):`) || DEFAULT_MODELS.anthropic;
2533
+ break;
2534
+ }
2535
+ case "vertex": {
2536
+ config.vertexProjectId = await promptInput("GCP Project ID:");
2537
+ if (!config.vertexProjectId) {
2538
+ console.log(chalk2.red("Project ID is required."));
2539
+ throw new Error("__exit__");
2540
+ }
2541
+ config.vertexRegion = await promptInput("Region (default: us-east5):") || "us-east5";
2542
+ config.vertexCredentials = await promptInput("Service account credentials JSON (or leave empty for ADC):") || void 0;
2543
+ config.model = await promptInput(`Model (default: ${DEFAULT_MODELS.vertex}):`) || DEFAULT_MODELS.vertex;
2544
+ break;
2545
+ }
2546
+ case "openai": {
2547
+ config.apiKey = await promptInput("API key:");
2548
+ if (!config.apiKey) {
2549
+ console.log(chalk2.red("API key is required."));
2550
+ throw new Error("__exit__");
2551
+ }
2552
+ config.baseUrl = await promptInput("Base URL (leave empty for OpenAI, or enter custom endpoint):") || void 0;
2553
+ config.model = await promptInput(`Model (default: ${DEFAULT_MODELS.openai}):`) || DEFAULT_MODELS.openai;
2554
+ break;
2555
+ }
2556
+ }
2557
+ writeConfigFile(config);
2558
+ return config;
2559
+ }
2560
+
2196
2561
  // src/scoring/index.ts
2197
2562
  import { existsSync as existsSync8 } from "fs";
2198
2563
  import { join as join7 } from "path";
@@ -3107,7 +3472,7 @@ function checkFreshness(dir) {
3107
3472
 
3108
3473
  // src/scoring/checks/bonus.ts
3109
3474
  import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "fs";
3110
- import { execSync as execSync5 } from "child_process";
3475
+ import { execSync as execSync7 } from "child_process";
3111
3476
  import { join as join6 } from "path";
3112
3477
  function readFileOrNull5(path23) {
3113
3478
  try {
@@ -3118,7 +3483,7 @@ function readFileOrNull5(path23) {
3118
3483
  }
3119
3484
  function hasPreCommitHook(dir) {
3120
3485
  try {
3121
- const gitDir = execSync5("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
3486
+ const gitDir = execSync7("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
3122
3487
  const hookPath = join6(gitDir, "hooks", "pre-commit");
3123
3488
  const content = readFileOrNull5(hookPath);
3124
3489
  return content ? content.includes("caliber") : false;
@@ -3263,7 +3628,7 @@ function computeLocalScore(dir, targetAgent) {
3263
3628
  }
3264
3629
 
3265
3630
  // src/scoring/display.ts
3266
- import chalk2 from "chalk";
3631
+ import chalk3 from "chalk";
3267
3632
  var CATEGORY_LABELS = {
3268
3633
  existence: "FILES & SETUP",
3269
3634
  quality: "QUALITY",
@@ -3276,31 +3641,31 @@ var CATEGORY_ORDER = ["existence", "quality", "coverage", "accuracy", "freshness
3276
3641
  function gradeColor(grade) {
3277
3642
  switch (grade) {
3278
3643
  case "A":
3279
- return chalk2.green;
3644
+ return chalk3.green;
3280
3645
  case "B":
3281
- return chalk2.greenBright;
3646
+ return chalk3.greenBright;
3282
3647
  case "C":
3283
- return chalk2.yellow;
3648
+ return chalk3.yellow;
3284
3649
  case "D":
3285
- return chalk2.hex("#f97316");
3650
+ return chalk3.hex("#f97316");
3286
3651
  case "F":
3287
- return chalk2.red;
3652
+ return chalk3.red;
3288
3653
  default:
3289
- return chalk2.white;
3654
+ return chalk3.white;
3290
3655
  }
3291
3656
  }
3292
3657
  function progressBar(score, max, width = 40) {
3293
3658
  const filled = Math.round(score / max * width);
3294
3659
  const empty = width - filled;
3295
- const bar = chalk2.hex("#f97316")("\u2593".repeat(filled)) + chalk2.gray("\u2591".repeat(empty));
3660
+ const bar = chalk3.hex("#f97316")("\u2593".repeat(filled)) + chalk3.gray("\u2591".repeat(empty));
3296
3661
  return bar;
3297
3662
  }
3298
3663
  function formatCheck(check) {
3299
- const icon = check.passed ? chalk2.green("\u2713") : check.earnedPoints < 0 ? chalk2.red("\u2717") : chalk2.gray("\u2717");
3300
- const points = check.passed ? chalk2.green(`+${check.earnedPoints}`.padStart(4)) : check.earnedPoints < 0 ? chalk2.red(`${check.earnedPoints}`.padStart(4)) : chalk2.gray(" \u2014");
3301
- const name = check.passed ? chalk2.white(check.name) : chalk2.gray(check.name);
3302
- const detail = check.detail ? chalk2.gray(` (${check.detail})`) : "";
3303
- const suggestion = !check.passed && check.suggestion ? chalk2.gray(`
3664
+ const icon = check.passed ? chalk3.green("\u2713") : check.earnedPoints < 0 ? chalk3.red("\u2717") : chalk3.gray("\u2717");
3665
+ const points = check.passed ? chalk3.green(`+${check.earnedPoints}`.padStart(4)) : check.earnedPoints < 0 ? chalk3.red(`${check.earnedPoints}`.padStart(4)) : chalk3.gray(" \u2014");
3666
+ const name = check.passed ? chalk3.white(check.name) : chalk3.gray(check.name);
3667
+ const detail = check.detail ? chalk3.gray(` (${check.detail})`) : "";
3668
+ const suggestion = !check.passed && check.suggestion ? chalk3.gray(`
3304
3669
  \u2192 ${check.suggestion}`) : "";
3305
3670
  return ` ${icon} ${name.padEnd(38)}${points}${detail}${suggestion}`;
3306
3671
  }
@@ -3308,21 +3673,21 @@ function displayScore(result) {
3308
3673
  const gc = gradeColor(result.grade);
3309
3674
  const agentLabel = result.targetAgent === "both" ? "Claude Code + Cursor" : result.targetAgent === "claude" ? "Claude Code" : "Cursor";
3310
3675
  console.log("");
3311
- console.log(chalk2.gray(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
3312
- console.log(chalk2.gray(" \u2502") + " " + chalk2.gray("\u2502"));
3676
+ console.log(chalk3.gray(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
3677
+ console.log(chalk3.gray(" \u2502") + " " + chalk3.gray("\u2502"));
3313
3678
  console.log(
3314
- chalk2.gray(" \u2502") + " Agent Config Score" + gc(` ${String(result.score).padStart(3)} / ${result.maxScore}`) + " Grade " + gc(result.grade) + " " + chalk2.gray("\u2502")
3679
+ chalk3.gray(" \u2502") + " Agent Config Score" + gc(` ${String(result.score).padStart(3)} / ${result.maxScore}`) + " Grade " + gc(result.grade) + " " + chalk3.gray("\u2502")
3315
3680
  );
3316
- console.log(chalk2.gray(" \u2502") + ` ${progressBar(result.score, result.maxScore)} ` + chalk2.gray("\u2502"));
3317
- console.log(chalk2.gray(" \u2502") + chalk2.dim(` Target: ${agentLabel}`) + " ".repeat(Math.max(1, 40 - agentLabel.length)) + chalk2.gray("\u2502"));
3318
- console.log(chalk2.gray(" \u2502") + " " + chalk2.gray("\u2502"));
3319
- console.log(chalk2.gray(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
3681
+ console.log(chalk3.gray(" \u2502") + ` ${progressBar(result.score, result.maxScore)} ` + chalk3.gray("\u2502"));
3682
+ console.log(chalk3.gray(" \u2502") + chalk3.dim(` Target: ${agentLabel}`) + " ".repeat(Math.max(1, 40 - agentLabel.length)) + chalk3.gray("\u2502"));
3683
+ console.log(chalk3.gray(" \u2502") + " " + chalk3.gray("\u2502"));
3684
+ console.log(chalk3.gray(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
3320
3685
  console.log("");
3321
3686
  for (const category of CATEGORY_ORDER) {
3322
3687
  const summary = result.categories[category];
3323
3688
  const categoryChecks = result.checks.filter((c) => c.category === category);
3324
3689
  console.log(
3325
- chalk2.gray(` ${CATEGORY_LABELS[category]}`) + chalk2.gray(" ".repeat(Math.max(1, 45 - CATEGORY_LABELS[category].length))) + chalk2.white(`${summary.earned}`) + chalk2.gray(` / ${summary.max}`)
3690
+ chalk3.gray(` ${CATEGORY_LABELS[category]}`) + chalk3.gray(" ".repeat(Math.max(1, 45 - CATEGORY_LABELS[category].length))) + chalk3.white(`${summary.earned}`) + chalk3.gray(` / ${summary.max}`)
3326
3691
  );
3327
3692
  for (const check of categoryChecks) {
3328
3693
  console.log(formatCheck(check));
@@ -3333,7 +3698,7 @@ function displayScore(result) {
3333
3698
  function displayScoreDelta(before, after) {
3334
3699
  const delta = after.score - before.score;
3335
3700
  const deltaStr = delta >= 0 ? `+${delta}` : `${delta}`;
3336
- const deltaColor = delta >= 0 ? chalk2.green : chalk2.red;
3701
+ const deltaColor = delta >= 0 ? chalk3.green : chalk3.red;
3337
3702
  const beforeGc = gradeColor(before.grade);
3338
3703
  const afterGc = gradeColor(after.grade);
3339
3704
  const BOX_INNER = 51;
@@ -3344,30 +3709,30 @@ function displayScoreDelta(before, after) {
3344
3709
  const totalPad = BOX_INNER - contentLen;
3345
3710
  const pad1 = Math.max(2, Math.ceil(totalPad / 2));
3346
3711
  const pad2 = Math.max(1, totalPad - pad1);
3347
- const scoreLineFormatted = " Score: " + beforeGc(`${before.score}`) + chalk2.gray(" \u2192 ") + afterGc(`${after.score}`) + " ".repeat(pad1) + deltaColor(deltaPart) + " ".repeat(pad2) + beforeGc(before.grade) + chalk2.gray(" \u2192 ") + afterGc(after.grade);
3712
+ const scoreLineFormatted = " Score: " + beforeGc(`${before.score}`) + chalk3.gray(" \u2192 ") + afterGc(`${after.score}`) + " ".repeat(pad1) + deltaColor(deltaPart) + " ".repeat(pad2) + beforeGc(before.grade) + chalk3.gray(" \u2192 ") + afterGc(after.grade);
3348
3713
  const visibleLen = 3 + scorePart.length + pad1 + deltaPart.length + pad2 + gradePart.length;
3349
3714
  const trailingPad = Math.max(0, BOX_INNER - visibleLen);
3350
3715
  const barWidth = Math.floor((BOX_INNER - 12) / 2);
3351
- const barLine = ` ${progressBar(before.score, before.maxScore, barWidth)}` + chalk2.gray(" \u2192 ") + progressBar(after.score, after.maxScore, barWidth) + " ";
3716
+ const barLine = ` ${progressBar(before.score, before.maxScore, barWidth)}` + chalk3.gray(" \u2192 ") + progressBar(after.score, after.maxScore, barWidth) + " ";
3352
3717
  console.log("");
3353
- console.log(chalk2.gray(" \u256D" + "\u2500".repeat(BOX_INNER) + "\u256E"));
3354
- console.log(chalk2.gray(" \u2502") + " ".repeat(BOX_INNER) + chalk2.gray("\u2502"));
3355
- console.log(chalk2.gray(" \u2502") + scoreLineFormatted + " ".repeat(trailingPad) + chalk2.gray("\u2502"));
3356
- console.log(chalk2.gray(" \u2502") + barLine + chalk2.gray("\u2502"));
3357
- console.log(chalk2.gray(" \u2502") + " ".repeat(BOX_INNER) + chalk2.gray("\u2502"));
3358
- console.log(chalk2.gray(" \u2570" + "\u2500".repeat(BOX_INNER) + "\u256F"));
3718
+ console.log(chalk3.gray(" \u256D" + "\u2500".repeat(BOX_INNER) + "\u256E"));
3719
+ console.log(chalk3.gray(" \u2502") + " ".repeat(BOX_INNER) + chalk3.gray("\u2502"));
3720
+ console.log(chalk3.gray(" \u2502") + scoreLineFormatted + " ".repeat(trailingPad) + chalk3.gray("\u2502"));
3721
+ console.log(chalk3.gray(" \u2502") + barLine + chalk3.gray("\u2502"));
3722
+ console.log(chalk3.gray(" \u2502") + " ".repeat(BOX_INNER) + chalk3.gray("\u2502"));
3723
+ console.log(chalk3.gray(" \u2570" + "\u2500".repeat(BOX_INNER) + "\u256F"));
3359
3724
  console.log("");
3360
3725
  const improved = after.checks.filter((ac) => {
3361
3726
  const bc = before.checks.find((b) => b.id === ac.id);
3362
3727
  return bc && ac.earnedPoints > bc.earnedPoints;
3363
3728
  });
3364
3729
  if (improved.length > 0) {
3365
- console.log(chalk2.gray(" What improved:"));
3730
+ console.log(chalk3.gray(" What improved:"));
3366
3731
  for (const check of improved) {
3367
3732
  const bc = before.checks.find((b) => b.id === check.id);
3368
3733
  const gain = check.earnedPoints - bc.earnedPoints;
3369
3734
  console.log(
3370
- chalk2.green(" +") + chalk2.white(` ${check.name.padEnd(50)}`) + chalk2.green(`+${gain}`)
3735
+ chalk3.green(" +") + chalk3.white(` ${check.name.padEnd(50)}`) + chalk3.green(`+${gain}`)
3371
3736
  );
3372
3737
  }
3373
3738
  console.log("");
@@ -3376,7 +3741,7 @@ function displayScoreDelta(before, after) {
3376
3741
 
3377
3742
  // src/commands/init.ts
3378
3743
  async function initCommand(options) {
3379
- console.log(chalk3.bold.hex("#6366f1")(`
3744
+ console.log(chalk4.bold.hex("#6366f1")(`
3380
3745
  \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557
3381
3746
  \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
3382
3747
  \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
@@ -3384,55 +3749,84 @@ async function initCommand(options) {
3384
3749
  \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551
3385
3750
  \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
3386
3751
  `));
3387
- console.log(chalk3.dim(" Configure your coding agent environment\n"));
3388
- console.log(chalk3.bold(" What is Caliber?\n"));
3389
- console.log(chalk3.dim(" Caliber audits your AI agent configurations and suggests targeted"));
3390
- console.log(chalk3.dim(" improvements. It analyzes CLAUDE.md, .cursorrules, and skills"));
3391
- console.log(chalk3.dim(" against your actual codebase \u2014 keeping what works, fixing"));
3392
- console.log(chalk3.dim(" what's stale, and adding what's missing.\n"));
3393
- console.log(chalk3.bold(" How it works:\n"));
3394
- console.log(chalk3.dim(" 1. Scan Analyze your code, dependencies, and existing configs"));
3395
- console.log(chalk3.dim(" 2. Generate AI creates or improves config files for your project"));
3396
- console.log(chalk3.dim(" 3. Review You accept, refine, or decline the proposed changes"));
3397
- console.log(chalk3.dim(" 4. Apply Config files are written with backups\n"));
3398
- console.log(chalk3.hex("#6366f1").bold(" Step 1/4 \u2014 Check LLM provider\n"));
3399
- const config = loadConfig();
3752
+ console.log(chalk4.dim(" Configure your coding agent environment\n"));
3753
+ console.log(chalk4.bold(" What is Caliber?\n"));
3754
+ console.log(chalk4.dim(" Caliber audits your AI agent configurations and suggests targeted"));
3755
+ console.log(chalk4.dim(" improvements. It analyzes CLAUDE.md, .cursorrules, and skills"));
3756
+ console.log(chalk4.dim(" against your actual codebase \u2014 keeping what works, fixing"));
3757
+ console.log(chalk4.dim(" what's stale, and adding what's missing.\n"));
3758
+ console.log(chalk4.bold(" How it works:\n"));
3759
+ console.log(chalk4.dim(" 1. Scan Analyze your code, dependencies, and existing configs"));
3760
+ console.log(chalk4.dim(" 2. Generate AI creates or improves config files for your project"));
3761
+ console.log(chalk4.dim(" 3. Review You accept, refine, or decline the proposed changes"));
3762
+ console.log(chalk4.dim(" 4. Apply Config files are written with backups\n"));
3763
+ console.log(chalk4.hex("#6366f1").bold(" Step 1/4 \u2014 How do you want to use Caliber?\n"));
3764
+ let config = loadConfig();
3400
3765
  if (!config) {
3401
- console.log(chalk3.red(" No LLM provider configured.\n"));
3402
- console.log(chalk3.dim(" Set one of these environment variables:"));
3403
- console.log(chalk3.dim(" ANTHROPIC_API_KEY \u2014 for Anthropic Claude"));
3404
- console.log(chalk3.dim(" OPENAI_API_KEY \u2014 for OpenAI or compatible endpoints"));
3405
- console.log(chalk3.dim(" VERTEX_PROJECT_ID \u2014 for Google Vertex AI\n"));
3406
- console.log(chalk3.dim(" Or run `caliber config` for interactive setup.\n"));
3407
- throw new Error("__exit__");
3766
+ console.log(chalk4.dim(" No LLM provider set yet. Choose how to run Caliber:\n"));
3767
+ try {
3768
+ await runInteractiveProviderSetup({
3769
+ selectMessage: "How do you want to use Caliber? (choose LLM provider)"
3770
+ });
3771
+ } catch (err) {
3772
+ if (err.message === "__exit__") throw err;
3773
+ throw err;
3774
+ }
3775
+ config = loadConfig();
3776
+ if (!config) {
3777
+ console.log(chalk4.red(" Setup was cancelled or failed.\n"));
3778
+ throw new Error("__exit__");
3779
+ }
3780
+ console.log(chalk4.green(" \u2713 Provider saved. Continuing with init.\n"));
3408
3781
  }
3409
- console.log(chalk3.dim(` Provider: ${config.provider} | Model: ${config.model}
3782
+ console.log(chalk4.dim(` Provider: ${config.provider} | Model: ${config.model}
3410
3783
  `));
3411
- console.log(chalk3.hex("#6366f1").bold(" Step 2/4 \u2014 Scan project\n"));
3412
- console.log(chalk3.dim(" Detecting languages, dependencies, file structure, and existing configs.\n"));
3784
+ console.log(chalk4.hex("#6366f1").bold(" Step 2/4 \u2014 Scan project\n"));
3785
+ console.log(chalk4.dim(" Detecting languages, dependencies, file structure, and existing configs.\n"));
3413
3786
  const spinner = ora("Analyzing project...").start();
3414
3787
  const fingerprint = collectFingerprint(process.cwd());
3415
3788
  await enrichFingerprintWithLLM(fingerprint, process.cwd());
3416
3789
  spinner.succeed("Project analyzed");
3417
- console.log(chalk3.dim(` Languages: ${fingerprint.languages.join(", ") || "none detected"}`));
3418
- console.log(chalk3.dim(` Files: ${fingerprint.fileTree.length} found
3790
+ console.log(chalk4.dim(` Languages: ${fingerprint.languages.join(", ") || "none detected"}`));
3791
+ console.log(chalk4.dim(` Files: ${fingerprint.fileTree.length} found
3419
3792
  `));
3420
3793
  const targetAgent = options.agent || await promptAgent();
3421
3794
  const baselineScore = computeLocalScore(process.cwd(), targetAgent);
3795
+ const hasExistingConfig = !!(fingerprint.existingConfigs.claudeMd || fingerprint.existingConfigs.claudeSettings || fingerprint.existingConfigs.claudeSkills?.length || fingerprint.existingConfigs.cursorrules || fingerprint.existingConfigs.cursorRules?.length);
3796
+ if (hasExistingConfig && baselineScore.score === 100) {
3797
+ console.log(chalk4.hex("#6366f1").bold(" Step 3/4 \u2014 Score check\n"));
3798
+ displayScore(baselineScore);
3799
+ console.log(chalk4.bold.green(" Your setup is already optimal \u2014 nothing to change.\n"));
3800
+ console.log(chalk4.dim(" Run `caliber init --force` to regenerate anyway.\n"));
3801
+ if (!options.force) return;
3802
+ }
3422
3803
  const isEmpty = fingerprint.fileTree.length < 3;
3423
3804
  if (isEmpty) {
3424
- fingerprint.description = await promptInput("What will you build in this project?");
3425
- }
3426
- const hasExistingConfig = !!(fingerprint.existingConfigs.claudeMd || fingerprint.existingConfigs.claudeSettings || fingerprint.existingConfigs.claudeSkills?.length || fingerprint.existingConfigs.cursorrules || fingerprint.existingConfigs.cursorRules?.length);
3427
- if (hasExistingConfig) {
3428
- console.log(chalk3.hex("#6366f1").bold(" Step 3/4 \u2014 Auditing your configs\n"));
3429
- console.log(chalk3.dim(" AI is reviewing your existing configs against your codebase"));
3430
- console.log(chalk3.dim(" and suggesting improvements.\n"));
3805
+ fingerprint.description = await promptInput2("What will you build in this project?");
3806
+ }
3807
+ let failingChecks;
3808
+ let currentScore;
3809
+ if (hasExistingConfig && baselineScore.score >= 95 && !options.force) {
3810
+ failingChecks = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0).map((c) => ({ name: c.name, suggestion: c.suggestion }));
3811
+ currentScore = baselineScore.score;
3812
+ if (failingChecks.length > 0) {
3813
+ console.log(chalk4.hex("#6366f1").bold(" Step 3/4 \u2014 Targeted fixes\n"));
3814
+ console.log(chalk4.dim(` Score is ${baselineScore.score}/100 \u2014 only fixing ${failingChecks.length} remaining issue${failingChecks.length === 1 ? "" : "s"}:
3815
+ `));
3816
+ for (const check of failingChecks) {
3817
+ console.log(chalk4.dim(` \u2022 ${check.name}`));
3818
+ }
3819
+ console.log("");
3820
+ }
3821
+ } else if (hasExistingConfig) {
3822
+ console.log(chalk4.hex("#6366f1").bold(" Step 3/4 \u2014 Auditing your configs\n"));
3823
+ console.log(chalk4.dim(" AI is reviewing your existing configs against your codebase"));
3824
+ console.log(chalk4.dim(" and suggesting improvements.\n"));
3431
3825
  } else {
3432
- console.log(chalk3.hex("#6366f1").bold(" Step 3/4 \u2014 Generating configs\n"));
3433
- console.log(chalk3.dim(" AI is creating agent config files tailored to your project.\n"));
3826
+ console.log(chalk4.hex("#6366f1").bold(" Step 3/4 \u2014 Generating configs\n"));
3827
+ console.log(chalk4.dim(" AI is creating agent config files tailored to your project.\n"));
3434
3828
  }
3435
- console.log(chalk3.dim(" This usually takes 1\u20133 minutes.\n"));
3829
+ console.log(chalk4.dim(" This usually takes 1\u20133 minutes.\n"));
3436
3830
  const genStartTime = Date.now();
3437
3831
  const genSpinner = ora("Generating setup...").start();
3438
3832
  const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES, { showElapsedTime: true });
@@ -3455,7 +3849,9 @@ async function initCommand(options) {
3455
3849
  genMessages.stop();
3456
3850
  genSpinner.fail(`Generation error: ${error}`);
3457
3851
  }
3458
- }
3852
+ },
3853
+ failingChecks,
3854
+ currentScore
3459
3855
  );
3460
3856
  if (!generatedSetup) {
3461
3857
  generatedSetup = result.setup;
@@ -3471,8 +3867,8 @@ async function initCommand(options) {
3471
3867
  if (!generatedSetup) {
3472
3868
  genSpinner.fail("Failed to generate setup.");
3473
3869
  if (rawOutput) {
3474
- console.log(chalk3.dim("\nRaw LLM output (JSON parse failed):"));
3475
- console.log(chalk3.dim(rawOutput.slice(0, 500)));
3870
+ console.log(chalk4.dim("\nRaw LLM output (JSON parse failed):"));
3871
+ console.log(chalk4.dim(rawOutput.slice(0, 500)));
3476
3872
  }
3477
3873
  throw new Error("__exit__");
3478
3874
  }
@@ -3480,12 +3876,12 @@ async function initCommand(options) {
3480
3876
  const mins = Math.floor(elapsedMs / 6e4);
3481
3877
  const secs = Math.floor(elapsedMs % 6e4 / 1e3);
3482
3878
  const timeStr = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
3483
- genSpinner.succeed(`Setup generated ${chalk3.dim(`in ${timeStr}`)}`);
3879
+ genSpinner.succeed(`Setup generated ${chalk4.dim(`in ${timeStr}`)}`);
3484
3880
  printSetupSummary(generatedSetup);
3485
- console.log(chalk3.hex("#6366f1").bold(" Step 4/4 \u2014 Review\n"));
3881
+ console.log(chalk4.hex("#6366f1").bold(" Step 4/4 \u2014 Review\n"));
3486
3882
  const setupFiles = collectSetupFiles(generatedSetup);
3487
3883
  const staged = stageFiles(setupFiles, process.cwd());
3488
- console.log(chalk3.dim(` ${chalk3.green(`${staged.newFiles} new`)} / ${chalk3.yellow(`${staged.modifiedFiles} modified`)} file${staged.newFiles + staged.modifiedFiles !== 1 ? "s" : ""}
3884
+ console.log(chalk4.dim(` ${chalk4.green(`${staged.newFiles} new`)} / ${chalk4.yellow(`${staged.modifiedFiles} modified`)} file${staged.newFiles + staged.modifiedFiles !== 1 ? "s" : ""}
3489
3885
  `));
3490
3886
  const wantsReview = await promptWantsReview();
3491
3887
  if (wantsReview) {
@@ -3497,12 +3893,12 @@ async function initCommand(options) {
3497
3893
  generatedSetup = await refineLoop(generatedSetup, targetAgent);
3498
3894
  if (!generatedSetup) {
3499
3895
  cleanupStaging();
3500
- console.log(chalk3.dim("Refinement cancelled. No files were modified."));
3896
+ console.log(chalk4.dim("Refinement cancelled. No files were modified."));
3501
3897
  return;
3502
3898
  }
3503
3899
  const updatedFiles = collectSetupFiles(generatedSetup);
3504
3900
  const restaged = stageFiles(updatedFiles, process.cwd());
3505
- console.log(chalk3.dim(` ${chalk3.green(`${restaged.newFiles} new`)} / ${chalk3.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
3901
+ console.log(chalk4.dim(` ${chalk4.green(`${restaged.newFiles} new`)} / ${chalk4.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
3506
3902
  `));
3507
3903
  printSetupSummary(generatedSetup);
3508
3904
  const wantsReviewAgain = await promptWantsReview();
@@ -3514,11 +3910,11 @@ async function initCommand(options) {
3514
3910
  }
3515
3911
  cleanupStaging();
3516
3912
  if (action === "decline") {
3517
- console.log(chalk3.dim("Setup declined. No files were modified."));
3913
+ console.log(chalk4.dim("Setup declined. No files were modified."));
3518
3914
  return;
3519
3915
  }
3520
3916
  if (options.dryRun) {
3521
- console.log(chalk3.yellow("\n[Dry run] Would write the following files:"));
3917
+ console.log(chalk4.yellow("\n[Dry run] Would write the following files:"));
3522
3918
  console.log(JSON.stringify(generatedSetup, null, 2));
3523
3919
  return;
3524
3920
  }
@@ -3526,29 +3922,29 @@ async function initCommand(options) {
3526
3922
  try {
3527
3923
  const result = writeSetup(generatedSetup);
3528
3924
  writeSpinner.succeed("Config files written");
3529
- console.log(chalk3.bold("\nFiles created/updated:"));
3925
+ console.log(chalk4.bold("\nFiles created/updated:"));
3530
3926
  for (const file of result.written) {
3531
- console.log(` ${chalk3.green("\u2713")} ${file}`);
3927
+ console.log(` ${chalk4.green("\u2713")} ${file}`);
3532
3928
  }
3533
3929
  if (result.deleted.length > 0) {
3534
- console.log(chalk3.bold("\nFiles removed:"));
3930
+ console.log(chalk4.bold("\nFiles removed:"));
3535
3931
  for (const file of result.deleted) {
3536
- console.log(` ${chalk3.red("\u2717")} ${file}`);
3932
+ console.log(` ${chalk4.red("\u2717")} ${file}`);
3537
3933
  }
3538
3934
  }
3539
3935
  if (result.backupDir) {
3540
- console.log(chalk3.dim(`
3936
+ console.log(chalk4.dim(`
3541
3937
  Backups saved to ${result.backupDir}`));
3542
3938
  }
3543
3939
  } catch (err) {
3544
3940
  writeSpinner.fail("Failed to write files");
3545
- console.error(chalk3.red(err instanceof Error ? err.message : "Unknown error"));
3941
+ console.error(chalk4.red(err instanceof Error ? err.message : "Unknown error"));
3546
3942
  throw new Error("__exit__");
3547
3943
  }
3548
3944
  if (!fs17.existsSync("AGENTS.md")) {
3549
3945
  const agentsContent = "# AGENTS.md\n\nThis project uses AI coding agents. See CLAUDE.md for Claude Code configuration and .cursor/rules/ for Cursor rules.\n";
3550
3946
  fs17.writeFileSync("AGENTS.md", agentsContent);
3551
- console.log(` ${chalk3.green("\u2713")} AGENTS.md`);
3947
+ console.log(` ${chalk4.green("\u2713")} AGENTS.md`);
3552
3948
  }
3553
3949
  ensurePermissions();
3554
3950
  const sha = getCurrentHeadSha();
@@ -3561,45 +3957,45 @@ async function initCommand(options) {
3561
3957
  if (hookChoice === "claude" || hookChoice === "both") {
3562
3958
  const hookResult = installHook();
3563
3959
  if (hookResult.installed) {
3564
- console.log(` ${chalk3.green("\u2713")} Claude Code hook installed \u2014 docs update on session end`);
3565
- console.log(chalk3.dim(" Run `caliber hooks remove` to disable"));
3960
+ console.log(` ${chalk4.green("\u2713")} Claude Code hook installed \u2014 docs update on session end`);
3961
+ console.log(chalk4.dim(" Run `caliber hooks remove` to disable"));
3566
3962
  } else if (hookResult.alreadyInstalled) {
3567
- console.log(chalk3.dim(" Claude Code hook already installed"));
3963
+ console.log(chalk4.dim(" Claude Code hook already installed"));
3568
3964
  }
3569
3965
  const learnResult = installLearningHooks();
3570
3966
  if (learnResult.installed) {
3571
- console.log(` ${chalk3.green("\u2713")} Learning hooks installed \u2014 session insights captured automatically`);
3572
- console.log(chalk3.dim(" Run `caliber learn remove` to disable"));
3967
+ console.log(` ${chalk4.green("\u2713")} Learning hooks installed \u2014 session insights captured automatically`);
3968
+ console.log(chalk4.dim(" Run `caliber learn remove` to disable"));
3573
3969
  } else if (learnResult.alreadyInstalled) {
3574
- console.log(chalk3.dim(" Learning hooks already installed"));
3970
+ console.log(chalk4.dim(" Learning hooks already installed"));
3575
3971
  }
3576
3972
  }
3577
3973
  if (hookChoice === "precommit" || hookChoice === "both") {
3578
3974
  const precommitResult = installPreCommitHook();
3579
3975
  if (precommitResult.installed) {
3580
- console.log(` ${chalk3.green("\u2713")} Pre-commit hook installed \u2014 docs refresh before each commit`);
3581
- console.log(chalk3.dim(" Run `caliber hooks remove-precommit` to disable"));
3976
+ console.log(` ${chalk4.green("\u2713")} Pre-commit hook installed \u2014 docs refresh before each commit`);
3977
+ console.log(chalk4.dim(" Run `caliber hooks remove-precommit` to disable"));
3582
3978
  } else if (precommitResult.alreadyInstalled) {
3583
- console.log(chalk3.dim(" Pre-commit hook already installed"));
3979
+ console.log(chalk4.dim(" Pre-commit hook already installed"));
3584
3980
  } else {
3585
- console.log(chalk3.yellow(" Could not install pre-commit hook (not a git repository?)"));
3981
+ console.log(chalk4.yellow(" Could not install pre-commit hook (not a git repository?)"));
3586
3982
  }
3587
3983
  }
3588
3984
  if (hookChoice === "skip") {
3589
- console.log(chalk3.dim(" Skipped auto-refresh hooks. Run `caliber hooks install` later to enable."));
3985
+ console.log(chalk4.dim(" Skipped auto-refresh hooks. Run `caliber hooks install` later to enable."));
3590
3986
  }
3591
3987
  const afterScore = computeLocalScore(process.cwd(), targetAgent);
3592
3988
  displayScoreDelta(baselineScore, afterScore);
3593
- console.log(chalk3.bold.green(" Setup complete! Your coding agent is now configured."));
3594
- console.log(chalk3.dim(" Run `caliber undo` to revert changes.\n"));
3595
- console.log(chalk3.bold(" Next steps:\n"));
3596
- console.log(` ${chalk3.hex("#6366f1")("caliber undo")} Revert all changes from this run`);
3989
+ console.log(chalk4.bold.green(" Setup complete! Your coding agent is now configured."));
3990
+ console.log(chalk4.dim(" Run `caliber undo` to revert changes.\n"));
3991
+ console.log(chalk4.bold(" Next steps:\n"));
3992
+ console.log(` ${chalk4.hex("#6366f1")("caliber undo")} Revert all changes from this run`);
3597
3993
  console.log("");
3598
3994
  }
3599
3995
  async function refineLoop(currentSetup, _targetAgent) {
3600
3996
  const history = [];
3601
3997
  while (true) {
3602
- const message = await promptInput("\nWhat would you like to change?");
3998
+ const message = await promptInput2("\nWhat would you like to change?");
3603
3999
  if (!message || message.toLowerCase() === "done" || message.toLowerCase() === "accept") {
3604
4000
  return currentSetup;
3605
4001
  }
@@ -3621,24 +4017,24 @@ async function refineLoop(currentSetup, _targetAgent) {
3621
4017
  history.push({ role: "assistant", content: JSON.stringify(refined) });
3622
4018
  refineSpinner.succeed("Setup updated");
3623
4019
  printSetupSummary(refined);
3624
- console.log(chalk3.dim('Type "done" to accept, or describe more changes.'));
4020
+ console.log(chalk4.dim('Type "done" to accept, or describe more changes.'));
3625
4021
  } else {
3626
4022
  refineSpinner.fail("Refinement failed \u2014 could not parse AI response.");
3627
- console.log(chalk3.dim('Try rephrasing your request, or type "done" to keep the current setup.'));
4023
+ console.log(chalk4.dim('Try rephrasing your request, or type "done" to keep the current setup.'));
3628
4024
  }
3629
4025
  }
3630
4026
  }
3631
- function promptInput(question) {
3632
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
4027
+ function promptInput2(question) {
4028
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
3633
4029
  return new Promise((resolve2) => {
3634
- rl.question(chalk3.cyan(`${question} `), (answer) => {
4030
+ rl.question(chalk4.cyan(`${question} `), (answer) => {
3635
4031
  rl.close();
3636
4032
  resolve2(answer.trim());
3637
4033
  });
3638
4034
  });
3639
4035
  }
3640
4036
  async function promptAgent() {
3641
- return select({
4037
+ return select2({
3642
4038
  message: "Which coding agent are you using?",
3643
4039
  choices: [
3644
4040
  { name: "Claude Code", value: "claude" },
@@ -3657,13 +4053,13 @@ async function promptHookType(targetAgent) {
3657
4053
  choices.push({ name: "Both (Claude Code + pre-commit)", value: "both" });
3658
4054
  }
3659
4055
  choices.push({ name: "Skip for now", value: "skip" });
3660
- return select({
4056
+ return select2({
3661
4057
  message: "How would you like to auto-refresh your docs?",
3662
4058
  choices
3663
4059
  });
3664
4060
  }
3665
4061
  async function promptWantsReview() {
3666
- const answer = await select({
4062
+ const answer = await select2({
3667
4063
  message: "Would you like to review the diffs before deciding?",
3668
4064
  choices: [
3669
4065
  { name: "Yes, show me the diffs", value: true },
@@ -3685,7 +4081,7 @@ async function promptReviewMethod() {
3685
4081
  return { name: "Terminal", value: "terminal" };
3686
4082
  }
3687
4083
  });
3688
- return select({ message: "How would you like to review the changes?", choices });
4084
+ return select2({ message: "How would you like to review the changes?", choices });
3689
4085
  }
3690
4086
  function openReview(method, stagedFiles) {
3691
4087
  if (method === "cursor" || method === "vscode") {
@@ -3693,7 +4089,7 @@ function openReview(method, stagedFiles) {
3693
4089
  originalPath: f.originalPath,
3694
4090
  proposedPath: f.proposedPath
3695
4091
  })));
3696
- console.log(chalk3.dim(" Diffs opened in your editor.\n"));
4092
+ console.log(chalk4.dim(" Diffs opened in your editor.\n"));
3697
4093
  } else {
3698
4094
  for (const file of stagedFiles) {
3699
4095
  if (file.currentPath) {
@@ -3705,19 +4101,19 @@ function openReview(method, stagedFiles) {
3705
4101
  if (line.startsWith("+") && !line.startsWith("+++")) added++;
3706
4102
  if (line.startsWith("-") && !line.startsWith("---")) removed++;
3707
4103
  }
3708
- console.log(` ${chalk3.yellow("~")} ${file.relativePath} ${chalk3.green(`+${added}`)} ${chalk3.red(`-${removed}`)}`);
4104
+ console.log(` ${chalk4.yellow("~")} ${file.relativePath} ${chalk4.green(`+${added}`)} ${chalk4.red(`-${removed}`)}`);
3709
4105
  } else {
3710
4106
  const lines = fs17.readFileSync(file.proposedPath, "utf-8").split("\n").length;
3711
- console.log(` ${chalk3.green("+")} ${file.relativePath} ${chalk3.dim(`${lines} lines`)}`);
4107
+ console.log(` ${chalk4.green("+")} ${file.relativePath} ${chalk4.dim(`${lines} lines`)}`);
3712
4108
  }
3713
4109
  }
3714
4110
  console.log("");
3715
- console.log(chalk3.dim(` Files staged at .caliber/staged/ for manual inspection.
4111
+ console.log(chalk4.dim(` Files staged at .caliber/staged/ for manual inspection.
3716
4112
  `));
3717
4113
  }
3718
4114
  }
3719
4115
  async function promptReviewAction() {
3720
- return select({
4116
+ return select2({
3721
4117
  message: "What would you like to do?",
3722
4118
  choices: [
3723
4119
  { name: "Accept and apply", value: "accept" },
@@ -3732,46 +4128,46 @@ function printSetupSummary(setup) {
3732
4128
  const fileDescriptions = setup.fileDescriptions;
3733
4129
  const deletions = setup.deletions;
3734
4130
  console.log("");
3735
- console.log(chalk3.bold(" Proposed changes:\n"));
4131
+ console.log(chalk4.bold(" Proposed changes:\n"));
3736
4132
  const getDescription = (filePath) => {
3737
4133
  return fileDescriptions?.[filePath];
3738
4134
  };
3739
4135
  if (claude) {
3740
4136
  if (claude.claudeMd) {
3741
- const icon = fs17.existsSync("CLAUDE.md") ? chalk3.yellow("~") : chalk3.green("+");
4137
+ const icon = fs17.existsSync("CLAUDE.md") ? chalk4.yellow("~") : chalk4.green("+");
3742
4138
  const desc = getDescription("CLAUDE.md");
3743
- console.log(` ${icon} ${chalk3.bold("CLAUDE.md")}`);
3744
- if (desc) console.log(chalk3.dim(` ${desc}`));
4139
+ console.log(` ${icon} ${chalk4.bold("CLAUDE.md")}`);
4140
+ if (desc) console.log(chalk4.dim(` ${desc}`));
3745
4141
  console.log("");
3746
4142
  }
3747
4143
  const skills = claude.skills;
3748
4144
  if (Array.isArray(skills) && skills.length > 0) {
3749
4145
  for (const skill of skills) {
3750
4146
  const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
3751
- const icon = fs17.existsSync(skillPath) ? chalk3.yellow("~") : chalk3.green("+");
4147
+ const icon = fs17.existsSync(skillPath) ? chalk4.yellow("~") : chalk4.green("+");
3752
4148
  const desc = getDescription(skillPath);
3753
- console.log(` ${icon} ${chalk3.bold(skillPath)}`);
3754
- console.log(chalk3.dim(` ${desc || skill.description || skill.name}`));
4149
+ console.log(` ${icon} ${chalk4.bold(skillPath)}`);
4150
+ console.log(chalk4.dim(` ${desc || skill.description || skill.name}`));
3755
4151
  console.log("");
3756
4152
  }
3757
4153
  }
3758
4154
  }
3759
4155
  if (cursor) {
3760
4156
  if (cursor.cursorrules) {
3761
- const icon = fs17.existsSync(".cursorrules") ? chalk3.yellow("~") : chalk3.green("+");
4157
+ const icon = fs17.existsSync(".cursorrules") ? chalk4.yellow("~") : chalk4.green("+");
3762
4158
  const desc = getDescription(".cursorrules");
3763
- console.log(` ${icon} ${chalk3.bold(".cursorrules")}`);
3764
- if (desc) console.log(chalk3.dim(` ${desc}`));
4159
+ console.log(` ${icon} ${chalk4.bold(".cursorrules")}`);
4160
+ if (desc) console.log(chalk4.dim(` ${desc}`));
3765
4161
  console.log("");
3766
4162
  }
3767
4163
  const cursorSkills = cursor.skills;
3768
4164
  if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
3769
4165
  for (const skill of cursorSkills) {
3770
4166
  const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
3771
- const icon = fs17.existsSync(skillPath) ? chalk3.yellow("~") : chalk3.green("+");
4167
+ const icon = fs17.existsSync(skillPath) ? chalk4.yellow("~") : chalk4.green("+");
3772
4168
  const desc = getDescription(skillPath);
3773
- console.log(` ${icon} ${chalk3.bold(skillPath)}`);
3774
- console.log(chalk3.dim(` ${desc || skill.description || skill.name}`));
4169
+ console.log(` ${icon} ${chalk4.bold(skillPath)}`);
4170
+ console.log(chalk4.dim(` ${desc || skill.description || skill.name}`));
3775
4171
  console.log("");
3776
4172
  }
3777
4173
  }
@@ -3779,14 +4175,14 @@ function printSetupSummary(setup) {
3779
4175
  if (Array.isArray(rules) && rules.length > 0) {
3780
4176
  for (const rule of rules) {
3781
4177
  const rulePath = `.cursor/rules/${rule.filename}`;
3782
- const icon = fs17.existsSync(rulePath) ? chalk3.yellow("~") : chalk3.green("+");
4178
+ const icon = fs17.existsSync(rulePath) ? chalk4.yellow("~") : chalk4.green("+");
3783
4179
  const desc = getDescription(rulePath);
3784
- console.log(` ${icon} ${chalk3.bold(rulePath)}`);
4180
+ console.log(` ${icon} ${chalk4.bold(rulePath)}`);
3785
4181
  if (desc) {
3786
- console.log(chalk3.dim(` ${desc}`));
4182
+ console.log(chalk4.dim(` ${desc}`));
3787
4183
  } else {
3788
4184
  const firstLine = rule.content.split("\n").filter((l) => l.trim() && !l.trim().startsWith("#"))[0];
3789
- if (firstLine) console.log(chalk3.dim(` ${firstLine.trim().slice(0, 80)}`));
4185
+ if (firstLine) console.log(chalk4.dim(` ${firstLine.trim().slice(0, 80)}`));
3790
4186
  }
3791
4187
  console.log("");
3792
4188
  }
@@ -3794,12 +4190,12 @@ function printSetupSummary(setup) {
3794
4190
  }
3795
4191
  if (Array.isArray(deletions) && deletions.length > 0) {
3796
4192
  for (const del of deletions) {
3797
- console.log(` ${chalk3.red("-")} ${chalk3.bold(del.filePath)}`);
3798
- console.log(chalk3.dim(` ${del.reason}`));
4193
+ console.log(` ${chalk4.red("-")} ${chalk4.bold(del.filePath)}`);
4194
+ console.log(chalk4.dim(` ${del.reason}`));
3799
4195
  console.log("");
3800
4196
  }
3801
4197
  }
3802
- console.log(` ${chalk3.green("+")} ${chalk3.dim("new")} ${chalk3.yellow("~")} ${chalk3.dim("modified")} ${chalk3.red("-")} ${chalk3.dim("removed")}`);
4198
+ console.log(` ${chalk4.green("+")} ${chalk4.dim("new")} ${chalk4.yellow("~")} ${chalk4.dim("modified")} ${chalk4.red("-")} ${chalk4.dim("removed")}`);
3803
4199
  console.log("");
3804
4200
  }
3805
4201
  function buildSkillContent(skill) {
@@ -3865,7 +4261,7 @@ function collectSetupFiles(setup) {
3865
4261
  }
3866
4262
 
3867
4263
  // src/commands/undo.ts
3868
- import chalk4 from "chalk";
4264
+ import chalk5 from "chalk";
3869
4265
  import ora2 from "ora";
3870
4266
  function undoCommand() {
3871
4267
  const spinner = ora2("Reverting setup...").start();
@@ -3877,26 +4273,26 @@ function undoCommand() {
3877
4273
  }
3878
4274
  spinner.succeed("Setup reverted successfully.\n");
3879
4275
  if (restored.length > 0) {
3880
- console.log(chalk4.cyan(" Restored from backup:"));
4276
+ console.log(chalk5.cyan(" Restored from backup:"));
3881
4277
  for (const file of restored) {
3882
- console.log(` ${chalk4.green("\u21A9")} ${file}`);
4278
+ console.log(` ${chalk5.green("\u21A9")} ${file}`);
3883
4279
  }
3884
4280
  }
3885
4281
  if (removed.length > 0) {
3886
- console.log(chalk4.cyan(" Removed:"));
4282
+ console.log(chalk5.cyan(" Removed:"));
3887
4283
  for (const file of removed) {
3888
- console.log(` ${chalk4.red("\u2717")} ${file}`);
4284
+ console.log(` ${chalk5.red("\u2717")} ${file}`);
3889
4285
  }
3890
4286
  }
3891
4287
  console.log("");
3892
4288
  } catch (err) {
3893
- spinner.fail(chalk4.red(err instanceof Error ? err.message : "Undo failed"));
4289
+ spinner.fail(chalk5.red(err instanceof Error ? err.message : "Undo failed"));
3894
4290
  throw new Error("__exit__");
3895
4291
  }
3896
4292
  }
3897
4293
 
3898
4294
  // src/commands/status.ts
3899
- import chalk5 from "chalk";
4295
+ import chalk6 from "chalk";
3900
4296
  import fs18 from "fs";
3901
4297
  async function statusCommand(options) {
3902
4298
  const config = loadConfig();
@@ -3910,39 +4306,39 @@ async function statusCommand(options) {
3910
4306
  }, null, 2));
3911
4307
  return;
3912
4308
  }
3913
- console.log(chalk5.bold("\nCaliber Status\n"));
4309
+ console.log(chalk6.bold("\nCaliber Status\n"));
3914
4310
  if (config) {
3915
- console.log(` LLM: ${chalk5.green(config.provider)} (${config.model})`);
4311
+ console.log(` LLM: ${chalk6.green(config.provider)} (${config.model})`);
3916
4312
  } else {
3917
- console.log(` LLM: ${chalk5.yellow("Not configured")} \u2014 run \`caliber config\``);
4313
+ console.log(` LLM: ${chalk6.yellow("Not configured")} \u2014 run \`caliber config\``);
3918
4314
  }
3919
4315
  if (!manifest) {
3920
- console.log(` Setup: ${chalk5.dim("No setup applied")}`);
3921
- console.log(chalk5.dim("\n Run `caliber init` to get started.\n"));
4316
+ console.log(` Setup: ${chalk6.dim("No setup applied")}`);
4317
+ console.log(chalk6.dim("\n Run `caliber init` to get started.\n"));
3922
4318
  return;
3923
4319
  }
3924
- console.log(` Files managed: ${chalk5.cyan(manifest.entries.length.toString())}`);
4320
+ console.log(` Files managed: ${chalk6.cyan(manifest.entries.length.toString())}`);
3925
4321
  for (const entry of manifest.entries) {
3926
4322
  const exists = fs18.existsSync(entry.path);
3927
- const icon = exists ? chalk5.green("\u2713") : chalk5.red("\u2717");
4323
+ const icon = exists ? chalk6.green("\u2713") : chalk6.red("\u2717");
3928
4324
  console.log(` ${icon} ${entry.path} (${entry.action})`);
3929
4325
  }
3930
4326
  console.log("");
3931
4327
  }
3932
4328
 
3933
4329
  // src/commands/regenerate.ts
3934
- import chalk6 from "chalk";
4330
+ import chalk7 from "chalk";
3935
4331
  import ora3 from "ora";
3936
4332
  import confirm from "@inquirer/confirm";
3937
4333
  async function regenerateCommand(options) {
3938
4334
  const config = loadConfig();
3939
4335
  if (!config) {
3940
- console.log(chalk6.red("No LLM provider configured. Run `caliber config` or set ANTHROPIC_API_KEY."));
4336
+ console.log(chalk7.red("No LLM provider configured. Run `caliber config` (e.g. choose Cursor) or set ANTHROPIC_API_KEY."));
3941
4337
  throw new Error("__exit__");
3942
4338
  }
3943
4339
  const manifest = readManifest();
3944
4340
  if (!manifest) {
3945
- console.log(chalk6.yellow("No existing setup found. Run `caliber init` first."));
4341
+ console.log(chalk7.yellow("No existing setup found. Run `caliber init` first."));
3946
4342
  throw new Error("__exit__");
3947
4343
  }
3948
4344
  const spinner = ora3("Re-analyzing project...").start();
@@ -3985,26 +4381,26 @@ async function regenerateCommand(options) {
3985
4381
  }
3986
4382
  genSpinner.succeed("Setup regenerated");
3987
4383
  if (options.dryRun) {
3988
- console.log(chalk6.yellow("\n[Dry run] Would write:"));
4384
+ console.log(chalk7.yellow("\n[Dry run] Would write:"));
3989
4385
  console.log(JSON.stringify(generatedSetup, null, 2));
3990
4386
  return;
3991
4387
  }
3992
4388
  const shouldApply = await confirm({ message: "Apply regenerated setup?", default: true });
3993
4389
  if (!shouldApply) {
3994
- console.log(chalk6.dim("Regeneration cancelled."));
4390
+ console.log(chalk7.dim("Regeneration cancelled."));
3995
4391
  return;
3996
4392
  }
3997
4393
  const writeSpinner = ora3("Updating config files...").start();
3998
4394
  const result = writeSetup(generatedSetup);
3999
4395
  writeSpinner.succeed("Config files updated");
4000
4396
  for (const file of result.written) {
4001
- console.log(` ${chalk6.green("\u2713")} ${file}`);
4397
+ console.log(` ${chalk7.green("\u2713")} ${file}`);
4002
4398
  }
4003
4399
  console.log("");
4004
4400
  }
4005
4401
 
4006
4402
  // src/commands/recommend.ts
4007
- import chalk7 from "chalk";
4403
+ import chalk8 from "chalk";
4008
4404
  import ora4 from "ora";
4009
4405
  import { mkdirSync, readFileSync as readFileSync7, existsSync as existsSync9, writeFileSync } from "fs";
4010
4406
  import { join as join8, dirname as dirname2 } from "path";
@@ -4187,7 +4583,7 @@ async function recommendCommand(options) {
4187
4583
  ...extractTopDeps()
4188
4584
  ].filter(Boolean))];
4189
4585
  if (technologies.length === 0) {
4190
- console.log(chalk7.yellow("Could not detect any languages or dependencies. Try running from a project root."));
4586
+ console.log(chalk8.yellow("Could not detect any languages or dependencies. Try running from a project root."));
4191
4587
  throw new Error("__exit__");
4192
4588
  }
4193
4589
  const spinner = ora4("Searching for skills...").start();
@@ -4213,18 +4609,18 @@ async function interactiveSelect(recs) {
4213
4609
  let lineCount = 0;
4214
4610
  function render() {
4215
4611
  const lines = [];
4216
- lines.push(chalk7.bold(" Recommendations"));
4612
+ lines.push(chalk8.bold(" Recommendations"));
4217
4613
  lines.push("");
4218
- lines.push(` ${chalk7.dim("Name".padEnd(30))} ${chalk7.dim("Technology".padEnd(18))} ${chalk7.dim("Source")}`);
4219
- lines.push(chalk7.dim(" " + "\u2500".repeat(70)));
4614
+ lines.push(` ${chalk8.dim("Name".padEnd(30))} ${chalk8.dim("Technology".padEnd(18))} ${chalk8.dim("Source")}`);
4615
+ lines.push(chalk8.dim(" " + "\u2500".repeat(70)));
4220
4616
  for (let i = 0; i < recs.length; i++) {
4221
4617
  const rec = recs[i];
4222
- const check = selected.has(i) ? chalk7.green("[x]") : "[ ]";
4223
- const ptr = i === cursor ? chalk7.cyan("\u276F") : " ";
4224
- lines.push(` ${ptr} ${check} ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${chalk7.dim(rec.source_url || "")}`);
4618
+ const check = selected.has(i) ? chalk8.green("[x]") : "[ ]";
4619
+ const ptr = i === cursor ? chalk8.cyan("\u276F") : " ";
4620
+ lines.push(` ${ptr} ${check} ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${chalk8.dim(rec.source_url || "")}`);
4225
4621
  }
4226
4622
  lines.push("");
4227
- lines.push(chalk7.dim(" \u2191\u2193 navigate \u23B5 toggle a all n none \u23CE install q cancel"));
4623
+ lines.push(chalk8.dim(" \u2191\u2193 navigate \u23B5 toggle a all n none \u23CE install q cancel"));
4228
4624
  return lines.join("\n");
4229
4625
  }
4230
4626
  function draw(initial) {
@@ -4273,7 +4669,7 @@ async function interactiveSelect(recs) {
4273
4669
  case "\n":
4274
4670
  cleanup();
4275
4671
  if (selected.size === 0) {
4276
- console.log(chalk7.dim("\n No skills selected.\n"));
4672
+ console.log(chalk8.dim("\n No skills selected.\n"));
4277
4673
  resolve2(null);
4278
4674
  } else {
4279
4675
  resolve2(Array.from(selected).sort().map((i) => recs[i]));
@@ -4283,7 +4679,7 @@ async function interactiveSelect(recs) {
4283
4679
  case "\x1B":
4284
4680
  case "":
4285
4681
  cleanup();
4286
- console.log(chalk7.dim("\n Cancelled.\n"));
4682
+ console.log(chalk8.dim("\n Cancelled.\n"));
4287
4683
  resolve2(null);
4288
4684
  break;
4289
4685
  }
@@ -4323,35 +4719,35 @@ async function installSkills(recs, platforms) {
4323
4719
  if (installed.length > 0) {
4324
4720
  spinner.succeed(`Installed ${installed.length} file${installed.length > 1 ? "s" : ""}`);
4325
4721
  for (const p of installed) {
4326
- console.log(chalk7.green(` \u2713 ${p}`));
4722
+ console.log(chalk8.green(` \u2713 ${p}`));
4327
4723
  }
4328
4724
  } else {
4329
4725
  spinner.fail("No skills were installed");
4330
4726
  }
4331
4727
  for (const w of warnings) {
4332
- console.log(chalk7.yellow(` \u26A0 ${w}`));
4728
+ console.log(chalk8.yellow(` \u26A0 ${w}`));
4333
4729
  }
4334
4730
  console.log("");
4335
4731
  }
4336
4732
  function printRecommendations(recs) {
4337
- console.log(chalk7.bold("\n Recommendations\n"));
4733
+ console.log(chalk8.bold("\n Recommendations\n"));
4338
4734
  console.log(
4339
- ` ${chalk7.dim("Name".padEnd(30))} ${chalk7.dim("Technology".padEnd(18))} ${chalk7.dim("Source")}`
4735
+ ` ${chalk8.dim("Name".padEnd(30))} ${chalk8.dim("Technology".padEnd(18))} ${chalk8.dim("Source")}`
4340
4736
  );
4341
- console.log(chalk7.dim(" " + "\u2500".repeat(70)));
4737
+ console.log(chalk8.dim(" " + "\u2500".repeat(70)));
4342
4738
  for (const rec of recs) {
4343
4739
  console.log(
4344
- ` ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${chalk7.dim(rec.source_url || "")}`
4740
+ ` ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${chalk8.dim(rec.source_url || "")}`
4345
4741
  );
4346
4742
  if (rec.reason) {
4347
- console.log(` ${chalk7.dim(" " + rec.reason)}`);
4743
+ console.log(` ${chalk8.dim(" " + rec.reason)}`);
4348
4744
  }
4349
4745
  }
4350
4746
  console.log("");
4351
4747
  }
4352
4748
 
4353
4749
  // src/commands/score.ts
4354
- import chalk8 from "chalk";
4750
+ import chalk9 from "chalk";
4355
4751
  async function scoreCommand(options) {
4356
4752
  const dir = process.cwd();
4357
4753
  const target = options.agent ?? readState()?.targetAgent;
@@ -4365,14 +4761,14 @@ async function scoreCommand(options) {
4365
4761
  return;
4366
4762
  }
4367
4763
  displayScore(result);
4368
- const separator = chalk8.gray(" " + "\u2500".repeat(53));
4764
+ const separator = chalk9.gray(" " + "\u2500".repeat(53));
4369
4765
  console.log(separator);
4370
4766
  if (result.score < 40) {
4371
- console.log(chalk8.gray(" Run ") + chalk8.hex("#f97316")("caliber init") + chalk8.gray(" to generate a complete, optimized setup."));
4767
+ console.log(chalk9.gray(" Run ") + chalk9.hex("#f97316")("caliber init") + chalk9.gray(" to generate a complete, optimized setup."));
4372
4768
  } else if (result.score < 70) {
4373
- console.log(chalk8.gray(" Run ") + chalk8.hex("#f97316")("caliber init") + chalk8.gray(" to improve your setup."));
4769
+ console.log(chalk9.gray(" Run ") + chalk9.hex("#f97316")("caliber init") + chalk9.gray(" to improve your setup."));
4374
4770
  } else {
4375
- console.log(chalk8.green(" Looking good!") + chalk8.gray(" Run ") + chalk8.hex("#f97316")("caliber update") + chalk8.gray(" to keep it fresh."));
4771
+ console.log(chalk9.green(" Looking good!") + chalk9.gray(" Run ") + chalk9.hex("#f97316")("caliber update") + chalk9.gray(" to keep it fresh."));
4376
4772
  }
4377
4773
  console.log("");
4378
4774
  }
@@ -4380,11 +4776,11 @@ async function scoreCommand(options) {
4380
4776
  // src/commands/refresh.ts
4381
4777
  import fs21 from "fs";
4382
4778
  import path18 from "path";
4383
- import chalk9 from "chalk";
4779
+ import chalk10 from "chalk";
4384
4780
  import ora5 from "ora";
4385
4781
 
4386
4782
  // src/lib/git-diff.ts
4387
- import { execSync as execSync6 } from "child_process";
4783
+ import { execSync as execSync8 } from "child_process";
4388
4784
  var MAX_DIFF_BYTES = 1e5;
4389
4785
  var DOC_PATTERNS = [
4390
4786
  "CLAUDE.md",
@@ -4398,7 +4794,7 @@ function excludeArgs() {
4398
4794
  }
4399
4795
  function safeExec(cmd) {
4400
4796
  try {
4401
- return execSync6(cmd, {
4797
+ return execSync8(cmd, {
4402
4798
  encoding: "utf-8",
4403
4799
  stdio: ["pipe", "pipe", "pipe"],
4404
4800
  maxBuffer: 10 * 1024 * 1024
@@ -4575,7 +4971,7 @@ function discoverGitRepos(parentDir) {
4575
4971
  }
4576
4972
  async function refreshSingleRepo(repoDir, options) {
4577
4973
  const quiet = !!options.quiet;
4578
- const prefix = options.label ? `${chalk9.bold(options.label)} ` : "";
4974
+ const prefix = options.label ? `${chalk10.bold(options.label)} ` : "";
4579
4975
  const state = readState();
4580
4976
  const lastSha = state?.lastRefreshSha ?? null;
4581
4977
  const diff = collectDiff(lastSha);
@@ -4584,7 +4980,7 @@ async function refreshSingleRepo(repoDir, options) {
4584
4980
  if (currentSha) {
4585
4981
  writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
4586
4982
  }
4587
- log(quiet, chalk9.dim(`${prefix}No changes since last refresh.`));
4983
+ log(quiet, chalk10.dim(`${prefix}No changes since last refresh.`));
4588
4984
  return;
4589
4985
  }
4590
4986
  const spinner = quiet ? null : ora5(`${prefix}Analyzing changes...`).start();
@@ -4616,10 +5012,10 @@ async function refreshSingleRepo(repoDir, options) {
4616
5012
  if (options.dryRun) {
4617
5013
  spinner?.info(`${prefix}Dry run \u2014 would update:`);
4618
5014
  for (const doc of response.docsUpdated) {
4619
- console.log(` ${chalk9.yellow("~")} ${doc}`);
5015
+ console.log(` ${chalk10.yellow("~")} ${doc}`);
4620
5016
  }
4621
5017
  if (response.changesSummary) {
4622
- console.log(chalk9.dim(`
5018
+ console.log(chalk10.dim(`
4623
5019
  ${response.changesSummary}`));
4624
5020
  }
4625
5021
  return;
@@ -4627,10 +5023,10 @@ async function refreshSingleRepo(repoDir, options) {
4627
5023
  const written = writeRefreshDocs(response.updatedDocs);
4628
5024
  spinner?.succeed(`${prefix}Updated ${written.length} doc${written.length === 1 ? "" : "s"}`);
4629
5025
  for (const file of written) {
4630
- log(quiet, ` ${chalk9.green("\u2713")} ${file}`);
5026
+ log(quiet, ` ${chalk10.green("\u2713")} ${file}`);
4631
5027
  }
4632
5028
  if (response.changesSummary) {
4633
- log(quiet, chalk9.dim(`
5029
+ log(quiet, chalk10.dim(`
4634
5030
  ${response.changesSummary}`));
4635
5031
  }
4636
5032
  if (currentSha) {
@@ -4643,7 +5039,7 @@ async function refreshCommand(options) {
4643
5039
  const config = loadConfig();
4644
5040
  if (!config) {
4645
5041
  if (quiet) return;
4646
- console.log(chalk9.red("No LLM provider configured. Run `caliber config` or set ANTHROPIC_API_KEY."));
5042
+ console.log(chalk10.red("No LLM provider configured. Run `caliber config` (e.g. choose Cursor) or set an API key."));
4647
5043
  throw new Error("__exit__");
4648
5044
  }
4649
5045
  if (isGitRepo()) {
@@ -4653,10 +5049,10 @@ async function refreshCommand(options) {
4653
5049
  const repos = discoverGitRepos(process.cwd());
4654
5050
  if (repos.length === 0) {
4655
5051
  if (quiet) return;
4656
- console.log(chalk9.red("Not inside a git repository and no git repos found in child directories."));
5052
+ console.log(chalk10.red("Not inside a git repository and no git repos found in child directories."));
4657
5053
  throw new Error("__exit__");
4658
5054
  }
4659
- log(quiet, chalk9.dim(`Found ${repos.length} git repo${repos.length === 1 ? "" : "s"}
5055
+ log(quiet, chalk10.dim(`Found ${repos.length} git repo${repos.length === 1 ? "" : "s"}
4660
5056
  `));
4661
5057
  const originalDir = process.cwd();
4662
5058
  for (const repo of repos) {
@@ -4666,7 +5062,7 @@ async function refreshCommand(options) {
4666
5062
  await refreshSingleRepo(repo, { ...options, label: repoName });
4667
5063
  } catch (err) {
4668
5064
  if (err instanceof Error && err.message === "__exit__") continue;
4669
- log(quiet, chalk9.yellow(`${repoName}: refresh failed \u2014 ${err instanceof Error ? err.message : "unknown error"}`));
5065
+ log(quiet, chalk10.yellow(`${repoName}: refresh failed \u2014 ${err instanceof Error ? err.message : "unknown error"}`));
4670
5066
  }
4671
5067
  }
4672
5068
  process.chdir(originalDir);
@@ -4674,153 +5070,107 @@ async function refreshCommand(options) {
4674
5070
  if (err instanceof Error && err.message === "__exit__") throw err;
4675
5071
  if (quiet) return;
4676
5072
  const msg = err instanceof Error ? err.message : "Unknown error";
4677
- console.log(chalk9.red(`Refresh failed: ${msg}`));
5073
+ console.log(chalk10.red(`Refresh failed: ${msg}`));
4678
5074
  throw new Error("__exit__");
4679
5075
  }
4680
5076
  }
4681
5077
 
4682
5078
  // src/commands/hooks.ts
4683
- import chalk10 from "chalk";
5079
+ import chalk11 from "chalk";
4684
5080
  async function hooksInstallCommand() {
4685
5081
  const result = installHook();
4686
5082
  if (result.alreadyInstalled) {
4687
- console.log(chalk10.dim("Claude Code hook already installed."));
5083
+ console.log(chalk11.dim("Claude Code hook already installed."));
4688
5084
  return;
4689
5085
  }
4690
- console.log(chalk10.green("\u2713") + " SessionEnd hook installed in .claude/settings.json");
4691
- console.log(chalk10.dim(" Docs will auto-refresh when Claude Code sessions end."));
5086
+ console.log(chalk11.green("\u2713") + " SessionEnd hook installed in .claude/settings.json");
5087
+ console.log(chalk11.dim(" Docs will auto-refresh when Claude Code sessions end."));
4692
5088
  }
4693
5089
  async function hooksRemoveCommand() {
4694
5090
  const result = removeHook();
4695
5091
  if (result.notFound) {
4696
- console.log(chalk10.dim("Claude Code hook not found."));
5092
+ console.log(chalk11.dim("Claude Code hook not found."));
4697
5093
  return;
4698
5094
  }
4699
- console.log(chalk10.green("\u2713") + " SessionEnd hook removed from .claude/settings.json");
5095
+ console.log(chalk11.green("\u2713") + " SessionEnd hook removed from .claude/settings.json");
4700
5096
  }
4701
5097
  async function hooksInstallPrecommitCommand() {
4702
5098
  const result = installPreCommitHook();
4703
5099
  if (result.alreadyInstalled) {
4704
- console.log(chalk10.dim("Pre-commit hook already installed."));
5100
+ console.log(chalk11.dim("Pre-commit hook already installed."));
4705
5101
  return;
4706
5102
  }
4707
5103
  if (!result.installed) {
4708
- console.log(chalk10.red("Failed to install pre-commit hook (not a git repository?)."));
5104
+ console.log(chalk11.red("Failed to install pre-commit hook (not a git repository?)."));
4709
5105
  return;
4710
5106
  }
4711
- console.log(chalk10.green("\u2713") + " Pre-commit hook installed in .git/hooks/pre-commit");
4712
- console.log(chalk10.dim(" Docs will auto-refresh before each commit via LLM."));
5107
+ console.log(chalk11.green("\u2713") + " Pre-commit hook installed in .git/hooks/pre-commit");
5108
+ console.log(chalk11.dim(" Docs will auto-refresh before each commit via LLM."));
4713
5109
  }
4714
5110
  async function hooksRemovePrecommitCommand() {
4715
5111
  const result = removePreCommitHook();
4716
5112
  if (result.notFound) {
4717
- console.log(chalk10.dim("Pre-commit hook not found."));
5113
+ console.log(chalk11.dim("Pre-commit hook not found."));
4718
5114
  return;
4719
5115
  }
4720
- console.log(chalk10.green("\u2713") + " Pre-commit hook removed from .git/hooks/pre-commit");
5116
+ console.log(chalk11.green("\u2713") + " Pre-commit hook removed from .git/hooks/pre-commit");
4721
5117
  }
4722
5118
  async function hooksStatusCommand() {
4723
5119
  const claudeInstalled = isHookInstalled();
4724
5120
  const precommitInstalled = isPreCommitHookInstalled();
4725
5121
  if (claudeInstalled) {
4726
- console.log(chalk10.green("\u2713") + " Claude Code hook is " + chalk10.green("installed"));
5122
+ console.log(chalk11.green("\u2713") + " Claude Code hook is " + chalk11.green("installed"));
4727
5123
  } else {
4728
- console.log(chalk10.dim("\u2717") + " Claude Code hook is " + chalk10.yellow("not installed"));
5124
+ console.log(chalk11.dim("\u2717") + " Claude Code hook is " + chalk11.yellow("not installed"));
4729
5125
  }
4730
5126
  if (precommitInstalled) {
4731
- console.log(chalk10.green("\u2713") + " Pre-commit hook is " + chalk10.green("installed"));
5127
+ console.log(chalk11.green("\u2713") + " Pre-commit hook is " + chalk11.green("installed"));
4732
5128
  } else {
4733
- console.log(chalk10.dim("\u2717") + " Pre-commit hook is " + chalk10.yellow("not installed"));
5129
+ console.log(chalk11.dim("\u2717") + " Pre-commit hook is " + chalk11.yellow("not installed"));
4734
5130
  }
4735
5131
  if (!claudeInstalled && !precommitInstalled) {
4736
- console.log(chalk10.dim("\n Run `caliber hooks install` or `caliber hooks install-precommit` to enable auto-refresh."));
5132
+ console.log(chalk11.dim("\n Run `caliber hooks install` or `caliber hooks install-precommit` to enable auto-refresh."));
4737
5133
  }
4738
5134
  }
4739
5135
 
4740
5136
  // src/commands/config.ts
4741
- import chalk11 from "chalk";
4742
- import readline2 from "readline";
4743
- import select2 from "@inquirer/select";
4744
- function promptInput2(question) {
4745
- const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
4746
- return new Promise((resolve2) => {
4747
- rl.question(chalk11.cyan(`${question} `), (answer) => {
4748
- rl.close();
4749
- resolve2(answer.trim());
4750
- });
4751
- });
4752
- }
5137
+ import chalk12 from "chalk";
4753
5138
  async function configCommand() {
4754
5139
  const existing = loadConfig();
4755
5140
  if (existing) {
4756
- console.log(chalk11.bold("\nCurrent Configuration\n"));
4757
- console.log(` Provider: ${chalk11.cyan(existing.provider)}`);
4758
- console.log(` Model: ${chalk11.cyan(existing.model)}`);
5141
+ console.log(chalk12.bold("\nCurrent Configuration\n"));
5142
+ console.log(` Provider: ${chalk12.cyan(existing.provider)}`);
5143
+ console.log(` Model: ${chalk12.cyan(existing.model)}`);
4759
5144
  if (existing.apiKey) {
4760
5145
  const masked = existing.apiKey.slice(0, 8) + "..." + existing.apiKey.slice(-4);
4761
- console.log(` API Key: ${chalk11.dim(masked)}`);
5146
+ console.log(` API Key: ${chalk12.dim(masked)}`);
5147
+ }
5148
+ if (existing.provider === "cursor") {
5149
+ console.log(` Seat: ${chalk12.dim("Cursor (agent acp)")}`);
5150
+ }
5151
+ if (existing.provider === "claude-cli") {
5152
+ console.log(` Seat: ${chalk12.dim("Claude Code (claude -p)")}`);
4762
5153
  }
4763
5154
  if (existing.baseUrl) {
4764
- console.log(` Base URL: ${chalk11.dim(existing.baseUrl)}`);
5155
+ console.log(` Base URL: ${chalk12.dim(existing.baseUrl)}`);
4765
5156
  }
4766
5157
  if (existing.vertexProjectId) {
4767
- console.log(` Vertex Project: ${chalk11.dim(existing.vertexProjectId)}`);
4768
- console.log(` Vertex Region: ${chalk11.dim(existing.vertexRegion || "us-east5")}`);
5158
+ console.log(` Vertex Project: ${chalk12.dim(existing.vertexProjectId)}`);
5159
+ console.log(` Vertex Region: ${chalk12.dim(existing.vertexRegion || "us-east5")}`);
4769
5160
  }
4770
- console.log(` Source: ${chalk11.dim(process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY || process.env.VERTEX_PROJECT_ID ? "environment variables" : getConfigFilePath())}`);
5161
+ console.log(` Source: ${chalk12.dim(process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY || process.env.VERTEX_PROJECT_ID || process.env.CALIBER_USE_CURSOR_SEAT || process.env.CALIBER_USE_CLAUDE_CLI ? "environment variables" : getConfigFilePath())}`);
4771
5162
  console.log("");
4772
5163
  }
4773
- const provider = await select2({
4774
- message: "Select LLM provider",
4775
- choices: [
4776
- { name: "Anthropic (Claude)", value: "anthropic" },
4777
- { name: "Google Vertex AI (Claude)", value: "vertex" },
4778
- { name: "OpenAI / OpenAI-compatible", value: "openai" }
4779
- ]
4780
- });
4781
- const config = { provider, model: "" };
4782
- switch (provider) {
4783
- case "anthropic": {
4784
- config.apiKey = await promptInput2("Anthropic API key:");
4785
- if (!config.apiKey) {
4786
- console.log(chalk11.red("API key is required."));
4787
- throw new Error("__exit__");
4788
- }
4789
- config.model = await promptInput2(`Model (default: ${DEFAULT_MODELS.anthropic}):`) || DEFAULT_MODELS.anthropic;
4790
- break;
4791
- }
4792
- case "vertex": {
4793
- config.vertexProjectId = await promptInput2("GCP Project ID:");
4794
- if (!config.vertexProjectId) {
4795
- console.log(chalk11.red("Project ID is required."));
4796
- throw new Error("__exit__");
4797
- }
4798
- config.vertexRegion = await promptInput2("Region (default: us-east5):") || "us-east5";
4799
- config.vertexCredentials = await promptInput2("Service account credentials JSON (or leave empty for ADC):") || void 0;
4800
- config.model = await promptInput2(`Model (default: ${DEFAULT_MODELS.vertex}):`) || DEFAULT_MODELS.vertex;
4801
- break;
4802
- }
4803
- case "openai": {
4804
- config.apiKey = await promptInput2("API key:");
4805
- if (!config.apiKey) {
4806
- console.log(chalk11.red("API key is required."));
4807
- throw new Error("__exit__");
4808
- }
4809
- config.baseUrl = await promptInput2("Base URL (leave empty for OpenAI, or enter custom endpoint):") || void 0;
4810
- config.model = await promptInput2(`Model (default: ${DEFAULT_MODELS.openai}):`) || DEFAULT_MODELS.openai;
4811
- break;
4812
- }
4813
- }
4814
- writeConfigFile(config);
4815
- console.log(chalk11.green("\n\u2713 Configuration saved"));
4816
- console.log(chalk11.dim(` ${getConfigFilePath()}
5164
+ await runInteractiveProviderSetup();
5165
+ console.log(chalk12.green("\n\u2713 Configuration saved"));
5166
+ console.log(chalk12.dim(` ${getConfigFilePath()}
4817
5167
  `));
4818
- console.log(chalk11.dim(" You can also set environment variables instead:"));
4819
- console.log(chalk11.dim(" ANTHROPIC_API_KEY, OPENAI_API_KEY, or VERTEX_PROJECT_ID\n"));
5168
+ console.log(chalk12.dim(" You can also set environment variables instead:"));
5169
+ console.log(chalk12.dim(" ANTHROPIC_API_KEY, OPENAI_API_KEY, VERTEX_PROJECT_ID, CALIBER_USE_CURSOR_SEAT=1, or CALIBER_USE_CLAUDE_CLI=1\n"));
4820
5170
  }
4821
5171
 
4822
5172
  // src/commands/learn.ts
4823
- import chalk12 from "chalk";
5173
+ import chalk13 from "chalk";
4824
5174
 
4825
5175
  // src/learner/stdin.ts
4826
5176
  var STDIN_TIMEOUT_MS = 5e3;
@@ -5121,46 +5471,46 @@ async function learnFinalizeCommand() {
5121
5471
  async function learnInstallCommand() {
5122
5472
  const result = installLearningHooks();
5123
5473
  if (result.alreadyInstalled) {
5124
- console.log(chalk12.dim("Learning hooks already installed."));
5474
+ console.log(chalk13.dim("Learning hooks already installed."));
5125
5475
  return;
5126
5476
  }
5127
- console.log(chalk12.green("\u2713") + " Learning hooks installed in .claude/settings.json");
5128
- console.log(chalk12.dim(" PostToolUse, PostToolUseFailure, and SessionEnd hooks active."));
5129
- console.log(chalk12.dim(" Session learnings will be written to CLAUDE.md and skills."));
5477
+ console.log(chalk13.green("\u2713") + " Learning hooks installed in .claude/settings.json");
5478
+ console.log(chalk13.dim(" PostToolUse, PostToolUseFailure, and SessionEnd hooks active."));
5479
+ console.log(chalk13.dim(" Session learnings will be written to CLAUDE.md and skills."));
5130
5480
  }
5131
5481
  async function learnRemoveCommand() {
5132
5482
  const result = removeLearningHooks();
5133
5483
  if (result.notFound) {
5134
- console.log(chalk12.dim("Learning hooks not found."));
5484
+ console.log(chalk13.dim("Learning hooks not found."));
5135
5485
  return;
5136
5486
  }
5137
- console.log(chalk12.green("\u2713") + " Learning hooks removed from .claude/settings.json");
5487
+ console.log(chalk13.green("\u2713") + " Learning hooks removed from .claude/settings.json");
5138
5488
  }
5139
5489
  async function learnStatusCommand() {
5140
5490
  const installed = areLearningHooksInstalled();
5141
5491
  const state = readState2();
5142
5492
  const eventCount = getEventCount();
5143
- console.log(chalk12.bold("Session Learning Status"));
5493
+ console.log(chalk13.bold("Session Learning Status"));
5144
5494
  console.log();
5145
5495
  if (installed) {
5146
- console.log(chalk12.green("\u2713") + " Learning hooks are " + chalk12.green("installed"));
5496
+ console.log(chalk13.green("\u2713") + " Learning hooks are " + chalk13.green("installed"));
5147
5497
  } else {
5148
- console.log(chalk12.dim("\u2717") + " Learning hooks are " + chalk12.yellow("not installed"));
5149
- console.log(chalk12.dim(" Run `caliber learn install` to enable session learning."));
5498
+ console.log(chalk13.dim("\u2717") + " Learning hooks are " + chalk13.yellow("not installed"));
5499
+ console.log(chalk13.dim(" Run `caliber learn install` to enable session learning."));
5150
5500
  }
5151
5501
  console.log();
5152
- console.log(`Events recorded: ${chalk12.cyan(String(eventCount))}`);
5153
- console.log(`Total this session: ${chalk12.cyan(String(state.eventCount))}`);
5502
+ console.log(`Events recorded: ${chalk13.cyan(String(eventCount))}`);
5503
+ console.log(`Total this session: ${chalk13.cyan(String(state.eventCount))}`);
5154
5504
  if (state.lastAnalysisTimestamp) {
5155
- console.log(`Last analysis: ${chalk12.cyan(state.lastAnalysisTimestamp)}`);
5505
+ console.log(`Last analysis: ${chalk13.cyan(state.lastAnalysisTimestamp)}`);
5156
5506
  } else {
5157
- console.log(`Last analysis: ${chalk12.dim("none")}`);
5507
+ console.log(`Last analysis: ${chalk13.dim("none")}`);
5158
5508
  }
5159
5509
  const learnedSection = readLearnedSection();
5160
5510
  if (learnedSection) {
5161
5511
  const lineCount = learnedSection.split("\n").filter(Boolean).length;
5162
5512
  console.log(`
5163
- Learned items in CLAUDE.md: ${chalk12.cyan(String(lineCount))}`);
5513
+ Learned items in CLAUDE.md: ${chalk13.cyan(String(lineCount))}`);
5164
5514
  }
5165
5515
  }
5166
5516
 
@@ -5197,8 +5547,8 @@ learn.command("status").description("Show learning system status").action(learnS
5197
5547
  import fs25 from "fs";
5198
5548
  import path22 from "path";
5199
5549
  import { fileURLToPath as fileURLToPath2 } from "url";
5200
- import { execSync as execSync7 } from "child_process";
5201
- import chalk13 from "chalk";
5550
+ import { execSync as execSync9 } from "child_process";
5551
+ import chalk14 from "chalk";
5202
5552
  import ora6 from "ora";
5203
5553
  import confirm2 from "@inquirer/confirm";
5204
5554
  var __dirname_vc = path22.dirname(fileURLToPath2(import.meta.url));
@@ -5207,7 +5557,7 @@ var pkg2 = JSON.parse(
5207
5557
  );
5208
5558
  function getInstalledVersion() {
5209
5559
  try {
5210
- const globalRoot = execSync7("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
5560
+ const globalRoot = execSync9("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
5211
5561
  const pkgPath = path22.join(globalRoot, "@rely-ai", "caliber", "package.json");
5212
5562
  return JSON.parse(fs25.readFileSync(pkgPath, "utf-8")).version;
5213
5563
  } catch {
@@ -5232,17 +5582,17 @@ async function checkForUpdates() {
5232
5582
  const isInteractive = process.stdin.isTTY === true;
5233
5583
  if (!isInteractive) {
5234
5584
  console.log(
5235
- chalk13.yellow(
5585
+ chalk14.yellow(
5236
5586
  `
5237
5587
  Update available: ${current} -> ${latest}
5238
- Run ${chalk13.bold("npm install -g @rely-ai/caliber")} to upgrade.
5588
+ Run ${chalk14.bold("npm install -g @rely-ai/caliber")} to upgrade.
5239
5589
  `
5240
5590
  )
5241
5591
  );
5242
5592
  return;
5243
5593
  }
5244
5594
  console.log(
5245
- chalk13.yellow(`
5595
+ chalk14.yellow(`
5246
5596
  Update available: ${current} -> ${latest}`)
5247
5597
  );
5248
5598
  const shouldUpdate = await confirm2({ message: "Would you like to update now? (Y/n)", default: true });
@@ -5252,7 +5602,7 @@ Update available: ${current} -> ${latest}`)
5252
5602
  }
5253
5603
  const spinner = ora6("Updating caliber...").start();
5254
5604
  try {
5255
- execSync7(`npm install -g @rely-ai/caliber@${latest}`, {
5605
+ execSync9(`npm install -g @rely-ai/caliber@${latest}`, {
5256
5606
  stdio: "pipe",
5257
5607
  timeout: 12e4,
5258
5608
  env: { ...process.env, npm_config_fund: "false", npm_config_audit: "false" }
@@ -5260,16 +5610,16 @@ Update available: ${current} -> ${latest}`)
5260
5610
  const installed = getInstalledVersion();
5261
5611
  if (installed !== latest) {
5262
5612
  spinner.fail(`Update incomplete \u2014 got ${installed ?? "unknown"}, expected ${latest}`);
5263
- console.log(chalk13.yellow(`Run ${chalk13.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually.
5613
+ console.log(chalk14.yellow(`Run ${chalk14.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually.
5264
5614
  `));
5265
5615
  return;
5266
5616
  }
5267
- spinner.succeed(chalk13.green(`Updated to ${latest}`));
5617
+ spinner.succeed(chalk14.green(`Updated to ${latest}`));
5268
5618
  const args = process.argv.slice(2);
5269
- console.log(chalk13.dim(`
5619
+ console.log(chalk14.dim(`
5270
5620
  Restarting: caliber ${args.join(" ")}
5271
5621
  `));
5272
- execSync7(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
5622
+ execSync9(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
5273
5623
  stdio: "inherit",
5274
5624
  env: { ...process.env, CALIBER_SKIP_UPDATE_CHECK: "1" }
5275
5625
  });
@@ -5279,11 +5629,11 @@ Restarting: caliber ${args.join(" ")}
5279
5629
  if (err instanceof Error) {
5280
5630
  const stderr = err.stderr;
5281
5631
  const errMsg = stderr ? String(stderr).trim().split("\n").pop() : err.message.split("\n")[0];
5282
- if (errMsg && !errMsg.includes("SIGTERM")) console.log(chalk13.dim(` ${errMsg}`));
5632
+ if (errMsg && !errMsg.includes("SIGTERM")) console.log(chalk14.dim(` ${errMsg}`));
5283
5633
  }
5284
5634
  console.log(
5285
- chalk13.yellow(
5286
- `Run ${chalk13.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually to upgrade.
5635
+ chalk14.yellow(
5636
+ `Run ${chalk14.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually to upgrade.
5287
5637
  `
5288
5638
  )
5289
5639
  );