@rely-ai/caliber 1.23.0-dev.1773793983 → 1.23.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 (2) hide show
  1. package/dist/bin.js +228 -65
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -169,6 +169,22 @@ var init_config = __esm({
169
169
  }
170
170
  });
171
171
 
172
+ // src/llm/types.ts
173
+ var types_exports = {};
174
+ __export(types_exports, {
175
+ isSeatBased: () => isSeatBased
176
+ });
177
+ function isSeatBased(provider) {
178
+ return SEAT_BASED_PROVIDERS.has(provider);
179
+ }
180
+ var SEAT_BASED_PROVIDERS;
181
+ var init_types = __esm({
182
+ "src/llm/types.ts"() {
183
+ "use strict";
184
+ SEAT_BASED_PROVIDERS = /* @__PURE__ */ new Set(["cursor", "claude-cli"]);
185
+ }
186
+ });
187
+
172
188
  // src/constants.ts
173
189
  var constants_exports = {};
174
190
  __export(constants_exports, {
@@ -1036,8 +1052,8 @@ var AnthropicProvider = class {
1036
1052
  cacheWriteTokens: u.cache_creation_input_tokens
1037
1053
  });
1038
1054
  }
1039
- const block = response.content[0];
1040
- return block.type === "text" ? block.text : "";
1055
+ const block = response.content?.[0];
1056
+ return block?.type === "text" ? block.text : "";
1041
1057
  }
1042
1058
  async listModels() {
1043
1059
  const models = [];
@@ -1140,8 +1156,8 @@ var VertexProvider = class {
1140
1156
  cacheWriteTokens: u.cache_creation_input_tokens
1141
1157
  });
1142
1158
  }
1143
- const block = response.content[0];
1144
- return block.type === "text" ? block.text : "";
1159
+ const block = response.content?.[0];
1160
+ return block?.type === "text" ? block.text : "";
1145
1161
  }
