@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.
- package/dist/bin.js +406 -69
- 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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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([
|
|
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
|
|
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
|
-
|
|
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 (
|
|
945
|
+
if (IS_WINDOWS5) {
|
|
932
946
|
const quote = (s) => `"${s}"`;
|
|
933
|
-
|
|
947
|
+
spawn4([cmd, "--diff", quote(leftPath), quote(file.proposedPath)].join(" "), { shell: true, stdio: "ignore", detached: true }).unref();
|
|
934
948
|
} else {
|
|
935
|
-
|
|
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
|
|
956
|
+
var IS_WINDOWS5, DIFF_TEMP_DIR;
|
|
943
957
|
var init_editor = __esm({
|
|
944
958
|
"src/utils/editor.ts"() {
|
|
945
959
|
"use strict";
|
|
946
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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)
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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