@rely-ai/caliber 1.44.2 → 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 +324 -49
- 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,
|
|
@@ -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
|
});
|
|
@@ -3132,6 +3146,215 @@ function isClaudeCliLoggedIn() {
|
|
|
3132
3146
|
return cachedLoggedIn;
|
|
3133
3147
|
}
|
|
3134
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
|
+
|
|
3135
3358
|
// src/llm/model-recovery.ts
|
|
3136
3359
|
init_config();
|
|
3137
3360
|
init_resolve_caliber();
|
|
@@ -3155,7 +3378,8 @@ var KNOWN_MODELS = {
|
|
|
3155
3378
|
openai: ["gpt-5.4-mini", "gpt-4o", "gpt-4o-mini", "o3-mini"],
|
|
3156
3379
|
minimax: ["MiniMax-M2.7", "MiniMax-M2.7-highspeed"],
|
|
3157
3380
|
cursor: ["auto", "composer-1.5"],
|
|
3158
|
-
"claude-cli": []
|
|
3381
|
+
"claude-cli": [],
|
|
3382
|
+
opencode: []
|
|
3159
3383
|
};
|
|
3160
3384
|
function isModelNotAvailableError(error) {
|
|
3161
3385
|
const msg = error.message.toLowerCase();
|
|
@@ -3285,6 +3509,19 @@ function createProvider(config) {
|
|
|
3285
3509
|
}
|
|
3286
3510
|
return new ClaudeCliProvider(config);
|
|
3287
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
|
+
}
|
|
3288
3525
|
default:
|
|
3289
3526
|
throw new Error(`Unknown provider: ${config.provider}`);
|
|
3290
3527
|
}
|
|
@@ -3294,7 +3531,7 @@ function getProvider() {
|
|
|
3294
3531
|
const config = loadConfig();
|
|
3295
3532
|
if (!config) {
|
|
3296
3533
|
throw new Error(
|
|
3297
|
-
`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.`
|
|
3298
3535
|
);
|
|
3299
3536
|
}
|
|
3300
3537
|
cachedConfig = config;
|
|
@@ -3945,7 +4182,7 @@ init_config();
|
|
|
3945
4182
|
import fs8 from "fs";
|
|
3946
4183
|
import path7 from "path";
|
|
3947
4184
|
import crypto from "crypto";
|
|
3948
|
-
import { execSync as
|
|
4185
|
+
import { execSync as execSync9 } from "child_process";
|
|
3949
4186
|
var CACHE_VERSION = 1;
|
|
3950
4187
|
var CACHE_DIR = ".caliber/cache";
|
|
3951
4188
|
var CACHE_FILE = "fingerprint.json";
|
|
@@ -3954,7 +4191,7 @@ function getCachePath(dir) {
|
|
|
3954
4191
|
}
|
|
3955
4192
|
function getGitHead(dir) {
|
|
3956
4193
|
try {
|
|
3957
|
-
return
|
|
4194
|
+
return execSync9("git rev-parse HEAD", {
|
|
3958
4195
|
cwd: dir,
|
|
3959
4196
|
encoding: "utf-8",
|
|
3960
4197
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -3966,7 +4203,7 @@ function getGitHead(dir) {
|
|
|
3966
4203
|
}
|
|
3967
4204
|
function getDirtySignature(dir) {
|
|
3968
4205
|
try {
|
|
3969
|
-
const output =
|
|
4206
|
+
const output = execSync9("git diff --name-only HEAD", {
|
|
3970
4207
|
cwd: dir,
|
|
3971
4208
|
encoding: "utf-8",
|
|
3972
4209
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -4309,7 +4546,7 @@ function getCursorConfigDir() {
|
|
|
4309
4546
|
init_resolve_caliber();
|
|
4310
4547
|
import fs11 from "fs";
|
|
4311
4548
|
import path10 from "path";
|
|
4312
|
-
import { execSync as
|
|
4549
|
+
import { execSync as execSync10 } from "child_process";
|
|
4313
4550
|
var SETTINGS_PATH = path10.join(".claude", "settings.json");
|
|
4314
4551
|
var REFRESH_TAIL = "refresh --quiet";
|
|
4315
4552
|
var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
|
|
@@ -4514,7 +4751,7 @@ ${PRECOMMIT_END}`;
|
|
|
4514
4751
|
}
|
|
4515
4752
|
function getGitHooksDir() {
|
|
4516
4753
|
try {
|
|
4517
|
-
const gitDir =
|
|
4754
|
+
const gitDir = execSync10("git rev-parse --git-dir", {
|
|
4518
4755
|
encoding: "utf-8",
|
|
4519
4756
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4520
4757
|
}).trim();
|
|
@@ -5278,16 +5515,20 @@ async function generateSetup(fingerprint, targetAgent, prompt, callbacks, failin
|
|
|
5278
5515
|
({ platform, topic }) => generateSkill(skillContext, topic, fastModel).then((skill) => ({ platform, skill }))
|
|
5279
5516
|
)
|
|
5280
5517
|
);
|
|
5281
|
-
const { succeeded, failed: failedCount } = mergeSkillResults(skillResults, setup);
|
|
5518
|
+
const { succeeded, failed: failedCount, failedNames } = mergeSkillResults(skillResults, setup);
|
|
5282
5519
|
if (failedCount > 0 && callbacks) {
|
|
5283
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`;
|
|
5284
5521
|
callbacks.onStatus(msg);
|
|
5522
|
+
for (const name of failedNames) {
|
|
5523
|
+
callbacks.onStatus(` \u2192 ${name}`);
|
|
5524
|
+
}
|
|
5285
5525
|
}
|
|
5286
5526
|
return coreResult;
|
|
5287
5527
|
}
|
|
5288
5528
|
function mergeSkillResults(results, setup) {
|
|
5289
5529
|
let succeeded = 0;
|
|
5290
5530
|
let failed = 0;
|
|
5531
|
+
const failedNames = [];
|
|
5291
5532
|
for (const result of results) {
|
|
5292
5533
|
if (result.status === "fulfilled") {
|
|
5293
5534
|
const { platform, skill } = result.value;
|
|
@@ -5303,9 +5544,11 @@ function mergeSkillResults(results, setup) {
|
|
|
5303
5544
|
succeeded++;
|
|
5304
5545
|
} else {
|
|
5305
5546
|
failed++;
|
|
5547
|
+
const reason = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
5548
|
+
failedNames.push(reason);
|
|
5306
5549
|
}
|
|
5307
5550
|
}
|
|
5308
|
-
return { succeeded, failed };
|
|
5551
|
+
return { succeeded, failed, failedNames };
|
|
5309
5552
|
}
|
|
5310
5553
|
var MAX_SKILL_TOPICS = 5;
|
|
5311
5554
|
function collectSkillTopics(setup, targetAgent, fingerprint) {
|
|
@@ -5630,8 +5873,14 @@ async function generateSkillsForSetup(setup, fingerprint, targetAgent, onStatus)
|
|
|
5630
5873
|
({ platform, topic }) => generateSkill(skillContext, topic, fastModel).then((skill) => ({ platform, skill }))
|
|
5631
5874
|
)
|
|
5632
5875
|
);
|
|
5633
|
-
const { succeeded, failed } = mergeSkillResults(skillResults, setup);
|
|
5634
|
-
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
|
+
}
|
|
5635
5884
|
return succeeded;
|
|
5636
5885
|
}
|
|
5637
5886
|
var LIMITS = {
|
|
@@ -6622,7 +6871,7 @@ init_resolve_caliber();
|
|
|
6622
6871
|
// src/lib/state.ts
|
|
6623
6872
|
import fs25 from "fs";
|
|
6624
6873
|
import path21 from "path";
|
|
6625
|
-
import { execSync as
|
|
6874
|
+
import { execSync as execSync11 } from "child_process";
|
|
6626
6875
|
var STATE_FILE = path21.join(CALIBER_DIR, ".caliber-state.json");
|
|
6627
6876
|
function normalizeTargetAgent(value) {
|
|
6628
6877
|
if (Array.isArray(value)) return value;
|
|
@@ -6650,7 +6899,7 @@ function writeState(state) {
|
|
|
6650
6899
|
}
|
|
6651
6900
|
function getCurrentHeadSha() {
|
|
6652
6901
|
try {
|
|
6653
|
-
return
|
|
6902
|
+
return execSync11("git rev-parse HEAD", {
|
|
6654
6903
|
encoding: "utf-8",
|
|
6655
6904
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6656
6905
|
}).trim();
|
|
@@ -6681,9 +6930,10 @@ init_config();
|
|
|
6681
6930
|
import chalk3 from "chalk";
|
|
6682
6931
|
import select2 from "@inquirer/select";
|
|
6683
6932
|
import confirm from "@inquirer/confirm";
|
|
6684
|
-
var
|
|
6933
|
+
var IS_WINDOWS4 = process.platform === "win32";
|
|
6685
6934
|
var PROVIDER_CHOICES = [
|
|
6686
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" },
|
|
6687
6937
|
{ name: "Cursor \u2014 use your existing subscription (no API key)", value: "cursor" },
|
|
6688
6938
|
{ name: "Anthropic \u2014 API key from console.anthropic.com", value: "anthropic" },
|
|
6689
6939
|
{ name: "Google Vertex AI \u2014 Claude models via GCP", value: "vertex" },
|
|
@@ -6726,10 +6976,23 @@ async function runInteractiveProviderSetup(options) {
|
|
|
6726
6976
|
}
|
|
6727
6977
|
break;
|
|
6728
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
|
+
}
|
|
6729
6992
|
case "cursor": {
|
|
6730
6993
|
if (!isCursorAgentAvailable()) {
|
|
6731
6994
|
console.log(chalk3.yellow("\n Cursor Agent CLI not found."));
|
|
6732
|
-
if (
|
|
6995
|
+
if (IS_WINDOWS4) {
|
|
6733
6996
|
console.log(
|
|
6734
6997
|
chalk3.dim(" Install it from: ") + chalk3.hex("#83D1EB")("https://www.cursor.com/downloads")
|
|
6735
6998
|
);
|
|
@@ -7336,7 +7599,7 @@ function checkGrounding(dir) {
|
|
|
7336
7599
|
|
|
7337
7600
|
// src/scoring/checks/accuracy.ts
|
|
7338
7601
|
import { existsSync as existsSync4, statSync } from "fs";
|
|
7339
|
-
import { execSync as
|
|
7602
|
+
import { execSync as execSync12 } from "child_process";
|
|
7340
7603
|
import { join as join5 } from "path";
|
|
7341
7604
|
init_resolve_caliber();
|
|
7342
7605
|
function validateReferences(dir) {
|
|
@@ -7346,13 +7609,13 @@ function validateReferences(dir) {
|
|
|
7346
7609
|
}
|
|
7347
7610
|
function detectGitDrift(dir) {
|
|
7348
7611
|
try {
|
|
7349
|
-
|
|
7612
|
+
execSync12("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] });
|
|
7350
7613
|
} catch {
|
|
7351
7614
|
return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: false };
|
|
7352
7615
|
}
|
|
7353
7616
|
const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules", ".cursor/rules"];
|
|
7354
7617
|
try {
|
|
7355
|
-
const headTimestamp =
|
|
7618
|
+
const headTimestamp = execSync12(
|
|
7356
7619
|
"git log -1 --format=%ct HEAD",
|
|
7357
7620
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
7358
7621
|
).trim();
|
|
@@ -7373,7 +7636,7 @@ function detectGitDrift(dir) {
|
|
|
7373
7636
|
let latestConfigCommitHash = null;
|
|
7374
7637
|
for (const file of configFiles) {
|
|
7375
7638
|
try {
|
|
7376
|
-
const hash =
|
|
7639
|
+
const hash = execSync12(
|
|
7377
7640
|
`git log -1 --format=%H -- "${file}"`,
|
|
7378
7641
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
7379
7642
|
).trim();
|
|
@@ -7382,7 +7645,7 @@ function detectGitDrift(dir) {
|
|
|
7382
7645
|
latestConfigCommitHash = hash;
|
|
7383
7646
|
} else {
|
|
7384
7647
|
try {
|
|
7385
|
-
|
|
7648
|
+
execSync12(
|
|
7386
7649
|
`git merge-base --is-ancestor ${latestConfigCommitHash} ${hash}`,
|
|
7387
7650
|
{ cwd: dir, stdio: ["pipe", "pipe", "pipe"] }
|
|
7388
7651
|
);
|
|
@@ -7397,12 +7660,12 @@ function detectGitDrift(dir) {
|
|
|
7397
7660
|
return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: true };
|
|
7398
7661
|
}
|
|
7399
7662
|
try {
|
|
7400
|
-
const countStr =
|
|
7663
|
+
const countStr = execSync12(
|
|
7401
7664
|
`git rev-list --count ${latestConfigCommitHash}..HEAD`,
|
|
7402
7665
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
7403
7666
|
).trim();
|
|
7404
7667
|
const commitsSince = parseInt(countStr, 10) || 0;
|
|
7405
|
-
const lastDate =
|
|
7668
|
+
const lastDate = execSync12(
|
|
7406
7669
|
`git log -1 --format=%ci ${latestConfigCommitHash}`,
|
|
7407
7670
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
7408
7671
|
).trim();
|
|
@@ -7475,12 +7738,12 @@ function checkAccuracy(dir) {
|
|
|
7475
7738
|
// src/scoring/checks/freshness.ts
|
|
7476
7739
|
init_resolve_caliber();
|
|
7477
7740
|
import { existsSync as existsSync5, statSync as statSync2 } from "fs";
|
|
7478
|
-
import { execSync as
|
|
7741
|
+
import { execSync as execSync13 } from "child_process";
|
|
7479
7742
|
import { join as join6 } from "path";
|
|
7480
7743
|
function getCommitsSinceConfigUpdate(dir) {
|
|
7481
7744
|
const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules"];
|
|
7482
7745
|
try {
|
|
7483
|
-
const headTimestamp =
|
|
7746
|
+
const headTimestamp = execSync13(
|
|
7484
7747
|
"git log -1 --format=%ct HEAD",
|
|
7485
7748
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
7486
7749
|
).trim();
|
|
@@ -7500,12 +7763,12 @@ function getCommitsSinceConfigUpdate(dir) {
|
|
|
7500
7763
|
}
|
|
7501
7764
|
for (const file of configFiles) {
|
|
7502
7765
|
try {
|
|
7503
|
-
const hash =
|
|
7766
|
+
const hash = execSync13(
|
|
7504
7767
|
`git log -1 --format=%H -- "${file}"`,
|
|
7505
7768
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
7506
7769
|
).trim();
|
|
7507
7770
|
if (hash) {
|
|
7508
|
-
const countStr =
|
|
7771
|
+
const countStr = execSync13(
|
|
7509
7772
|
`git rev-list --count ${hash}..HEAD`,
|
|
7510
7773
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
7511
7774
|
).trim();
|
|
@@ -7623,7 +7886,7 @@ function checkFreshness(dir) {
|
|
|
7623
7886
|
|
|
7624
7887
|
// src/scoring/checks/bonus.ts
|
|
7625
7888
|
import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
|
|
7626
|
-
import { execSync as
|
|
7889
|
+
import { execSync as execSync14 } from "child_process";
|
|
7627
7890
|
import { join as join7 } from "path";
|
|
7628
7891
|
init_resolve_caliber();
|
|
7629
7892
|
init_pre_commit_block();
|
|
@@ -7642,7 +7905,7 @@ function configContentSuggestsPinnedModel(lower) {
|
|
|
7642
7905
|
// src/scoring/checks/bonus.ts
|
|
7643
7906
|
function hasPreCommitHook(dir) {
|
|
7644
7907
|
try {
|
|
7645
|
-
const gitDir =
|
|
7908
|
+
const gitDir = execSync14("git rev-parse --git-dir", {
|
|
7646
7909
|
cwd: dir,
|
|
7647
7910
|
encoding: "utf-8",
|
|
7648
7911
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -8112,7 +8375,7 @@ import fs27 from "fs";
|
|
|
8112
8375
|
import path23 from "path";
|
|
8113
8376
|
import os5 from "os";
|
|
8114
8377
|
import crypto4 from "crypto";
|
|
8115
|
-
import { execSync as
|
|
8378
|
+
import { execSync as execSync15 } from "child_process";
|
|
8116
8379
|
var CONFIG_DIR2 = path23.join(os5.homedir(), ".caliber");
|
|
8117
8380
|
var CONFIG_FILE2 = path23.join(CONFIG_DIR2, "config.json");
|
|
8118
8381
|
var runtimeDisabled = false;
|
|
@@ -8164,7 +8427,7 @@ var PERSONAL_DOMAINS = /* @__PURE__ */ new Set([
|
|
|
8164
8427
|
]);
|
|
8165
8428
|
function getGitEmail() {
|
|
8166
8429
|
try {
|
|
8167
|
-
const email =
|
|
8430
|
+
const email = execSync15("git config user.email", { encoding: "utf-8" }).trim();
|
|
8168
8431
|
return email || void 0;
|
|
8169
8432
|
} catch {
|
|
8170
8433
|
return void 0;
|
|
@@ -8183,7 +8446,7 @@ function getGitEmailInfo() {
|
|
|
8183
8446
|
}
|
|
8184
8447
|
function getRepoHash() {
|
|
8185
8448
|
try {
|
|
8186
|
-
const remote =
|
|
8449
|
+
const remote = execSync15("git remote get-url origin || git rev-parse --show-toplevel", {
|
|
8187
8450
|
encoding: "utf-8",
|
|
8188
8451
|
stdio: ["pipe", "pipe", "pipe"]
|
|
8189
8452
|
}).trim();
|
|
@@ -10647,7 +10910,7 @@ function getScoreTrend(entries) {
|
|
|
10647
10910
|
}
|
|
10648
10911
|
|
|
10649
10912
|
// src/commands/init.ts
|
|
10650
|
-
var
|
|
10913
|
+
var IS_WINDOWS6 = process.platform === "win32";
|
|
10651
10914
|
function log(verbose, ...args) {
|
|
10652
10915
|
if (verbose) console.log(chalk14.dim(` [verbose] ${args.map(String).join(" ")}`));
|
|
10653
10916
|
}
|
|
@@ -10691,6 +10954,14 @@ async function initCommand(options) {
|
|
|
10691
10954
|
const report = options.debugReport ? new DebugReport() : null;
|
|
10692
10955
|
console.log(title.bold(" Step 1/3 \u2014 Connect\n"));
|
|
10693
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
|
+
}
|
|
10694
10965
|
if (!config && !options.autoApprove) {
|
|
10695
10966
|
if (isClaudeCliAvailable() && isClaudeCliLoggedIn()) {
|
|
10696
10967
|
console.log(chalk14.dim(" Detected: Claude Code CLI (uses your Pro/Max/Team subscription)\n"));
|
|
@@ -10720,6 +10991,10 @@ async function initCommand(options) {
|
|
|
10720
10991
|
const autoConfig = { provider: "cursor", model: "sonnet-4.6" };
|
|
10721
10992
|
writeConfigFile(autoConfig);
|
|
10722
10993
|
config = autoConfig;
|
|
10994
|
+
} else if (isOpenCodeAvailable()) {
|
|
10995
|
+
const autoConfig = { provider: "opencode", model: DEFAULT_MODELS.opencode };
|
|
10996
|
+
writeConfigFile(autoConfig);
|
|
10997
|
+
config = autoConfig;
|
|
10723
10998
|
}
|
|
10724
10999
|
}
|
|
10725
11000
|
if (!config) {
|
|
@@ -10783,7 +11058,7 @@ async function initCommand(options) {
|
|
|
10783
11058
|
console.log(` ${chalk14.green("\u2713")} Onboarding hook \u2014 nudges new team members to set up`);
|
|
10784
11059
|
installSessionStartHook();
|
|
10785
11060
|
console.log(` ${chalk14.green("\u2713")} Freshness hook \u2014 warns when configs are stale`);
|
|
10786
|
-
if (
|
|
11061
|
+
if (IS_WINDOWS6) {
|
|
10787
11062
|
console.log(
|
|
10788
11063
|
chalk14.yellow(
|
|
10789
11064
|
"\n Note: hooks use shell syntax and require Git Bash (included with Git for Windows)."
|
|
@@ -11798,7 +12073,7 @@ import ora6 from "ora";
|
|
|
11798
12073
|
import pLimit from "p-limit";
|
|
11799
12074
|
|
|
11800
12075
|
// src/lib/git-diff.ts
|
|
11801
|
-
import { execSync as
|
|
12076
|
+
import { execSync as execSync17 } from "child_process";
|
|
11802
12077
|
var MAX_DIFF_BYTES = 1e5;
|
|
11803
12078
|
var DOC_PATTERNS = [
|
|
11804
12079
|
"CLAUDE.md",
|
|
@@ -11824,7 +12099,7 @@ function excludeArgs() {
|
|
|
11824
12099
|
}
|
|
11825
12100
|
function safeExec(cmd) {
|
|
11826
12101
|
try {
|
|
11827
|
-
return
|
|
12102
|
+
return execSync17(cmd, {
|
|
11828
12103
|
encoding: "utf-8",
|
|
11829
12104
|
stdio: ["pipe", "pipe", "pipe"],
|
|
11830
12105
|
maxBuffer: 10 * 1024 * 1024
|
|
@@ -13694,13 +13969,13 @@ async function learnObserveCommand(options) {
|
|
|
13694
13969
|
try {
|
|
13695
13970
|
const { resolveCaliber: resolveCaliber2, isNpxResolution: isNpxResolution2 } = await Promise.resolve().then(() => (init_resolve_caliber(), resolve_caliber_exports));
|
|
13696
13971
|
const bin = resolveCaliber2();
|
|
13697
|
-
const { spawn:
|
|
13972
|
+
const { spawn: spawn5 } = await import("child_process");
|
|
13698
13973
|
const logPath = path38.join(getLearningDir(), LEARNING_FINALIZE_LOG);
|
|
13699
13974
|
if (!fs47.existsSync(getLearningDir())) fs47.mkdirSync(getLearningDir(), { recursive: true });
|
|
13700
13975
|
const logFd = fs47.openSync(logPath, "a");
|
|
13701
13976
|
const NPX_SUFFIX = " --yes @rely-ai/caliber";
|
|
13702
13977
|
const [exe, binArgs] = isNpxResolution2() ? [bin.slice(0, -NPX_SUFFIX.length) || "npx", ["--yes", "@rely-ai/caliber"]] : [bin, []];
|
|
13703
|
-
|
|
13978
|
+
spawn5(exe, [...binArgs, "learn", "finalize", "--auto", "--incremental"], {
|
|
13704
13979
|
detached: true,
|
|
13705
13980
|
stdio: ["ignore", logFd, logFd]
|
|
13706
13981
|
}).unref();
|
|
@@ -14798,7 +15073,7 @@ learn.command("add <content>").description("Add a learning directly (used by age
|
|
|
14798
15073
|
import fs53 from "fs";
|
|
14799
15074
|
import path43 from "path";
|
|
14800
15075
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
14801
|
-
import { execSync as
|
|
15076
|
+
import { execSync as execSync18, execFileSync as execFileSync5 } from "child_process";
|
|
14802
15077
|
import chalk29 from "chalk";
|
|
14803
15078
|
import ora8 from "ora";
|
|
14804
15079
|
import confirm4 from "@inquirer/confirm";
|
|
@@ -14826,7 +15101,7 @@ function isNewer(registry, current) {
|
|
|
14826
15101
|
}
|
|
14827
15102
|
function getInstalledVersion() {
|
|
14828
15103
|
try {
|
|
14829
|
-
const globalRoot =
|
|
15104
|
+
const globalRoot = execSync18("npm root -g", {
|
|
14830
15105
|
encoding: "utf-8",
|
|
14831
15106
|
stdio: ["pipe", "pipe", "pipe"]
|
|
14832
15107
|
}).trim();
|
package/package.json
CHANGED