1146
1162
  async stream(options, callbacks) {
1147
1163
  const messages = options.messages ? [
@@ -1261,14 +1277,44 @@ var OpenAICompatProvider = class {
1261
1277
  // src/llm/cursor-acp.ts
1262
1278
  import { spawn, execSync as execSync3 } from "child_process";
1263
1279
  import os2 from "os";
1280
+
1281
+ // src/llm/seat-based-errors.ts
1282
+ var ERROR_PATTERNS = [
1283
+ { pattern: /not logged in|not authenticated|login required|unauthorized/i, message: "Authentication required. Run the login command for your provider to re-authenticate." },
1284
+ { pattern: /rate limit|too many requests|429/i, message: "Rate limit exceeded. Retrying..." },
1285
+ { pattern: /model.*not found|invalid model|model.*unavailable/i, message: "The requested model is not available. Run `caliber config` to select a different model." }
1286
+ ];
1287
+ function parseSeatBasedError(stderr, exitCode) {
1288
+ if (!stderr && exitCode === 0) return null;
1289
+ for (const { pattern, message } of ERROR_PATTERNS) {
1290
+ if (pattern.test(stderr)) return message;
1291
+ }
1292
+ return null;
1293
+ }
1294
+ function isRateLimitError(stderr) {
1295
+ return /rate limit|too many requests|429/i.test(stderr);
1296
+ }
1297
+
1298
+ // src/llm/cursor-acp.ts
1264
1299
  var AGENT_BIN = "agent";
1265
1300
  var IS_WINDOWS = process.platform === "win32";
1301
+ var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
1302
+ var SIGKILL_DELAY_MS = 5e3;
1303
+ var STDERR_MAX_BYTES = 10 * 1024;
1266
1304
  var CursorAcpProvider = class {
1267
1305
  defaultModel;
1268
1306
  cursorApiKey;
1307
+ timeoutMs;
1308
+ warmProcess = null;
1309
+ warmModel = null;
1269
1310
  constructor(config) {
1270
1311
  this.defaultModel = config.model || "sonnet-4.6";
1271
1312
  this.cursorApiKey = process.env.CURSOR_API_KEY ?? process.env.CURSOR_AUTH_TOKEN;
1313
+ const envTimeout = process.env.CALIBER_CURSOR_TIMEOUT_MS;
1314
+ this.timeoutMs = envTimeout ? parseInt(envTimeout, 10) : DEFAULT_TIMEOUT_MS;
1315
+ if (!Number.isFinite(this.timeoutMs) || this.timeoutMs < 1e3) {
1316
+ this.timeoutMs = DEFAULT_TIMEOUT_MS;
1317
+ }
1272
1318
  }
1273
1319
  async call(options) {
1274
1320
  const prompt = this.buildPrompt(options);
@@ -1280,6 +1326,29 @@ var CursorAcpProvider = class {
1280
1326
  const model = options.model || this.defaultModel;
1281
1327
  return this.runPrintStream(model, prompt, callbacks);
1282
1328
  }
1329
+ /**
1330
+ * Pre-spawn an agent process so it's ready when the first call comes.
1331
+ * Call this during fingerprint collection to hide spawn latency.
1332
+ */
1333
+ prewarm(model) {
1334
+ const targetModel = model || this.defaultModel;
1335
+ if (this.warmProcess && !this.warmProcess.killed && this.warmModel === targetModel) return;
1336
+ const args = this.buildArgs(targetModel, false);
1337
+ this.warmProcess = spawn(AGENT_BIN, args, {
1338
+ stdio: ["pipe", "pipe", "pipe"],
1339
+ env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
1340
+ ...IS_WINDOWS && { shell: true }
1341
+ });
1342
+ this.warmModel = targetModel;
1343
+ this.warmProcess.on("error", () => {
1344
+ this.warmProcess = null;
1345
+ this.warmModel = null;
1346
+ });
1347
+ this.warmProcess.on("close", () => {
1348
+ this.warmProcess = null;
1349
+ this.warmModel = null;
1350
+ });
1351
+ }
1283
1352
  buildArgs(model, streaming) {
1284
1353
  const args = ["--print", "--trust", "--workspace", os2.tmpdir()];
1285
1354
  if (model && model !== "auto" && model !== "default") {
@@ -1293,23 +1362,78 @@ var CursorAcpProvider = class {
1293
1362
  }
1294
1363
  return args;
1295
1364
  }
1365
+ takeWarmProcess(model, streaming) {
1366
+ if (!streaming && this.warmProcess && !this.warmProcess.killed && this.warmModel === model) {
1367
+ const proc = this.warmProcess;
1368
+ this.warmProcess = null;
1369
+ this.warmModel = null;
1370
+ return proc;
1371
+ }
1372
+ return null;
1373
+ }
1374
+ spawnAgent(model, streaming) {
1375
+ const warm = this.takeWarmProcess(model, streaming);
1376
+ if (warm) {
1377
+ const stderrChunks2 = [];
1378
+ warm.stderr?.on("data", (chunk) => {
1379
+ if (Buffer.concat(stderrChunks2).length < STDERR_MAX_BYTES) stderrChunks2.push(chunk);
1380
+ });
1381
+ return { child: warm, stderrChunks: stderrChunks2 };
1382
+ }
1383
+ const args = this.buildArgs(model, streaming);
1384
+ const child = spawn(AGENT_BIN, args, {
1385
+ stdio: ["pipe", "pipe", "pipe"],
1386
+ env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
1387
+ ...IS_WINDOWS && { shell: true }
1388
+ });
1389
+ const stderrChunks = [];
1390
+ child.stderr.on("data", (chunk) => {
1391
+ if (Buffer.concat(stderrChunks).length < STDERR_MAX_BYTES) stderrChunks.push(chunk);
1392
+ });
1393
+ return { child, stderrChunks };
1394
+ }
1395
+ killWithEscalation(child) {
1396
+ child.kill("SIGTERM");
1397
+ const killTimer = setTimeout(() => {
1398
+ if (!child.killed) child.kill("SIGKILL");
1399
+ }, SIGKILL_DELAY_MS);
1400
+ killTimer.unref();
1401
+ }
1402
+ buildErrorMessage(code, stderrChunks) {
1403
+ const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
1404
+ const parsed = parseSeatBasedError(stderr, code);
1405
+ if (parsed) return parsed;
1406
+ const base = `Cursor agent exited with code ${code}`;
1407
+ return stderr ? `${base}: ${stderr.slice(0, 200)}` : base;
1408
+ }
1296
1409
  runPrint(model, prompt) {
1297
1410
  return new Promise((resolve2, reject) => {
1298
- const args = this.buildArgs(model, false);
1299
- const child = spawn(AGENT_BIN, args, {
1300
- stdio: ["pipe", "pipe", "ignore"],
1301
- env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
1302
- ...IS_WINDOWS && { shell: true }
1303
- });
1411
+ const { child, stderrChunks } = this.spawnAgent(model, false);
1412
+ let settled = false;
1304
1413
  const chunks = [];
1305
- child.stdout.on("data", (data) => {
1306
- chunks.push(data);
1414
+ child.stdout.on("data", (data) => chunks.push(data));
1415
+ const timer = setTimeout(() => {
1416
+ this.killWithEscalation(child);
1417
+ if (!settled) {
1418
+ settled = true;
1419
+ reject(new Error(`Cursor agent timed out after ${this.timeoutMs / 1e3}s. Set CALIBER_CURSOR_TIMEOUT_MS to increase.`));
1420
+ }
1421
+ }, this.timeoutMs);
1422
+ timer.unref();
1423
+ child.on("error", (err) => {
1424
+ clearTimeout(timer);
1425
+ if (!settled) {
1426
+ settled = true;
1427
+ reject(err);
1428
+ }
1307
1429
  });
1308
- child.on("error", reject);
1309
1430
  child.on("close", (code) => {
1431
+ clearTimeout(timer);
1432
+ if (settled) return;
1433
+ settled = true;
1310
1434
  const output = Buffer.concat(chunks).toString("utf-8").trim();
1311
1435
  if (code !== 0 && !output) {
1312
- reject(new Error(`Cursor agent exited with code ${code}`));
1436
+ reject(new Error(this.buildErrorMessage(code, stderrChunks)));
1313
1437
  } else {
1314
1438
  resolve2(output);
1315
1439
  }
@@ -1320,14 +1444,20 @@ var CursorAcpProvider = class {
1320
1444
  }
1321
1445
  runPrintStream(model, prompt, callbacks) {
1322
1446
  return new Promise((resolve2, reject) => {
1323
- const args = this.buildArgs(model, true);
1324
- const child = spawn(AGENT_BIN, args, {
1325
- stdio: ["pipe", "pipe", "ignore"],
1326
- env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
1327
- ...IS_WINDOWS && { shell: true }
1328
- });
1447
+ const { child, stderrChunks } = this.spawnAgent(model, true);
1329
1448
  let buffer = "";
1330
1449
  let endCalled = false;
1450
+ let settled = false;
1451
+ const timer = setTimeout(() => {
1452
+ this.killWithEscalation(child);
1453
+ if (!settled) {
1454
+ settled = true;
1455
+ const err = new Error(`Cursor agent timed out after ${this.timeoutMs / 1e3}s. Set CALIBER_CURSOR_TIMEOUT_MS to increase.`);
1456
+ callbacks.onError(err);
1457
+ reject(err);
1458
+ }
1459
+ }, this.timeoutMs);
1460
+ timer.unref();
1331
1461
  child.stdout.on("data", (data) => {
1332
1462
  buffer += data.toString("utf-8");
1333
1463
  const lines = buffer.split("\n");
@@ -1352,16 +1482,26 @@ var CursorAcpProvider = class {
1352
1482
  }
1353
1483
  });
1354
1484
  child.on("error", (err) => {
1355
- callbacks.onError(err);
1356
- reject(err);
1485
+ clearTimeout(timer);
1486
+ if (!settled) {
1487
+ settled = true;
1488
+ callbacks.onError(err);
1489
+ reject(err);
1490
+ }
1357
1491
  });
1358
1492
  child.on("close", (code) => {
1493
+ clearTimeout(timer);
1494
+ if (settled) return;
1495
+ settled = true;
1359
1496
  if (buffer.trim()) {
1360
1497
  try {
1361
1498
  const event = JSON.parse(buffer);
1362
1499
  if (event.type === "assistant") {
1363
- const text = event.message?.content?.[0]?.text || event.content;
1364
- if (text) callbacks.onText(text);
1500
+ const isDelta = "timestamp_ms" in event;
1501
+ if (isDelta) {
1502
+ const text = event.message?.content?.[0]?.text || event.content;
1503
+ if (text) callbacks.onText(text);
1504
+ }
1365
1505
  } else if (event.type === "result") {
1366
1506
  endCalled = true;
1367
1507
  callbacks.onEnd({ stopReason: event.is_error ? "error" : "end_turn" });
@@ -1374,9 +1514,16 @@ var CursorAcpProvider = class {
1374
1514
  callbacks.onEnd({ stopReason: code === 0 ? "end_turn" : "error" });
1375
1515
  }
1376
1516
  if (code !== 0 && code !== null) {
1377
- const err = new Error(`Cursor agent exited with code ${code}`);
1378
- callbacks.onError(err);
1379
- reject(err);
1517
+ const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
1518
+ if (isRateLimitError(stderr)) {
1519
+ const err = new Error("Rate limit exceeded");
1520
+ callbacks.onError(err);
1521
+ reject(err);
1522
+ } else {
1523
+ const err = new Error(this.buildErrorMessage(code, stderrChunks));
1524
+ callbacks.onError(err);
1525
+ reject(err);
1526
+ }
1380
1527
  } else {
1381
1528
  resolve2();
1382
1529
  }
@@ -1403,28 +1550,48 @@ var CursorAcpProvider = class {
1403
1550
  };
1404
1551
  function isCursorAgentAvailable() {
1405
1552
  try {
1406
- const cmd = process.platform === "win32" ? `where ${AGENT_BIN}` : `which ${AGENT_BIN}`;
1553
+ const cmd = IS_WINDOWS ? `where ${AGENT_BIN}` : `which ${AGENT_BIN}`;
1407
1554
  execSync3(cmd, { stdio: "ignore" });
1408
1555
  return true;
1409
1556
  } catch {
1410
1557
  return false;
1411
1558
  }
1412
1559
  }
1560
+ function isCursorLoggedIn() {
1561
+ try {
1562
+ const result = execSync3(`${AGENT_BIN} status`, { stdio: ["ignore", "pipe", "ignore"], timeout: 5e3 });
1563
+ return !result.toString().includes("not logged in");
1564
+ } catch {
1565
+ return false;
1566
+ }
1567
+ }
1413
1568
 
1414
1569
  // src/llm/claude-cli.ts
1415
1570
  import { spawn as spawn2, execSync as execSync4 } from "child_process";
1416
1571
  var CLAUDE_CLI_BIN = "claude";
1417
- var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
1572
+ var DEFAULT_TIMEOUT_MS2 = 10 * 60 * 1e3;
1418
1573
  var IS_WINDOWS2 = process.platform === "win32";
1574
+ function spawnClaude(args) {
1575
+ return IS_WINDOWS2 ? spawn2([CLAUDE_CLI_BIN, ...args].join(" "), {
1576
+ cwd: process.cwd(),
1577
+ stdio: ["pipe", "pipe", "pipe"],
1578
+ env: process.env,
1579
+ shell: true
1580
+ }) : spawn2(CLAUDE_CLI_BIN, args, {
1581
+ cwd: process.cwd(),
1582
+ stdio: ["pipe", "pipe", "pipe"],
1583
+ env: process.env
1584
+ });
1585
+ }
1419
1586
  var ClaudeCliProvider = class {
1420
1587
  defaultModel;
1421
1588
  timeoutMs;
1422
1589
  constructor(config) {
1423
1590
  this.defaultModel = config.model || "default";
1424
1591
  const envTimeout = process.env.CALIBER_CLAUDE_CLI_TIMEOUT_MS;
1425
- this.timeoutMs = envTimeout ? parseInt(envTimeout, 10) : DEFAULT_TIMEOUT_MS;
1592
+ this.timeoutMs = envTimeout ? parseInt(envTimeout, 10) : DEFAULT_TIMEOUT_MS2;
1426
1593
  if (!Number.isFinite(this.timeoutMs) || this.timeoutMs < 1e3) {
1427
- this.timeoutMs = DEFAULT_TIMEOUT_MS;
1594
+ this.timeoutMs = DEFAULT_TIMEOUT_MS2;
1428
1595
  }
1429
1596
  }
1430
1597
  async call(options) {
@@ -1435,23 +1602,16 @@ var ClaudeCliProvider = class {
1435
1602
  const combined = this.buildCombinedPrompt(options);
1436
1603
  const args = ["-p"];
1437
1604
  if (options.model) args.push("--model", options.model);
1438
- const child = IS_WINDOWS2 ? spawn2([CLAUDE_CLI_BIN, ...args].join(" "), {
1439
- cwd: process.cwd(),
1440
- stdio: ["pipe", "pipe", "inherit"],
1441
- env: process.env,
1442
- shell: true
1443
- }) : spawn2(CLAUDE_CLI_BIN, args, {
1444
- cwd: process.cwd(),
1445
- stdio: ["pipe", "pipe", "inherit"],
1446
- env: process.env
1447
- });
1605
+ const child = spawnClaude(args);
1448
1606
  child.stdin.end(combined);
1449
1607
  let settled = false;
1450
1608
  const chunks = [];
1609
+ const stderrChunks = [];
1451
1610
  child.stdout.on("data", (chunk) => {
1452
1611
  chunks.push(chunk);
1453
1612
  callbacks.onText(chunk.toString("utf-8"));
1454
1613
  });
1614
+ child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
1455
1615
  const timer = setTimeout(() => {
1456
1616
  child.kill("SIGTERM");
1457
1617
  if (!settled) {
@@ -1477,45 +1637,40 @@ var ClaudeCliProvider = class {
1477
1637
  if (code === 0) {
1478
1638
  callbacks.onEnd({ stopReason: "end_turn" });
1479
1639
  } else {
1640
+ const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
1641
+ const friendly = parseSeatBasedError(stderr, code);
1480
1642
  const stdout = Buffer.concat(chunks).toString("utf-8").trim();
1481
- const msg = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
1482
- callbacks.onError(new Error(stdout ? `${msg}. Output: ${stdout.slice(0, 200)}` : msg));
1643
+ const base = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
1644
+ const detail = friendly || stderr || (stdout ? stdout.slice(0, 200) : "");
1645
+ callbacks.onError(new Error(detail ? `${base}. ${detail}` : base));
1483
1646
  }
1484
1647
  });
1485
1648
  }
1486
1649
  buildCombinedPrompt(options) {
1487
1650
  const streamOpts = options;
1488
1651
  const hasHistory = streamOpts.messages && streamOpts.messages.length > 0;
1489
- let combined = "";
1490
- combined += "[[System]]\n" + options.system + "\n\n";
1652
+ let combined = options.system + "\n\n";
1491
1653
  if (hasHistory) {
1492
1654
  for (const msg of streamOpts.messages) {
1493
- combined += `[[${msg.role === "user" ? "User" : "Assistant"}]]
1494
- ${msg.content}
1655
+ const label = msg.role === "user" ? "User" : "Assistant";
1656
+ combined += `${label}: ${msg.content}
1495
1657
 
1496
1658
  `;
1497
1659
  }
1498
1660
  }
1499
- combined += "[[User]]\n" + options.prompt;
1661
+ combined += options.prompt;
1500
1662
  return combined;
1501
1663
  }
1502
1664
  runClaudePrint(combinedPrompt, model) {
1503
1665
  return new Promise((resolve2, reject) => {
1504
1666
  const args = ["-p"];
1505
1667
  if (model) args.push("--model", model);
1506
- const child = IS_WINDOWS2 ? spawn2([CLAUDE_CLI_BIN, ...args].join(" "), {
1507
- cwd: process.cwd(),
1508
- stdio: ["pipe", "pipe", "inherit"],
1509
- env: process.env,
1510
- shell: true
1511
- }) : spawn2(CLAUDE_CLI_BIN, args, {
1512
- cwd: process.cwd(),
1513
- stdio: ["pipe", "pipe", "inherit"],
1514
- env: process.env
1515
- });
1668
+ const child = spawnClaude(args);
1516
1669
  child.stdin.end(combinedPrompt);
1517
1670
  const chunks = [];
1671
+ const stderrChunks = [];
1518
1672
  child.stdout.on("data", (chunk) => chunks.push(chunk));
1673
+ child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
1519
1674
  child.on("error", (err) => {
1520
1675
  clearTimeout(timer);
1521
1676
  reject(err);
@@ -1526,8 +1681,11 @@ ${msg.content}
1526
1681
  if (code === 0) {
1527
1682
  resolve2(stdout);
1528
1683
  } else {
1529
- const msg = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
1530
- reject(new Error(stdout ? `${msg}. Output: ${stdout.slice(0, 200)}` : msg));
1684
+ const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
1685
+ const friendly = parseSeatBasedError(stderr, code);
1686
+ const base = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
1687
+ const detail = friendly || stderr || (stdout ? stdout.slice(0, 200) : "");
1688
+ reject(new Error(detail ? `${base}. ${detail}` : base));
1531
1689
  }
1532
1690
  });
1533
1691
  const timer = setTimeout(() => {
@@ -1701,6 +1859,7 @@ async function handleModelNotAvailable(failedModel, provider, config) {
1701
1859
  }
1702
1860
 
1703
1861
  // src/llm/index.ts
1862
+ init_types();
1704
1863
  init_config();
1705
1864
  var cachedProvider = null;
1706
1865
  var cachedConfig = null;
@@ -1776,11 +1935,11 @@ async function llmCall(options) {
1776
1935
  throw error;
1777
1936
  }
1778
1937
  if (isOverloaded(error) && attempt < MAX_RETRIES) {
1779
- await new Promise((r) => setTimeout(r, 2e3));
1938
+ await new Promise((r) => setTimeout(r, 1e3 * Math.pow(2, attempt - 1)));
1780
1939
  continue;
1781
1940
  }
1782
1941
  if (isTransientError(error) && attempt < MAX_RETRIES) {
1783
- await new Promise((r) => setTimeout(r, 2e3));
1942
+ await new Promise((r) => setTimeout(r, 1e3 * Math.pow(2, attempt - 1)));
1784
1943
  continue;
1785
1944
  }
1786
1945
  throw error;
@@ -1796,7 +1955,8 @@ async function validateModel(options) {
1796
1955
  const provider = getProvider();
1797
1956
  const config = cachedConfig;
1798
1957
  if (!config) return;
1799
- if (config.provider === "cursor" || config.provider === "claude-cli") return;
1958
+ const { isSeatBased: isSeatBased2 } = await Promise.resolve().then(() => (init_types(), types_exports));
1959
+ if (isSeatBased2(config.provider)) return;
1800
1960
  const modelsToCheck = [config.model];
1801
1961
  if (options?.fast) {
1802
1962
  const { getFastModel: getFastModel2 } = await Promise.resolve().then(() => (init_config(), config_exports));
@@ -4196,8 +4356,11 @@ async function runInteractiveProviderSetup(options) {
4196
4356
  console.log(chalk5.dim(" Then run ") + chalk5.hex("#83D1EB")("agent login") + chalk5.dim(" to authenticate.\n"));
4197
4357
  const proceed = await confirm({ message: "Continue anyway?" });
4198
4358
  if (!proceed) throw new Error("__exit__");
4199
- } else {
4200
- console.log(chalk5.dim(" Run `agent login` if you haven't, or set CURSOR_API_KEY."));
4359
+ } else if (!isCursorLoggedIn()) {
4360
+ console.log(chalk5.yellow("\n Cursor Agent CLI found but not logged in."));
4361
+ console.log(chalk5.dim(" Run ") + chalk5.hex("#83D1EB")("agent login") + chalk5.dim(" to authenticate.\n"));
4362
+ const proceed = await confirm({ message: "Continue anyway?" });
4363
+ if (!proceed) throw new Error("__exit__");
4201
4364
  }
4202
4365
  break;
4203
4366
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rely-ai/caliber",
3
- "version": "1.23.0-dev.1773793983",
3
+ "version": "1.23.0",
4
4
  "description": "Analyze your codebase and generate optimized AI agent configs (CLAUDE.md, .cursorrules, skills) — no API key needed",
5
5
  "type": "module",
6
6
  "bin": {