@rely-ai/caliber 1.44.1 → 1.45.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 +406 -69
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -84,6 +84,12 @@ function resolveFromEnv() {
84
84
  model: process.env.CALIBER_MODEL || DEFAULT_MODELS["claude-cli"]
85
85
  };
86
86
  }
87
+ if (process.env.CALIBER_USE_OPENCODE === "1" || process.env.CALIBER_USE_OPENCODE === "true") {
88
+ return {
89
+ provider: "opencode",
90
+ model: process.env.CALIBER_MODEL || DEFAULT_MODELS.opencode
91
+ };
92
+ }
87
93
  return null;
88
94
  }
89
95
  function readConfigFile() {
@@ -91,7 +97,7 @@ function readConfigFile() {
91
97
  if (!fs4.existsSync(CONFIG_FILE)) return null;
92
98
  const raw = fs4.readFileSync(CONFIG_FILE, "utf-8");
93
99
  const parsed = JSON.parse(raw);
94
- if (!parsed.provider || !["anthropic", "vertex", "openai", "minimax", "cursor", "claude-cli"].includes(
100
+ if (!parsed.provider || !["anthropic", "vertex", "openai", "minimax", "cursor", "claude-cli", "opencode"].includes(
95
101
  parsed.provider
96
102
  )) {
97
103
  return null;
@@ -118,6 +124,9 @@ function getDisplayModel(config) {
118
124
  if (config.model === "default" && config.provider === "claude-cli") {
119
125
  return process.env.ANTHROPIC_MODEL || "default (inherited from Claude Code)";
120
126
  }
127
+ if (config.model === "default" && config.provider === "opencode") {
128
+ return "default (inherited from OpenCode)";
129
+ }
121
130
  return config.model;
122
131
  }
123
132
  function getFastModel() {
@@ -143,7 +152,8 @@ var init_config = __esm({
143
152
  openai: "gpt-5.4-mini",
144
153
  minimax: "MiniMax-M2.7",
145
154
  cursor: "sonnet-4.6",
146
- "claude-cli": "default"
155
+ "claude-cli": "default",
156
+ opencode: "default"
147
157
  };
148
158
  MODEL_CONTEXT_WINDOWS = {
149
159
  "claude-sonnet-4-6": 2e5,
@@ -180,7 +190,7 @@ __export(resolve_caliber_exports, {
180
190
  resolveCaliber: () => resolveCaliber
181
191
  });
182
192
  import fs6 from "fs";
183
- import { execSync as execSync4 } from "child_process";
193
+ import { execSync as execSync5 } from "child_process";
184
194
  function resolveCaliber() {
185
195
  if (_resolved) return _resolved;
186
196
  const whichCmd = process.platform === "win32" ? "where caliber" : "which caliber";
@@ -188,7 +198,7 @@ function resolveCaliber() {
188
198
  const isNpx = process.argv[1]?.includes("_npx") || process.env.npm_execpath?.includes("npx");
189
199
  if (isNpx) {
190
200
  try {
191
- const out = execSync4(whichCmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
201
+ const out = execSync5(whichCmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
192
202
  const caliberPath = out.split("\n")[0].trim();
193
203
  if (caliberPath) {
194
204
  _resolved = caliberPath;
@@ -197,7 +207,7 @@ function resolveCaliber() {
197
207
  } catch {
198
208
  }
199
209
  try {
200
- const out = execSync4(whichNpxCmd, {
210
+ const out = execSync5(whichNpxCmd, {
201
211
  encoding: "utf-8",
202
212
  stdio: ["pipe", "pipe", "pipe"]
203
213
  }).trim();
@@ -212,7 +222,7 @@ function resolveCaliber() {
212
222
  return _resolved;
213
223
  }
214
224
  try {
215
- const out = execSync4(whichCmd, {
225
+ const out = execSync5(whichCmd, {
216
226
  encoding: "utf-8",
217
227
  stdio: ["pipe", "pipe", "pipe"]
218
228
  }).trim();
@@ -267,7 +277,11 @@ var SEAT_BASED_PROVIDERS;
267
277
  var init_types = __esm({
268
278
  "src/llm/types.ts"() {
269
279
  "use strict";
270
- SEAT_BASED_PROVIDERS = /* @__PURE__ */ new Set(["cursor", "claude-cli"]);
280
+ SEAT_BASED_PROVIDERS = /* @__PURE__ */ new Set([
281
+ "cursor",
282
+ "claude-cli",
283
+ "opencode"
284
+ ]);
271
285
  }
272
286
  });
273
287
 
@@ -897,7 +911,7 @@ var init_builtin_skills = __esm({
897
911
  });
898
912
 
899
913
  // src/utils/editor.ts
900
- import { execSync as execSync14, spawn as spawn3 } from "child_process";
914
+ import { execSync as execSync16, spawn as spawn4 } from "child_process";
901
915
  import fs29 from "fs";
902
916
  import path25 from "path";
903
917
  import os6 from "os";
@@ -910,7 +924,7 @@ function getEmptyFilePath(proposedPath) {
910
924
  function commandExists(cmd) {
911
925
  try {
912
926
  const check = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
913
- execSync14(check, { stdio: "ignore" });
927
+ execSync16(check, { stdio: "ignore" });
914
928
  return true;
915
929
  } catch {
916
930
  return false;
@@ -928,22 +942,22 @@ function openDiffsInEditor(editor, files) {
928
942
  for (const file of files) {
929
943
  try {
930
944
  const leftPath = file.originalPath ?? getEmptyFilePath(file.proposedPath);
931
- if (IS_WINDOWS4) {
945
+ if (IS_WINDOWS5) {
932
946
  const quote = (s) => `"${s}"`;
933
- spawn3([cmd, "--diff", quote(leftPath), quote(file.proposedPath)].join(" "), { shell: true, stdio: "ignore", detached: true }).unref();
947
+ spawn4([cmd, "--diff", quote(leftPath), quote(file.proposedPath)].join(" "), { shell: true, stdio: "ignore", detached: true }).unref();
934
948
  } else {
935
- spawn3(cmd, ["--diff", leftPath, file.proposedPath], { stdio: "ignore", detached: true }).unref();
949
+ spawn4(cmd, ["--diff", leftPath, file.proposedPath], { stdio: "ignore", detached: true }).unref();
936
950
  }
937
951
  } catch {
938
952
  continue;
939
953
  }
940
954
  }
941
955
  }
942
- var IS_WINDOWS4, DIFF_TEMP_DIR;
956
+ var IS_WINDOWS5, DIFF_TEMP_DIR;
943
957
  var init_editor = __esm({
944
958
  "src/utils/editor.ts"() {
945
959
  "use strict";
946
- IS_WINDOWS4 = process.platform === "win32";
960
+ IS_WINDOWS5 = process.platform === "win32";
947
961
  DIFF_TEMP_DIR = path25.join(os6.tmpdir(), "caliber-diff");
948
962
  }
949
963
  });
@@ -1274,6 +1288,7 @@ function isGitRepo() {
1274
1288
  // src/fingerprint/file-tree.ts
1275
1289
  import fs from "fs";
1276
1290
  import path from "path";
1291
+ import { execSync as execSync2 } from "child_process";
1277
1292
  var IGNORE_DIRS = /* @__PURE__ */ new Set([
1278
1293
  "node_modules",
1279
1294
  ".git",
@@ -1290,8 +1305,59 @@ var IGNORE_DIRS = /* @__PURE__ */ new Set([
1290
1305
  "target"
1291
1306
  ]);
1292
1307
  function getFileTree(dir, maxDepth = 3) {
1308
+ const gitFiles = getGitTrackedFiles(dir);
1309
+ const entries = gitFiles ? buildTreeFromGitFiles(dir, gitFiles, maxDepth) : scanEntries(dir, maxDepth);
1310
+ return sortAndFormat(entries);
1311
+ }
1312
+ function getGitTrackedFiles(dir) {
1313
+ try {
1314
+ const output = execSync2("git ls-files --cached --others --exclude-standard", {
1315
+ cwd: dir,
1316
+ encoding: "utf-8",
1317
+ maxBuffer: 10 * 1024 * 1024,
1318
+ stdio: ["pipe", "pipe", "pipe"]
1319
+ });
1320
+ return output.trim().split("\n").filter(Boolean);
1321
+ } catch {
1322
+ return null;
1323
+ }
1324
+ }
1325
+ function buildTreeFromGitFiles(dir, files, maxDepth) {
1326
+ const result = [];
1327
+ const seenDirs = /* @__PURE__ */ new Set();
1328
+ for (const relFile of files) {
1329
+ const parts = relFile.split("/");
1330
+ if (parts.length - 1 > maxDepth) continue;
1331
+ const absPath = path.join(dir, relFile);
1332
+ let mtime = 0;
1333
+ try {
1334
+ mtime = fs.statSync(absPath).mtimeMs;
1335
+ } catch {
1336
+ continue;
1337
+ }
1338
+ result.push({ relPath: relFile, isDir: false, mtime });
1339
+ for (let i = 1; i < parts.length; i++) {
1340
+ const dirRel = parts.slice(0, i).join("/") + "/";
1341
+ if (seenDirs.has(dirRel)) continue;
1342
+ seenDirs.add(dirRel);
1343
+ const depth = i;
1344
+ if (depth > maxDepth) break;
1345
+ let dirMtime = 0;
1346
+ try {
1347
+ dirMtime = fs.statSync(path.join(dir, dirRel)).mtimeMs;
1348
+ } catch {
1349
+ }
1350
+ result.push({ relPath: dirRel, isDir: true, mtime: dirMtime });
1351
+ }
1352
+ }
1353
+ return result;
1354
+ }
1355
+ function scanEntries(dir, maxDepth) {
1293
1356
  const entries = [];
1294
1357
  scan(dir, "", 0, maxDepth, entries);
1358
+ return entries;
1359
+ }
1360
+ function sortAndFormat(entries) {
1295
1361
  const dirs = [];
1296
1362
  const files = [];
1297
1363
  for (const e of entries) {
@@ -1354,7 +1420,7 @@ import path3 from "path";
1354
1420
  // src/constants.ts
1355
1421
  import path2 from "path";
1356
1422
  import os from "os";
1357
- import { execSync as execSync2 } from "child_process";
1423
+ import { execSync as execSync3 } from "child_process";
1358
1424
  var AUTH_DIR = path2.join(os.homedir(), ".caliber");
1359
1425
  var CALIBER_DIR = ".caliber";
1360
1426
  var MANIFEST_FILE = path2.join(CALIBER_DIR, "manifest.json");
@@ -1363,7 +1429,7 @@ var _learningDirCache = null;
1363
1429
  function getLearningDir() {
1364
1430
  if (_learningDirCache) return _learningDirCache;
1365
1431
  try {
1366
- const gitCommonDir = execSync2("git rev-parse --git-common-dir", {
1432
+ const gitCommonDir = execSync3("git rev-parse --git-common-dir", {
1367
1433
  encoding: "utf-8",
1368
1434
  stdio: ["pipe", "pipe", "pipe"]
1369
1435
  }).trim();
@@ -1543,7 +1609,7 @@ function readExistingConfigs(dir) {
1543
1609
  // src/fingerprint/code-analysis.ts
1544
1610
  import fs3 from "fs";
1545
1611
  import path5 from "path";
1546
- import { execSync as execSync3 } from "child_process";
1612
+ import { execSync as execSync4 } from "child_process";
1547
1613
 
1548
1614
  // src/lib/sanitize.ts
1549
1615
  import path4 from "path";
@@ -1962,7 +2028,7 @@ function buildImportCounts(files) {
1962
2028
  function getGitFrequency(dir) {
1963
2029
  const freq = /* @__PURE__ */ new Map();
1964
2030
  try {
1965
- const output = execSync3(
2031
+ const output = execSync4(
1966
2032
  'git log --since="6 months ago" --format="" --name-only --diff-filter=ACMR 2>/dev/null | head -10000',
1967
2033
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 5e3 }
1968
2034
  );
@@ -2371,7 +2437,7 @@ var OpenAICompatProvider = class {
2371
2437
  async call(options) {
2372
2438
  const response = await this.client.chat.completions.create({
2373
2439
  model: options.model || this.defaultModel,
2374
- max_tokens: options.maxTokens || 4096,
2440
+ max_completion_tokens: options.maxTokens || 4096,
2375
2441
  ...this.temperature !== void 0 && { temperature: this.temperature },
2376
2442
  messages: [
2377
2443
  { role: "system", content: options.system },
@@ -2406,7 +2472,7 @@ var OpenAICompatProvider = class {
2406
2472
  messages.push({ role: "user", content: options.prompt });
2407
2473
  const stream = await this.client.chat.completions.create({
2408
2474
  model: options.model || this.defaultModel,
2409
- max_tokens: options.maxTokens || 10240,
2475
+ max_completion_tokens: options.maxTokens || 10240,
2410
2476
  ...this.temperature !== void 0 && { temperature: this.temperature },
2411
2477
  messages,
2412
2478
  stream: true
@@ -2457,7 +2523,7 @@ var MiniMaxProvider = class extends OpenAICompatProvider {
2457
2523
  };
2458
2524
 
2459
2525
  // src/llm/cursor-acp.ts
2460
- import { spawn, execSync as execSync5, execFileSync } from "child_process";
2526
+ import { spawn, execSync as execSync6, execFileSync } from "child_process";
2461
2527
  import os3 from "os";
2462
2528
 
2463
2529
  // src/llm/seat-based-errors.ts
@@ -2535,7 +2601,7 @@ function resolveAgentBin() {
2535
2601
  if (_agentBin !== null) return _agentBin;
2536
2602
  try {
2537
2603
  const whichCmd = IS_WINDOWS ? "where agent" : "which agent";
2538
- const out = execSync5(whichCmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
2604
+ const out = execSync6(whichCmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
2539
2605
  const p = out.split("\n")[0].trim();
2540
2606
  if (p) {
2541
2607
  _agentBin = p;
@@ -2826,7 +2892,7 @@ var CursorAcpProvider = class {
2826
2892
  function isCursorAgentAvailable() {
2827
2893
  if (resolveAgentBin() !== "agent") return true;
2828
2894
  try {
2829
- execSync5(IS_WINDOWS ? "where agent" : "which agent", { stdio: "ignore" });
2895
+ execSync6(IS_WINDOWS ? "where agent" : "which agent", { stdio: "ignore" });
2830
2896
  return true;
2831
2897
  } catch {
2832
2898
  return false;
@@ -2847,7 +2913,7 @@ function isCursorLoggedIn() {
2847
2913
 
2848
2914
  // src/llm/claude-cli.ts
2849
2915
  import fs7 from "fs";
2850
- import { spawn as spawn2, execSync as execSync6, execFileSync as execFileSync2 } from "child_process";
2916
+ import { spawn as spawn2, execSync as execSync7, execFileSync as execFileSync2 } from "child_process";
2851
2917
  var DEFAULT_TIMEOUT_MS2 = 10 * 60 * 1e3;
2852
2918
  var IS_WINDOWS2 = process.platform === "win32";
2853
2919
  function candidateClaudePaths() {
@@ -2867,7 +2933,7 @@ function resolveClaudeBin() {
2867
2933
  if (_claudeBin !== null) return _claudeBin;
2868
2934
  try {
2869
2935
  const whichCmd = IS_WINDOWS2 ? "where claude" : "which claude";
2870
- const out = execSync6(whichCmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
2936
+ const out = execSync7(whichCmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
2871
2937
  const p = out.split("\n")[0].trim();
2872
2938
  if (p) {
2873
2939
  _claudeBin = p;
@@ -2886,10 +2952,18 @@ function resolveClaudeBin() {
2886
2952
  _claudeBin = "claude";
2887
2953
  return _claudeBin;
2888
2954
  }
2955
+ function cleanClaudeEnv() {
2956
+ const env = { ...process.env };
2957
+ for (const key of Object.keys(env)) {
2958
+ if (key === "CLAUDE_CODE_SIMPLE" || key === "CLAUDECODE" || key.startsWith("CLAUDE_CODE_")) {
2959
+ delete env[key];
2960
+ }
2961
+ }
2962
+ return env;
2963
+ }
2889
2964
  function spawnClaude(args) {
2890
2965
  const bin = resolveClaudeBin();
2891
- const { CLAUDE_CODE_SIMPLE: _stripped, ...parentEnv } = process.env;
2892
- const env = parentEnv;
2966
+ const env = cleanClaudeEnv();
2893
2967
  return IS_WINDOWS2 ? spawn2([bin, ...args].join(" "), {
2894
2968
  cwd: process.cwd(),
2895
2969
  stdio: ["pipe", "pipe", "pipe"],
@@ -3043,7 +3117,7 @@ var ClaudeCliProvider = class {
3043
3117
  function isClaudeCliAvailable() {
3044
3118
  if (resolveClaudeBin() !== "claude") return true;
3045
3119
  try {
3046
- execSync6(IS_WINDOWS2 ? "where claude" : "which claude", { stdio: "ignore" });
3120
+ execSync7(IS_WINDOWS2 ? "where claude" : "which claude", { stdio: "ignore" });
3047
3121
  return true;
3048
3122
  } catch {
3049
3123
  return false;
@@ -3056,7 +3130,8 @@ function isClaudeCliLoggedIn() {
3056
3130
  const result = execFileSync2(resolveClaudeBin(), ["auth", "status"], {
3057
3131
  input: "",
3058
3132
  stdio: ["pipe", "pipe", "pipe"],
3059
- timeout: 5e3
3133
+ timeout: 5e3,
3134
+ env: cleanClaudeEnv()
3060
3135
  });
3061
3136
  const output = result.toString().trim();
3062
3137
  try {
@@ -3071,6 +3146,215 @@ function isClaudeCliLoggedIn() {
3071
3146
  return cachedLoggedIn;
3072
3147
  }
3073
3148
 
3149
+ // src/llm/opencode.ts
3150
+ import { spawn as spawn3, execSync as execSync8 } from "child_process";
3151
+ var OPENCODE_BIN = "opencode";
3152
+ var DEFAULT_TIMEOUT_MS3 = 10 * 60 * 1e3;
3153
+ var IS_WINDOWS3 = process.platform === "win32";
3154
+ var cachedLoggedIn2 = null;
3155
+ function isOpenCodeAvailable() {
3156
+ try {
3157
+ const cmd = IS_WINDOWS3 ? `where ${OPENCODE_BIN}` : `which ${OPENCODE_BIN}`;
3158
+ execSync8(cmd, { stdio: "ignore" });
3159
+ return true;
3160
+ } catch {
3161
+ return false;
3162
+ }
3163
+ }
3164
+ function isOpenCodeLoggedIn() {
3165
+ if (cachedLoggedIn2 !== null) return cachedLoggedIn2;
3166
+ try {
3167
+ const result = execSync8("opencode auth status", {
3168
+ stdio: ["ignore", "pipe", "pipe"],
3169
+ timeout: 5e3
3170
+ });
3171
+ const output = result.toString().trim();
3172
+ try {
3173
+ const status = JSON.parse(output);
3174
+ cachedLoggedIn2 = status.loggedIn === true;
3175
+ } catch {
3176
+ cachedLoggedIn2 = !output.toLowerCase().includes("not logged in");
3177
+ }
3178
+ } catch {
3179
+ cachedLoggedIn2 = false;
3180
+ }
3181
+ return cachedLoggedIn2;
3182
+ }
3183
+ function spawnOpenCode(args) {
3184
+ const env = { ...process.env, OPENCODE_DISABLE_AUTOCOMPACT: "TRUE" };
3185
+ if (IS_WINDOWS3) {
3186
+ return spawn3([OPENCODE_BIN, ...args].join(" "), {
3187
+ cwd: process.cwd(),
3188
+ stdio: ["pipe", "pipe", "pipe"],
3189
+ env,
3190
+ shell: true
3191
+ });
3192
+ } else {
3193
+ return spawn3(OPENCODE_BIN, args, {
3194
+ cwd: process.cwd(),
3195
+ stdio: ["pipe", "pipe", "pipe"],
3196
+ env
3197
+ });
3198
+ }
3199
+ }
3200
+ function runCommand(args, input, timeoutMs) {
3201
+ return new Promise((resolve3, reject) => {
3202
+ const child = spawnOpenCode(args);
3203
+ const stderrChunks = [];
3204
+ child.stdin.end(input);
3205
+ let stdoutData = Buffer.alloc(0);
3206
+ child.stdout.on("data", (chunk) => {
3207
+ stdoutData = Buffer.concat([stdoutData, chunk]);
3208
+ });
3209
+ child.stderr.on("data", (chunk) => {
3210
+ stderrChunks.push(chunk);
3211
+ });
3212
+ const timer = setTimeout(() => {
3213
+ child.kill("SIGTERM");
3214
+ reject(
3215
+ new Error(
3216
+ `OpenCode timed out after ${timeoutMs / 1e3}s. Set CALIBER_OPENCODE_TIMEOUT_MS to increase.`
3217
+ )
3218
+ );
3219
+ }, timeoutMs);
3220
+ child.on("error", (err) => {
3221
+ clearTimeout(timer);
3222
+ reject(err);
3223
+ });
3224
+ child.on("close", (code, signal) => {
3225
+ clearTimeout(timer);
3226
+ const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
3227
+ if (code === 0) {
3228
+ resolve3(stdoutData.toString("utf-8").trim());
3229
+ } else {
3230
+ const friendly = parseSeatBasedError(stderr, code);
3231
+ const base = signal ? `OpenCode killed (${signal})` : code != null ? `OpenCode exited with code ${code}` : "OpenCode exited";
3232
+ const detail = friendly || stderr;
3233
+ reject(new Error(detail ? `${base}. ${detail}` : base));
3234
+ }
3235
+ });
3236
+ });
3237
+ }
3238
+ function runCommandStream(args, input, callbacks, timeoutMs) {
3239
+ return new Promise((resolve3, reject) => {
3240
+ const child = spawnOpenCode(args);
3241
+ const stderrChunks = [];
3242
+ let settled = false;
3243
+ let lineBuffer = "";
3244
+ child.stdin.end(input);
3245
+ child.stdout.on("data", (chunk) => {
3246
+ const text = chunk.toString("utf-8");
3247
+ lineBuffer += text;
3248
+ const lines = lineBuffer.split("\n");
3249
+ lineBuffer = lines.pop() || "";
3250
+ for (const line of lines) {
3251
+ if (!line.trim()) continue;
3252
+ try {
3253
+ const event = JSON.parse(line);
3254
+ if (event.type === "text" && event.part?.text) {
3255
+ callbacks.onText(event.part.text);
3256
+ }
3257
+ } catch {
3258
+ }
3259
+ }
3260
+ });
3261
+ child.stderr.on("data", (chunk) => {
3262
+ stderrChunks.push(chunk);
3263
+ });
3264
+ const timer = setTimeout(() => {
3265
+ child.kill("SIGTERM");
3266
+ if (!settled) {
3267
+ settled = true;
3268
+ reject(
3269
+ new Error(
3270
+ `OpenCode timed out after ${timeoutMs / 1e3}s. Set CALIBER_OPENCODE_TIMEOUT_MS to increase.`
3271
+ )
3272
+ );
3273
+ }
3274
+ }, timeoutMs);
3275
+ child.on("error", (err) => {
3276
+ clearTimeout(timer);
3277
+ if (!settled) {
3278
+ settled = true;
3279
+ reject(err);
3280
+ }
3281
+ });
3282
+ child.on("close", (code, signal) => {
3283
+ clearTimeout(timer);
3284
+ if (settled) return;
3285
+ settled = true;
3286
+ if (code === 0) {
3287
+ if (lineBuffer.trim()) {
3288
+ try {
3289
+ const event = JSON.parse(lineBuffer);
3290
+ if (event.type === "text" && event.part?.text) {
3291
+ callbacks.onText(event.part.text);
3292
+ }
3293
+ } catch {
3294
+ }
3295
+ }
3296
+ callbacks.onEnd({ stopReason: "end_turn" });
3297
+ resolve3();
3298
+ } else {
3299
+ const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
3300
+ const friendly = parseSeatBasedError(stderr, code);
3301
+ const base = signal ? `OpenCode killed (${signal})` : code != null ? `OpenCode exited with code ${code}` : "OpenCode exited";
3302
+ const detail = friendly || stderr;
3303
+ reject(new Error(detail ? `${base}. ${detail}` : base));
3304
+ }
3305
+ });
3306
+ });
3307
+ }
3308
+ var OpenCodeProvider = class {
3309
+ defaultModel;
3310
+ timeoutMs;
3311
+ constructor(config) {
3312
+ this.defaultModel = config.model || "default";
3313
+ const envTimeout = process.env.CALIBER_OPENCODE_TIMEOUT_MS;
3314
+ this.timeoutMs = envTimeout ? parseInt(envTimeout, 10) : DEFAULT_TIMEOUT_MS3;
3315
+ if (!Number.isFinite(this.timeoutMs) || this.timeoutMs < 1e3) {
3316
+ this.timeoutMs = DEFAULT_TIMEOUT_MS3;
3317
+ }
3318
+ }
3319
+ async call(options) {
3320
+ const system = options.system || "";
3321
+ const prompt = options.prompt || "";
3322
+ const combined = system + "\n\n" + prompt;
3323
+ const model = options.model || this.defaultModel;
3324
+ const args = ["run", "--format", "json", "--model", model, "--", "-"];
3325
+ const result = await runCommand(args, combined, this.timeoutMs);
3326
+ trackUsage(model, {
3327
+ inputTokens: estimateTokens(combined),
3328
+ outputTokens: estimateTokens(result)
3329
+ });
3330
+ return result;
3331
+ }
3332
+ async stream(options, callbacks) {
3333
+ const system = options.system || "";
3334
+ const prompt = options.prompt || "";
3335
+ const combined = system + "\n\n" + prompt;
3336
+ const model = options.model || this.defaultModel;
3337
+ const args = ["run", "--format", "json", "--model", model, "--", "-"];
3338
+ const inputEstimate = estimateTokens(combined);
3339
+ let outputChars = 0;
3340
+ const wrappedCallbacks = {
3341
+ onText: (text) => {
3342
+ outputChars += text.length;
3343
+ callbacks.onText(text);
3344
+ },
3345
+ onEnd: (meta) => {
3346
+ trackUsage(model, {
3347
+ inputTokens: inputEstimate,
3348
+ outputTokens: Math.ceil(outputChars / 4)
3349
+ });
3350
+ callbacks.onEnd(meta);
3351
+ },
3352
+ onError: (err) => callbacks.onError(err)
3353
+ };
3354
+ return runCommandStream(args, combined, wrappedCallbacks, this.timeoutMs);
3355
+ }
3356
+ };
3357
+
3074
3358
  // src/llm/model-recovery.ts
3075
3359
  init_config();
3076
3360
  init_resolve_caliber();
@@ -3094,7 +3378,8 @@ var KNOWN_MODELS = {
3094
3378
  openai: ["gpt-5.4-mini", "gpt-4o", "gpt-4o-mini", "o3-mini"],
3095
3379
  minimax: ["MiniMax-M2.7", "MiniMax-M2.7-highspeed"],
3096
3380
  cursor: ["auto", "composer-1.5"],
3097
- "claude-cli": []
3381
+ "claude-cli": [],
3382
+ opencode: []
3098
3383
  };
3099
3384
  function isModelNotAvailableError(error) {
3100
3385
  const msg = error.message.toLowerCase();
@@ -3224,6 +3509,19 @@ function createProvider(config) {
3224
3509
  }
3225
3510
  return new ClaudeCliProvider(config);
3226
3511
  }
3512
+ case "opencode": {
3513
+ if (!isOpenCodeAvailable()) {
3514
+ throw new Error(
3515
+ "OpenCode provider requires the OpenCode CLI. Install it from https://opencode.ai then run `opencode auth login`. Alternatively set ANTHROPIC_API_KEY or choose another provider."
3516
+ );
3517
+ }
3518
+ if (!isOpenCodeLoggedIn()) {
3519
+ throw new Error(
3520
+ "OpenCode CLI is installed but not logged in. Run `opencode auth login` in your terminal to authenticate, then retry."
3521
+ );
3522
+ }
3523
+ return new OpenCodeProvider(config);
3524
+ }
3227
3525
  default:
3228
3526
  throw new Error(`Unknown provider: ${config.provider}`);
3229
3527
  }
@@ -3233,7 +3531,7 @@ function getProvider() {
3233
3531
  const config = loadConfig();
3234
3532
  if (!config) {
3235
3533
  throw new Error(
3236
- `No LLM provider configured. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, MINIMAX_API_KEY, or VERTEX_PROJECT_ID; or run \`${resolveCaliber()} config\` and choose Cursor or Claude Code; or set CALIBER_USE_CURSOR_SEAT=1 / CALIBER_USE_CLAUDE_CLI=1.`
3534
+ `No LLM provider configured. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, MINIMAX_API_KEY, or VERTEX_PROJECT_ID; or run \`${resolveCaliber()} config\` and choose Cursor, Claude Code, or OpenCode; or set CALIBER_USE_CURSOR_SEAT=1 / CALIBER_USE_CLAUDE_CLI=1 / CALIBER_USE_OPENCODE=1.`
3237
3535
  );
3238
3536
  }
3239
3537
  cachedConfig = config;
@@ -3884,7 +4182,7 @@ init_config();
3884
4182
  import fs8 from "fs";
3885
4183
  import path7 from "path";
3886
4184
  import crypto from "crypto";
3887
- import { execSync as execSync7 } from "child_process";
4185
+ import { execSync as execSync9 } from "child_process";
3888
4186
  var CACHE_VERSION = 1;
3889
4187
  var CACHE_DIR = ".caliber/cache";
3890
4188
  var CACHE_FILE = "fingerprint.json";
@@ -3893,7 +4191,7 @@ function getCachePath(dir) {
3893
4191
  }
3894
4192
  function getGitHead(dir) {
3895
4193
  try {
3896
- return execSync7("git rev-parse HEAD", {
4194
+ return execSync9("git rev-parse HEAD", {
3897
4195
  cwd: dir,
3898
4196
  encoding: "utf-8",
3899
4197
  stdio: ["pipe", "pipe", "pipe"],
@@ -3905,7 +4203,7 @@ function getGitHead(dir) {
3905
4203
  }
3906
4204
  function getDirtySignature(dir) {
3907
4205
  try {
3908
- const output = execSync7("git diff --name-only HEAD", {
4206
+ const output = execSync9("git diff --name-only HEAD", {
3909
4207
  cwd: dir,
3910
4208
  encoding: "utf-8",
3911
4209
  stdio: ["pipe", "pipe", "pipe"],
@@ -4248,7 +4546,7 @@ function getCursorConfigDir() {
4248
4546
  init_resolve_caliber();
4249
4547
  import fs11 from "fs";
4250
4548
  import path10 from "path";
4251
- import { execSync as execSync8 } from "child_process";
4549
+ import { execSync as execSync10 } from "child_process";
4252
4550
  var SETTINGS_PATH = path10.join(".claude", "settings.json");
4253
4551
  var REFRESH_TAIL = "refresh --quiet";
4254
4552
  var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
@@ -4453,7 +4751,7 @@ ${PRECOMMIT_END}`;
4453
4751
  }
4454
4752
  function getGitHooksDir() {
4455
4753
  try {
4456
- const gitDir = execSync8("git rev-parse --git-dir", {
4754
+ const gitDir = execSync10("git rev-parse --git-dir", {
4457
4755
  encoding: "utf-8",
4458
4756
  stdio: ["pipe", "pipe", "pipe"]
4459
4757
  }).trim();
@@ -5217,15 +5515,20 @@ async function generateSetup(fingerprint, targetAgent, prompt, callbacks, failin
5217
5515
  ({ platform, topic }) => generateSkill(skillContext, topic, fastModel).then((skill) => ({ platform, skill }))
5218
5516
  )
5219
5517
  );
5220
- const { failed: failedCount } = mergeSkillResults(skillResults, setup);
5518
+ const { succeeded, failed: failedCount, failedNames } = mergeSkillResults(skillResults, setup);
5221
5519
  if (failedCount > 0 && callbacks) {
5222
- callbacks.onStatus(`${failedCount} skill${failedCount === 1 ? "" : "s"} failed to generate`);
5520
+ const msg = succeeded === 0 ? `${failedCount} skill${failedCount === 1 ? "" : "s"} failed to generate \u2014 config saved without skills` : `Warning: ${failedCount} of ${failedCount + succeeded} skill${failedCount === 1 ? "" : "s"} failed to generate`;
5521
+ callbacks.onStatus(msg);
5522
+ for (const name of failedNames) {
5523
+ callbacks.onStatus(` \u2192 ${name}`);
5524
+ }
5223
5525
  }
5224
5526
  return coreResult;
5225
5527
  }
5226
5528
  function mergeSkillResults(results, setup) {
5227
5529
  let succeeded = 0;
5228
5530
  let failed = 0;
5531
+ const failedNames = [];
5229
5532
  for (const result of results) {
5230
5533
  if (result.status === "fulfilled") {
5231
5534
  const { platform, skill } = result.value;
@@ -5241,9 +5544,11 @@ function mergeSkillResults(results, setup) {
5241
5544
  succeeded++;
5242
5545
  } else {
5243
5546
  failed++;
5547
+ const reason = result.reason instanceof Error ? result.reason.message : String(result.reason);
5548
+ failedNames.push(reason);
5244
5549
  }
5245
5550
  }
5246
- return { succeeded, failed };
5551
+ return { succeeded, failed, failedNames };
5247
5552
  }
5248
5553
  var MAX_SKILL_TOPICS = 5;
5249
5554
  function collectSkillTopics(setup, targetAgent, fingerprint) {
@@ -5568,8 +5873,14 @@ async function generateSkillsForSetup(setup, fingerprint, targetAgent, onStatus)
5568
5873
  ({ platform, topic }) => generateSkill(skillContext, topic, fastModel).then((skill) => ({ platform, skill }))
5569
5874
  )
5570
5875
  );
5571
- const { succeeded, failed } = mergeSkillResults(skillResults, setup);
5572
- if (failed > 0) onStatus?.(`${succeeded} generated, ${failed} failed`);
5876
+ const { succeeded, failed, failedNames } = mergeSkillResults(skillResults, setup);
5877
+ if (failed > 0) {
5878
+ const msg = failed === skillTopics.length ? `All ${failed} skills failed to generate` : `${failed}/${skillTopics.length} skills failed to generate`;
5879
+ onStatus?.(msg);
5880
+ for (const name of failedNames) {
5881
+ onStatus?.(` \u2192 ${name}`);
5882
+ }
5883
+ }
5573
5884
  return succeeded;
5574
5885
  }
5575
5886
  var LIMITS = {
@@ -6560,7 +6871,7 @@ init_resolve_caliber();
6560
6871
  // src/lib/state.ts
6561
6872
  import fs25 from "fs";
6562
6873
  import path21 from "path";
6563
- import { execSync as execSync9 } from "child_process";
6874
+ import { execSync as execSync11 } from "child_process";
6564
6875
  var STATE_FILE = path21.join(CALIBER_DIR, ".caliber-state.json");
6565
6876
  function normalizeTargetAgent(value) {
6566
6877
  if (Array.isArray(value)) return value;
@@ -6588,7 +6899,7 @@ function writeState(state) {
6588
6899
  }
6589
6900
  function getCurrentHeadSha() {
6590
6901
  try {
6591
- return execSync9("git rev-parse HEAD", {
6902
+ return execSync11("git rev-parse HEAD", {
6592
6903
  encoding: "utf-8",
6593
6904
  stdio: ["pipe", "pipe", "pipe"]
6594
6905
  }).trim();
@@ -6619,9 +6930,10 @@ init_config();
6619
6930
  import chalk3 from "chalk";
6620
6931
  import select2 from "@inquirer/select";
6621
6932
  import confirm from "@inquirer/confirm";
6622
- var IS_WINDOWS3 = process.platform === "win32";
6933
+ var IS_WINDOWS4 = process.platform === "win32";
6623
6934
  var PROVIDER_CHOICES = [
6624
6935
  { name: "Claude Code \u2014 use your existing subscription (no API key)", value: "claude-cli" },
6936
+ { name: "OpenCode \u2014 use your existing subscription (no API key)", value: "opencode" },
6625
6937
  { name: "Cursor \u2014 use your existing subscription (no API key)", value: "cursor" },
6626
6938
  { name: "Anthropic \u2014 API key from console.anthropic.com", value: "anthropic" },
6627
6939
  { name: "Google Vertex AI \u2014 Claude models via GCP", value: "vertex" },
@@ -6664,10 +6976,23 @@ async function runInteractiveProviderSetup(options) {
6664
6976
  }
6665
6977
  break;
6666
6978
  }
6979
+ case "opencode": {
6980
+ if (!isOpenCodeAvailable()) {
6981
+ console.log(chalk3.yellow("\n OpenCode CLI not found."));
6982
+ console.log(chalk3.dim(" Install it from: ") + chalk3.hex("#83D1EB")("https://opencode.ai"));
6983
+ console.log(
6984
+ chalk3.dim(" Then run ") + chalk3.hex("#83D1EB")("opencode auth login") + chalk3.dim(" to authenticate.\n")
6985
+ );
6986
+ const proceed = await confirm({ message: "Continue anyway?" });
6987
+ if (!proceed) throw new Error("__exit__");
6988
+ }
6989
+ config.model = await promptInput(`Model (default: ${DEFAULT_MODELS.opencode}):`) || DEFAULT_MODELS.opencode;
6990
+ break;
6991
+ }
6667
6992
  case "cursor": {
6668
6993
  if (!isCursorAgentAvailable()) {
6669
6994
  console.log(chalk3.yellow("\n Cursor Agent CLI not found."));
6670
- if (IS_WINDOWS3) {
6995
+ if (IS_WINDOWS4) {
6671
6996
  console.log(
6672
6997
  chalk3.dim(" Install it from: ") + chalk3.hex("#83D1EB")("https://www.cursor.com/downloads")
6673
6998
  );
@@ -7274,7 +7599,7 @@ function checkGrounding(dir) {
7274
7599
 
7275
7600
  // src/scoring/checks/accuracy.ts
7276
7601
  import { existsSync as existsSync4, statSync } from "fs";
7277
- import { execSync as execSync10 } from "child_process";
7602
+ import { execSync as execSync12 } from "child_process";
7278
7603
  import { join as join5 } from "path";
7279
7604
  init_resolve_caliber();
7280
7605
  function validateReferences(dir) {
@@ -7284,13 +7609,13 @@ function validateReferences(dir) {
7284
7609
  }
7285
7610
  function detectGitDrift(dir) {
7286
7611
  try {
7287
- execSync10("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] });
7612
+ execSync12("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] });
7288
7613
  } catch {
7289
7614
  return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: false };
7290
7615
  }
7291
7616
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules", ".cursor/rules"];
7292
7617
  try {
7293
- const headTimestamp = execSync10(
7618
+ const headTimestamp = execSync12(
7294
7619
  "git log -1 --format=%ct HEAD",
7295
7620
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7296
7621
  ).trim();
@@ -7311,7 +7636,7 @@ function detectGitDrift(dir) {
7311
7636
  let latestConfigCommitHash = null;
7312
7637
  for (const file of configFiles) {
7313
7638
  try {
7314
- const hash = execSync10(
7639
+ const hash = execSync12(
7315
7640
  `git log -1 --format=%H -- "${file}"`,
7316
7641
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7317
7642
  ).trim();
@@ -7320,7 +7645,7 @@ function detectGitDrift(dir) {
7320
7645
  latestConfigCommitHash = hash;
7321
7646
  } else {
7322
7647
  try {
7323
- execSync10(
7648
+ execSync12(
7324
7649
  `git merge-base --is-ancestor ${latestConfigCommitHash} ${hash}`,
7325
7650
  { cwd: dir, stdio: ["pipe", "pipe", "pipe"] }
7326
7651
  );
@@ -7335,12 +7660,12 @@ function detectGitDrift(dir) {
7335
7660
  return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: true };
7336
7661
  }
7337
7662
  try {
7338
- const countStr = execSync10(
7663
+ const countStr = execSync12(
7339
7664
  `git rev-list --count ${latestConfigCommitHash}..HEAD`,
7340
7665
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7341
7666
  ).trim();
7342
7667
  const commitsSince = parseInt(countStr, 10) || 0;
7343
- const lastDate = execSync10(
7668
+ const lastDate = execSync12(
7344
7669
  `git log -1 --format=%ci ${latestConfigCommitHash}`,
7345
7670
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7346
7671
  ).trim();
@@ -7413,12 +7738,12 @@ function checkAccuracy(dir) {
7413
7738
  // src/scoring/checks/freshness.ts
7414
7739
  init_resolve_caliber();
7415
7740
  import { existsSync as existsSync5, statSync as statSync2 } from "fs";
7416
- import { execSync as execSync11 } from "child_process";
7741
+ import { execSync as execSync13 } from "child_process";
7417
7742
  import { join as join6 } from "path";
7418
7743
  function getCommitsSinceConfigUpdate(dir) {
7419
7744
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules"];
7420
7745
  try {
7421
- const headTimestamp = execSync11(
7746
+ const headTimestamp = execSync13(
7422
7747
  "git log -1 --format=%ct HEAD",
7423
7748
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7424
7749
  ).trim();
@@ -7438,12 +7763,12 @@ function getCommitsSinceConfigUpdate(dir) {
7438
7763
  }
7439
7764
  for (const file of configFiles) {
7440
7765
  try {
7441
- const hash = execSync11(
7766
+ const hash = execSync13(
7442
7767
  `git log -1 --format=%H -- "${file}"`,
7443
7768
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7444
7769
  ).trim();
7445
7770
  if (hash) {
7446
- const countStr = execSync11(
7771
+ const countStr = execSync13(
7447
7772
  `git rev-list --count ${hash}..HEAD`,
7448
7773
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7449
7774
  ).trim();
@@ -7561,7 +7886,7 @@ function checkFreshness(dir) {
7561
7886
 
7562
7887
  // src/scoring/checks/bonus.ts
7563
7888
  import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
7564
- import { execSync as execSync12 } from "child_process";
7889
+ import { execSync as execSync14 } from "child_process";
7565
7890
  import { join as join7 } from "path";
7566
7891
  init_resolve_caliber();
7567
7892
  init_pre_commit_block();
@@ -7580,7 +7905,7 @@ function configContentSuggestsPinnedModel(lower) {
7580
7905
  // src/scoring/checks/bonus.ts
7581
7906
  function hasPreCommitHook(dir) {
7582
7907
  try {
7583
- const gitDir = execSync12("git rev-parse --git-dir", {
7908
+ const gitDir = execSync14("git rev-parse --git-dir", {
7584
7909
  cwd: dir,
7585
7910
  encoding: "utf-8",
7586
7911
  stdio: ["pipe", "pipe", "pipe"]
@@ -8050,7 +8375,7 @@ import fs27 from "fs";
8050
8375
  import path23 from "path";
8051
8376
  import os5 from "os";
8052
8377
  import crypto4 from "crypto";
8053
- import { execSync as execSync13 } from "child_process";
8378
+ import { execSync as execSync15 } from "child_process";
8054
8379
  var CONFIG_DIR2 = path23.join(os5.homedir(), ".caliber");
8055
8380
  var CONFIG_FILE2 = path23.join(CONFIG_DIR2, "config.json");
8056
8381
  var runtimeDisabled = false;
@@ -8102,7 +8427,7 @@ var PERSONAL_DOMAINS = /* @__PURE__ */ new Set([
8102
8427
  ]);
8103
8428
  function getGitEmail() {
8104
8429
  try {
8105
- const email = execSync13("git config user.email", { encoding: "utf-8" }).trim();
8430
+ const email = execSync15("git config user.email", { encoding: "utf-8" }).trim();
8106
8431
  return email || void 0;
8107
8432
  } catch {
8108
8433
  return void 0;
@@ -8121,7 +8446,7 @@ function getGitEmailInfo() {
8121
8446
  }
8122
8447
  function getRepoHash() {
8123
8448
  try {
8124
- const remote = execSync13("git remote get-url origin || git rev-parse --show-toplevel", {
8449
+ const remote = execSync15("git remote get-url origin || git rev-parse --show-toplevel", {
8125
8450
  encoding: "utf-8",
8126
8451
  stdio: ["pipe", "pipe", "pipe"]
8127
8452
  }).trim();
@@ -10585,7 +10910,7 @@ function getScoreTrend(entries) {
10585
10910
  }
10586
10911
 
10587
10912
  // src/commands/init.ts
10588
- var IS_WINDOWS5 = process.platform === "win32";
10913
+ var IS_WINDOWS6 = process.platform === "win32";
10589
10914
  function log(verbose, ...args) {
10590
10915
  if (verbose) console.log(chalk14.dim(` [verbose] ${args.map(String).join(" ")}`));
10591
10916
  }
@@ -10629,6 +10954,14 @@ async function initCommand(options) {
10629
10954
  const report = options.debugReport ? new DebugReport() : null;
10630
10955
  console.log(title.bold(" Step 1/3 \u2014 Connect\n"));
10631
10956
  let config = loadConfig();
10957
+ if (!config && options.agent?.includes("opencode")) {
10958
+ if (isOpenCodeAvailable()) {
10959
+ console.log(chalk14.dim(" Detected: OpenCode (uses your existing subscription)\n"));
10960
+ const autoConfig = { provider: "opencode", model: DEFAULT_MODELS.opencode };
10961
+ writeConfigFile(autoConfig);
10962
+ config = autoConfig;
10963
+ }
10964
+ }
10632
10965
  if (!config && !options.autoApprove) {
10633
10966
  if (isClaudeCliAvailable() && isClaudeCliLoggedIn()) {
10634
10967
  console.log(chalk14.dim(" Detected: Claude Code CLI (uses your Pro/Max/Team subscription)\n"));
@@ -10658,6 +10991,10 @@ async function initCommand(options) {
10658
10991
  const autoConfig = { provider: "cursor", model: "sonnet-4.6" };
10659
10992
  writeConfigFile(autoConfig);
10660
10993
  config = autoConfig;
10994
+ } else if (isOpenCodeAvailable()) {
10995
+ const autoConfig = { provider: "opencode", model: DEFAULT_MODELS.opencode };
10996
+ writeConfigFile(autoConfig);
10997
+ config = autoConfig;
10661
10998
  }
10662
10999
  }
10663
11000
  if (!config) {
@@ -10721,7 +11058,7 @@ async function initCommand(options) {
10721
11058
  console.log(` ${chalk14.green("\u2713")} Onboarding hook \u2014 nudges new team members to set up`);
10722
11059
  installSessionStartHook();
10723
11060
  console.log(` ${chalk14.green("\u2713")} Freshness hook \u2014 warns when configs are stale`);
10724
- if (IS_WINDOWS5) {
11061
+ if (IS_WINDOWS6) {
10725
11062
  console.log(
10726
11063
  chalk14.yellow(
10727
11064
  "\n Note: hooks use shell syntax and require Git Bash (included with Git for Windows)."
@@ -11736,7 +12073,7 @@ import ora6 from "ora";
11736
12073
  import pLimit from "p-limit";
11737
12074
 
11738
12075
  // src/lib/git-diff.ts
11739
- import { execSync as execSync15 } from "child_process";
12076
+ import { execSync as execSync17 } from "child_process";
11740
12077
  var MAX_DIFF_BYTES = 1e5;
11741
12078
  var DOC_PATTERNS = [
11742
12079
  "CLAUDE.md",
@@ -11762,7 +12099,7 @@ function excludeArgs() {
11762
12099
  }
11763
12100
  function safeExec(cmd) {
11764
12101
  try {
11765
- return execSync15(cmd, {
12102
+ return execSync17(cmd, {
11766
12103
  encoding: "utf-8",
11767
12104
  stdio: ["pipe", "pipe", "pipe"],
11768
12105
  maxBuffer: 10 * 1024 * 1024
@@ -13632,13 +13969,13 @@ async function learnObserveCommand(options) {
13632
13969
  try {
13633
13970
  const { resolveCaliber: resolveCaliber2, isNpxResolution: isNpxResolution2 } = await Promise.resolve().then(() => (init_resolve_caliber(), resolve_caliber_exports));
13634
13971
  const bin = resolveCaliber2();
13635
- const { spawn: spawn4 } = await import("child_process");
13972
+ const { spawn: spawn5 } = await import("child_process");
13636
13973
  const logPath = path38.join(getLearningDir(), LEARNING_FINALIZE_LOG);
13637
13974
  if (!fs47.existsSync(getLearningDir())) fs47.mkdirSync(getLearningDir(), { recursive: true });
13638
13975
  const logFd = fs47.openSync(logPath, "a");
13639
13976
  const NPX_SUFFIX = " --yes @rely-ai/caliber";
13640
13977
  const [exe, binArgs] = isNpxResolution2() ? [bin.slice(0, -NPX_SUFFIX.length) || "npx", ["--yes", "@rely-ai/caliber"]] : [bin, []];
13641
- spawn4(exe, [...binArgs, "learn", "finalize", "--auto", "--incremental"], {
13978
+ spawn5(exe, [...binArgs, "learn", "finalize", "--auto", "--incremental"], {
13642
13979
  detached: true,
13643
13980
  stdio: ["ignore", logFd, logFd]
13644
13981
  }).unref();
@@ -14736,7 +15073,7 @@ learn.command("add <content>").description("Add a learning directly (used by age
14736
15073
  import fs53 from "fs";
14737
15074
  import path43 from "path";
14738
15075
  import { fileURLToPath as fileURLToPath2 } from "url";
14739
- import { execSync as execSync16, execFileSync as execFileSync5 } from "child_process";
15076
+ import { execSync as execSync18, execFileSync as execFileSync5 } from "child_process";
14740
15077
  import chalk29 from "chalk";
14741
15078
  import ora8 from "ora";
14742
15079
  import confirm4 from "@inquirer/confirm";
@@ -14764,7 +15101,7 @@ function isNewer(registry, current) {
14764
15101
  }
14765
15102
  function getInstalledVersion() {
14766
15103
  try {
14767
- const globalRoot = execSync16("npm root -g", {
15104
+ const globalRoot = execSync18("npm root -g", {
14768
15105
  encoding: "utf-8",
14769
15106
  stdio: ["pipe", "pipe", "pipe"]
14770
15107
  }).trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rely-ai/caliber",
3
- "version": "1.44.1",
3
+ "version": "1.45.0",
4
4
  "description": "AI context infrastructure for coding agents — keeps CLAUDE.md, Cursor rules, and skills in sync as your codebase evolves",
5
5
  "type": "module",
6
6
  "bin": {