@rely-ai/caliber 1.23.0-dev.1773792440 → 1.23.0-dev.1773794235
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 +788 -556
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -25,9 +25,11 @@ var config_exports = {};
|
|
|
25
25
|
__export(config_exports, {
|
|
26
26
|
DEFAULT_FAST_MODELS: () => DEFAULT_FAST_MODELS,
|
|
27
27
|
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
28
|
+
MODEL_CONTEXT_WINDOWS: () => MODEL_CONTEXT_WINDOWS,
|
|
28
29
|
getConfigFilePath: () => getConfigFilePath,
|
|
29
30
|
getDisplayModel: () => getDisplayModel,
|
|
30
31
|
getFastModel: () => getFastModel,
|
|
32
|
+
getMaxPromptTokens: () => getMaxPromptTokens,
|
|
31
33
|
loadConfig: () => loadConfig,
|
|
32
34
|
readConfigFile: () => readConfigFile,
|
|
33
35
|
resolveFromEnv: () => resolveFromEnv,
|
|
@@ -36,6 +38,13 @@ __export(config_exports, {
|
|
|
36
38
|
import fs4 from "fs";
|
|
37
39
|
import path4 from "path";
|
|
38
40
|
import os from "os";
|
|
41
|
+
function getMaxPromptTokens() {
|
|
42
|
+
const config = loadConfig();
|
|
43
|
+
const model = process.env.CALIBER_MODEL || config?.model;
|
|
44
|
+
const contextWindow = model ? MODEL_CONTEXT_WINDOWS[model] ?? DEFAULT_CONTEXT_WINDOW : DEFAULT_CONTEXT_WINDOW;
|
|
45
|
+
const budget = Math.floor(contextWindow * INPUT_BUDGET_FRACTION);
|
|
46
|
+
return Math.max(MIN_PROMPT_TOKENS, Math.min(budget, MAX_PROMPT_TOKENS_CAP));
|
|
47
|
+
}
|
|
39
48
|
function loadConfig() {
|
|
40
49
|
const envConfig = resolveFromEnv();
|
|
41
50
|
if (envConfig) return envConfig;
|
|
@@ -123,7 +132,7 @@ function getFastModel() {
|
|
|
123
132
|
if (provider) return DEFAULT_FAST_MODELS[provider];
|
|
124
133
|
return void 0;
|
|
125
134
|
}
|
|
126
|
-
var CONFIG_DIR, CONFIG_FILE, DEFAULT_MODELS, DEFAULT_FAST_MODELS;
|
|
135
|
+
var CONFIG_DIR, CONFIG_FILE, DEFAULT_MODELS, MODEL_CONTEXT_WINDOWS, DEFAULT_CONTEXT_WINDOW, INPUT_BUDGET_FRACTION, MAX_PROMPT_TOKENS_CAP, MIN_PROMPT_TOKENS, DEFAULT_FAST_MODELS;
|
|
127
136
|
var init_config = __esm({
|
|
128
137
|
"src/llm/config.ts"() {
|
|
129
138
|
"use strict";
|
|
@@ -136,6 +145,21 @@ var init_config = __esm({
|
|
|
136
145
|
cursor: "sonnet-4.6",
|
|
137
146
|
"claude-cli": "default"
|
|
138
147
|
};
|
|
148
|
+
MODEL_CONTEXT_WINDOWS = {
|
|
149
|
+
"claude-sonnet-4-6": 2e5,
|
|
150
|
+
"claude-opus-4-6": 2e5,
|
|
151
|
+
"claude-haiku-4-5-20251001": 2e5,
|
|
152
|
+
"claude-sonnet-4-5-20250514": 2e5,
|
|
153
|
+
"gpt-4.1": 1e6,
|
|
154
|
+
"gpt-4.1-mini": 1e6,
|
|
155
|
+
"gpt-4o": 128e3,
|
|
156
|
+
"gpt-4o-mini": 128e3,
|
|
157
|
+
"sonnet-4.6": 2e5
|
|
158
|
+
};
|
|
159
|
+
DEFAULT_CONTEXT_WINDOW = 2e5;
|
|
160
|
+
INPUT_BUDGET_FRACTION = 0.6;
|
|
161
|
+
MAX_PROMPT_TOKENS_CAP = 3e5;
|
|
162
|
+
MIN_PROMPT_TOKENS = 3e4;
|
|
139
163
|
DEFAULT_FAST_MODELS = {
|
|
140
164
|
anthropic: "claude-haiku-4-5-20251001",
|
|
141
165
|
vertex: "claude-haiku-4-5-20251001",
|
|
@@ -145,6 +169,22 @@ var init_config = __esm({
|
|
|
145
169
|
}
|
|
146
170
|
});
|
|
147
171
|
|
|
172
|
+
// src/llm/types.ts
|
|
173
|
+
var types_exports = {};
|
|
174
|
+
__export(types_exports, {
|
|
175
|
+
isSeatBased: () => isSeatBased
|
|
176
|
+
});
|
|
177
|
+
function isSeatBased(provider) {
|
|
178
|
+
return SEAT_BASED_PROVIDERS.has(provider);
|
|
179
|
+
}
|
|
180
|
+
var SEAT_BASED_PROVIDERS;
|
|
181
|
+
var init_types = __esm({
|
|
182
|
+
"src/llm/types.ts"() {
|
|
183
|
+
"use strict";
|
|
184
|
+
SEAT_BASED_PROVIDERS = /* @__PURE__ */ new Set(["cursor", "claude-cli"]);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
148
188
|
// src/constants.ts
|
|
149
189
|
var constants_exports = {};
|
|
150
190
|
__export(constants_exports, {
|
|
@@ -158,17 +198,17 @@ __export(constants_exports, {
|
|
|
158
198
|
LEARNING_STATE_FILE: () => LEARNING_STATE_FILE,
|
|
159
199
|
MANIFEST_FILE: () => MANIFEST_FILE
|
|
160
200
|
});
|
|
161
|
-
import
|
|
201
|
+
import path10 from "path";
|
|
162
202
|
import os3 from "os";
|
|
163
203
|
var AUTH_DIR, CALIBER_DIR, MANIFEST_FILE, BACKUPS_DIR, LEARNING_DIR, LEARNING_SESSION_FILE, LEARNING_STATE_FILE, LEARNING_MAX_EVENTS, LEARNING_ROI_FILE;
|
|
164
204
|
var init_constants = __esm({
|
|
165
205
|
"src/constants.ts"() {
|
|
166
206
|
"use strict";
|
|
167
|
-
AUTH_DIR =
|
|
207
|
+
AUTH_DIR = path10.join(os3.homedir(), ".caliber");
|
|
168
208
|
CALIBER_DIR = ".caliber";
|
|
169
|
-
MANIFEST_FILE =
|
|
170
|
-
BACKUPS_DIR =
|
|
171
|
-
LEARNING_DIR =
|
|
209
|
+
MANIFEST_FILE = path10.join(CALIBER_DIR, "manifest.json");
|
|
210
|
+
BACKUPS_DIR = path10.join(CALIBER_DIR, "backups");
|
|
211
|
+
LEARNING_DIR = path10.join(CALIBER_DIR, "learning");
|
|
172
212
|
LEARNING_SESSION_FILE = "current-session.jsonl";
|
|
173
213
|
LEARNING_STATE_FILE = "state.json";
|
|
174
214
|
LEARNING_MAX_EVENTS = 500;
|
|
@@ -183,13 +223,13 @@ __export(lock_exports, {
|
|
|
183
223
|
isCaliberRunning: () => isCaliberRunning,
|
|
184
224
|
releaseLock: () => releaseLock
|
|
185
225
|
});
|
|
186
|
-
import
|
|
187
|
-
import
|
|
226
|
+
import fs30 from "fs";
|
|
227
|
+
import path24 from "path";
|
|
188
228
|
import os6 from "os";
|
|
189
229
|
function isCaliberRunning() {
|
|
190
230
|
try {
|
|
191
|
-
if (!
|
|
192
|
-
const raw =
|
|
231
|
+
if (!fs30.existsSync(LOCK_FILE)) return false;
|
|
232
|
+
const raw = fs30.readFileSync(LOCK_FILE, "utf-8").trim();
|
|
193
233
|
const { pid, ts } = JSON.parse(raw);
|
|
194
234
|
if (Date.now() - ts > STALE_MS) return false;
|
|
195
235
|
try {
|
|
@@ -204,13 +244,13 @@ function isCaliberRunning() {
|
|
|
204
244
|
}
|
|
205
245
|
function acquireLock() {
|
|
206
246
|
try {
|
|
207
|
-
|
|
247
|
+
fs30.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
|
|
208
248
|
} catch {
|
|
209
249
|
}
|
|
210
250
|
}
|
|
211
251
|
function releaseLock() {
|
|
212
252
|
try {
|
|
213
|
-
if (
|
|
253
|
+
if (fs30.existsSync(LOCK_FILE)) fs30.unlinkSync(LOCK_FILE);
|
|
214
254
|
} catch {
|
|
215
255
|
}
|
|
216
256
|
}
|
|
@@ -218,28 +258,28 @@ var LOCK_FILE, STALE_MS;
|
|
|
218
258
|
var init_lock = __esm({
|
|
219
259
|
"src/lib/lock.ts"() {
|
|
220
260
|
"use strict";
|
|
221
|
-
LOCK_FILE =
|
|
261
|
+
LOCK_FILE = path24.join(os6.tmpdir(), ".caliber.lock");
|
|
222
262
|
STALE_MS = 10 * 60 * 1e3;
|
|
223
263
|
}
|
|
224
264
|
});
|
|
225
265
|
|
|
226
266
|
// src/cli.ts
|
|
227
267
|
import { Command } from "commander";
|
|
228
|
-
import
|
|
229
|
-
import
|
|
268
|
+
import fs35 from "fs";
|
|
269
|
+
import path28 from "path";
|
|
230
270
|
import { fileURLToPath } from "url";
|
|
231
271
|
|
|
232
272
|
// src/commands/init.ts
|
|
233
|
-
import
|
|
273
|
+
import path21 from "path";
|
|
234
274
|
import chalk11 from "chalk";
|
|
235
275
|
import ora3 from "ora";
|
|
236
276
|
import select5 from "@inquirer/select";
|
|
237
277
|
import checkbox from "@inquirer/checkbox";
|
|
238
|
-
import
|
|
278
|
+
import fs26 from "fs";
|
|
239
279
|
|
|
240
280
|
// src/fingerprint/index.ts
|
|
241
|
-
import
|
|
242
|
-
import
|
|
281
|
+
import fs7 from "fs";
|
|
282
|
+
import path6 from "path";
|
|
243
283
|
|
|
244
284
|
// src/fingerprint/git.ts
|
|
245
285
|
import { execSync } from "child_process";
|
|
@@ -292,15 +332,23 @@ function getFileTree(dir, maxDepth = 3) {
|
|
|
292
332
|
for (const e of entries) {
|
|
293
333
|
(e.isDir ? dirs : files).push(e);
|
|
294
334
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
335
|
+
const dirMaxMtime = /* @__PURE__ */ new Map();
|
|
336
|
+
for (const d of dirs) dirMaxMtime.set(d.relPath, d.mtime);
|
|
337
|
+
for (const f of files) {
|
|
338
|
+
let remaining = f.relPath;
|
|
339
|
+
while (true) {
|
|
340
|
+
const lastSlash = remaining.lastIndexOf("/");
|
|
341
|
+
if (lastSlash === -1) break;
|
|
342
|
+
const dirPrefix = remaining.slice(0, lastSlash + 1);
|
|
343
|
+
const current = dirMaxMtime.get(dirPrefix);
|
|
344
|
+
if (current !== void 0 && f.mtime > current) {
|
|
345
|
+
dirMaxMtime.set(dirPrefix, f.mtime);
|
|
301
346
|
}
|
|
347
|
+
remaining = remaining.slice(0, lastSlash);
|
|
302
348
|
}
|
|
303
|
-
|
|
349
|
+
}
|
|
350
|
+
for (const d of dirs) {
|
|
351
|
+
d.mtime = dirMaxMtime.get(d.relPath) ?? d.mtime;
|
|
304
352
|
}
|
|
305
353
|
dirs.sort((a, b) => b.mtime - a.mtime);
|
|
306
354
|
files.sort((a, b) => b.mtime - a.mtime);
|
|
@@ -1004,8 +1052,8 @@ var AnthropicProvider = class {
|
|
|
1004
1052
|
cacheWriteTokens: u.cache_creation_input_tokens
|
|
1005
1053
|
});
|
|
1006
1054
|
}
|
|
1007
|
-
const block = response.content[0];
|
|
1008
|
-
return block
|
|
1055
|
+
const block = response.content?.[0];
|
|
1056
|
+
return block?.type === "text" ? block.text : "";
|
|
1009
1057
|
}
|
|
1010
1058
|
async listModels() {
|
|
1011
1059
|
const models = [];
|
|
@@ -1108,8 +1156,8 @@ var VertexProvider = class {
|
|
|
1108
1156
|
cacheWriteTokens: u.cache_creation_input_tokens
|
|
1109
1157
|
});
|
|
1110
1158
|
}
|
|
1111
|
-
const block = response.content[0];
|
|
1112
|
-
return block
|
|
1159
|
+
const block = response.content?.[0];
|
|
1160
|
+
return block?.type === "text" ? block.text : "";
|
|
1113
1161
|
}
|
|
1114
1162
|
async stream(options, callbacks) {
|
|
1115
1163
|
const messages = options.messages ? [
|
|
@@ -1229,14 +1277,44 @@ var OpenAICompatProvider = class {
|
|
|
1229
1277
|
// src/llm/cursor-acp.ts
|
|
1230
1278
|
import { spawn, execSync as execSync3 } from "child_process";
|
|
1231
1279
|
import os2 from "os";
|
|
1280
|
+
|
|
1281
|
+
// src/llm/seat-based-errors.ts
|
|
1282
|
+
var ERROR_PATTERNS = [
|
|
1283
|
+
{ pattern: /not logged in|not authenticated|login required|unauthorized/i, message: "Authentication required. Run the login command for your provider to re-authenticate." },
|
|
1284
|
+
{ pattern: /rate limit|too many requests|429/i, message: "Rate limit exceeded. Retrying..." },
|
|
1285
|
+
{ pattern: /model.*not found|invalid model|model.*unavailable/i, message: "The requested model is not available. Run `caliber config` to select a different model." }
|
|
1286
|
+
];
|
|
1287
|
+
function parseSeatBasedError(stderr, exitCode) {
|
|
1288
|
+
if (!stderr && exitCode === 0) return null;
|
|
1289
|
+
for (const { pattern, message } of ERROR_PATTERNS) {
|
|
1290
|
+
if (pattern.test(stderr)) return message;
|
|
1291
|
+
}
|
|
1292
|
+
return null;
|
|
1293
|
+
}
|
|
1294
|
+
function isRateLimitError(stderr) {
|
|
1295
|
+
return /rate limit|too many requests|429/i.test(stderr);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
// src/llm/cursor-acp.ts
|
|
1232
1299
|
var AGENT_BIN = "agent";
|
|
1233
1300
|
var IS_WINDOWS = process.platform === "win32";
|
|
1301
|
+
var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
1302
|
+
var SIGKILL_DELAY_MS = 5e3;
|
|
1303
|
+
var STDERR_MAX_BYTES = 10 * 1024;
|
|
1234
1304
|
var CursorAcpProvider = class {
|
|
1235
1305
|
defaultModel;
|
|
1236
1306
|
cursorApiKey;
|
|
1307
|
+
timeoutMs;
|
|
1308
|
+
warmProcess = null;
|
|
1309
|
+
warmModel = null;
|
|
1237
1310
|
constructor(config) {
|
|
1238
1311
|
this.defaultModel = config.model || "sonnet-4.6";
|
|
1239
1312
|
this.cursorApiKey = process.env.CURSOR_API_KEY ?? process.env.CURSOR_AUTH_TOKEN;
|
|
1313
|
+
const envTimeout = process.env.CALIBER_CURSOR_TIMEOUT_MS;
|
|
1314
|
+
this.timeoutMs = envTimeout ? parseInt(envTimeout, 10) : DEFAULT_TIMEOUT_MS;
|
|
1315
|
+
if (!Number.isFinite(this.timeoutMs) || this.timeoutMs < 1e3) {
|
|
1316
|
+
this.timeoutMs = DEFAULT_TIMEOUT_MS;
|
|
1317
|
+
}
|
|
1240
1318
|
}
|
|
1241
1319
|
async call(options) {
|
|
1242
1320
|
const prompt = this.buildPrompt(options);
|
|
@@ -1248,6 +1326,29 @@ var CursorAcpProvider = class {
|
|
|
1248
1326
|
const model = options.model || this.defaultModel;
|
|
1249
1327
|
return this.runPrintStream(model, prompt, callbacks);
|
|
1250
1328
|
}
|
|
1329
|
+
/**
|
|
1330
|
+
* Pre-spawn an agent process so it's ready when the first call comes.
|
|
1331
|
+
* Call this during fingerprint collection to hide spawn latency.
|
|
1332
|
+
*/
|
|
1333
|
+
prewarm(model) {
|
|
1334
|
+
const targetModel = model || this.defaultModel;
|
|
1335
|
+
if (this.warmProcess && !this.warmProcess.killed && this.warmModel === targetModel) return;
|
|
1336
|
+
const args = this.buildArgs(targetModel, false);
|
|
1337
|
+
this.warmProcess = spawn(AGENT_BIN, args, {
|
|
1338
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1339
|
+
env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
|
|
1340
|
+
...IS_WINDOWS && { shell: true }
|
|
1341
|
+
});
|
|
1342
|
+
this.warmModel = targetModel;
|
|
1343
|
+
this.warmProcess.on("error", () => {
|
|
1344
|
+
this.warmProcess = null;
|
|
1345
|
+
this.warmModel = null;
|
|
1346
|
+
});
|
|
1347
|
+
this.warmProcess.on("close", () => {
|
|
1348
|
+
this.warmProcess = null;
|
|
1349
|
+
this.warmModel = null;
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1251
1352
|
buildArgs(model, streaming) {
|
|
1252
1353
|
const args = ["--print", "--trust", "--workspace", os2.tmpdir()];
|
|
1253
1354
|
if (model && model !== "auto" && model !== "default") {
|
|
@@ -1261,23 +1362,78 @@ var CursorAcpProvider = class {
|
|
|
1261
1362
|
}
|
|
1262
1363
|
return args;
|
|
1263
1364
|
}
|
|
1365
|
+
takeWarmProcess(model, streaming) {
|
|
1366
|
+
if (!streaming && this.warmProcess && !this.warmProcess.killed && this.warmModel === model) {
|
|
1367
|
+
const proc = this.warmProcess;
|
|
1368
|
+
this.warmProcess = null;
|
|
1369
|
+
this.warmModel = null;
|
|
1370
|
+
return proc;
|
|
1371
|
+
}
|
|
1372
|
+
return null;
|
|
1373
|
+
}
|
|
1374
|
+
spawnAgent(model, streaming) {
|
|
1375
|
+
const warm = this.takeWarmProcess(model, streaming);
|
|
1376
|
+
if (warm) {
|
|
1377
|
+
const stderrChunks2 = [];
|
|
1378
|
+
warm.stderr?.on("data", (chunk) => {
|
|
1379
|
+
if (Buffer.concat(stderrChunks2).length < STDERR_MAX_BYTES) stderrChunks2.push(chunk);
|
|
1380
|
+
});
|
|
1381
|
+
return { child: warm, stderrChunks: stderrChunks2 };
|
|
1382
|
+
}
|
|
1383
|
+
const args = this.buildArgs(model, streaming);
|
|
1384
|
+
const child = spawn(AGENT_BIN, args, {
|
|
1385
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1386
|
+
env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
|
|
1387
|
+
...IS_WINDOWS && { shell: true }
|
|
1388
|
+
});
|
|
1389
|
+
const stderrChunks = [];
|
|
1390
|
+
child.stderr.on("data", (chunk) => {
|
|
1391
|
+
if (Buffer.concat(stderrChunks).length < STDERR_MAX_BYTES) stderrChunks.push(chunk);
|
|
1392
|
+
});
|
|
1393
|
+
return { child, stderrChunks };
|
|
1394
|
+
}
|
|
1395
|
+
killWithEscalation(child) {
|
|
1396
|
+
child.kill("SIGTERM");
|
|
1397
|
+
const killTimer = setTimeout(() => {
|
|
1398
|
+
if (!child.killed) child.kill("SIGKILL");
|
|
1399
|
+
}, SIGKILL_DELAY_MS);
|
|
1400
|
+
killTimer.unref();
|
|
1401
|
+
}
|
|
1402
|
+
buildErrorMessage(code, stderrChunks) {
|
|
1403
|
+
const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
|
|
1404
|
+
const parsed = parseSeatBasedError(stderr, code);
|
|
1405
|
+
if (parsed) return parsed;
|
|
1406
|
+
const base = `Cursor agent exited with code ${code}`;
|
|
1407
|
+
return stderr ? `${base}: ${stderr.slice(0, 200)}` : base;
|
|
1408
|
+
}
|
|
1264
1409
|
runPrint(model, prompt) {
|
|
1265
1410
|
return new Promise((resolve2, reject) => {
|
|
1266
|
-
const
|
|
1267
|
-
|
|
1268
|
-
stdio: ["pipe", "pipe", "ignore"],
|
|
1269
|
-
env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
|
|
1270
|
-
...IS_WINDOWS && { shell: true }
|
|
1271
|
-
});
|
|
1411
|
+
const { child, stderrChunks } = this.spawnAgent(model, false);
|
|
1412
|
+
let settled = false;
|
|
1272
1413
|
const chunks = [];
|
|
1273
|
-
child.stdout.on("data", (data) =>
|
|
1274
|
-
|
|
1414
|
+
child.stdout.on("data", (data) => chunks.push(data));
|
|
1415
|
+
const timer = setTimeout(() => {
|
|
1416
|
+
this.killWithEscalation(child);
|
|
1417
|
+
if (!settled) {
|
|
1418
|
+
settled = true;
|
|
1419
|
+
reject(new Error(`Cursor agent timed out after ${this.timeoutMs / 1e3}s. Set CALIBER_CURSOR_TIMEOUT_MS to increase.`));
|
|
1420
|
+
}
|
|
1421
|
+
}, this.timeoutMs);
|
|
1422
|
+
timer.unref();
|
|
1423
|
+
child.on("error", (err) => {
|
|
1424
|
+
clearTimeout(timer);
|
|
1425
|
+
if (!settled) {
|
|
1426
|
+
settled = true;
|
|
1427
|
+
reject(err);
|
|
1428
|
+
}
|
|
1275
1429
|
});
|
|
1276
|
-
child.on("error", reject);
|
|
1277
1430
|
child.on("close", (code) => {
|
|
1431
|
+
clearTimeout(timer);
|
|
1432
|
+
if (settled) return;
|
|
1433
|
+
settled = true;
|
|
1278
1434
|
const output = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1279
1435
|
if (code !== 0 && !output) {
|
|
1280
|
-
reject(new Error(
|
|
1436
|
+
reject(new Error(this.buildErrorMessage(code, stderrChunks)));
|
|
1281
1437
|
} else {
|
|
1282
1438
|
resolve2(output);
|
|
1283
1439
|
}
|
|
@@ -1288,14 +1444,20 @@ var CursorAcpProvider = class {
|
|
|
1288
1444
|
}
|
|
1289
1445
|
runPrintStream(model, prompt, callbacks) {
|
|
1290
1446
|
return new Promise((resolve2, reject) => {
|
|
1291
|
-
const
|
|
1292
|
-
const child = spawn(AGENT_BIN, args, {
|
|
1293
|
-
stdio: ["pipe", "pipe", "ignore"],
|
|
1294
|
-
env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
|
|
1295
|
-
...IS_WINDOWS && { shell: true }
|
|
1296
|
-
});
|
|
1447
|
+
const { child, stderrChunks } = this.spawnAgent(model, true);
|
|
1297
1448
|
let buffer = "";
|
|
1298
1449
|
let endCalled = false;
|
|
1450
|
+
let settled = false;
|
|
1451
|
+
const timer = setTimeout(() => {
|
|
1452
|
+
this.killWithEscalation(child);
|
|
1453
|
+
if (!settled) {
|
|
1454
|
+
settled = true;
|
|
1455
|
+
const err = new Error(`Cursor agent timed out after ${this.timeoutMs / 1e3}s. Set CALIBER_CURSOR_TIMEOUT_MS to increase.`);
|
|
1456
|
+
callbacks.onError(err);
|
|
1457
|
+
reject(err);
|
|
1458
|
+
}
|
|
1459
|
+
}, this.timeoutMs);
|
|
1460
|
+
timer.unref();
|
|
1299
1461
|
child.stdout.on("data", (data) => {
|
|
1300
1462
|
buffer += data.toString("utf-8");
|
|
1301
1463
|
const lines = buffer.split("\n");
|
|
@@ -1320,16 +1482,26 @@ var CursorAcpProvider = class {
|
|
|
1320
1482
|
}
|
|
1321
1483
|
});
|
|
1322
1484
|
child.on("error", (err) => {
|
|
1323
|
-
|
|
1324
|
-
|
|
1485
|
+
clearTimeout(timer);
|
|
1486
|
+
if (!settled) {
|
|
1487
|
+
settled = true;
|
|
1488
|
+
callbacks.onError(err);
|
|
1489
|
+
reject(err);
|
|
1490
|
+
}
|
|
1325
1491
|
});
|
|
1326
1492
|
child.on("close", (code) => {
|
|
1493
|
+
clearTimeout(timer);
|
|
1494
|
+
if (settled) return;
|
|
1495
|
+
settled = true;
|
|
1327
1496
|
if (buffer.trim()) {
|
|
1328
1497
|
try {
|
|
1329
1498
|
const event = JSON.parse(buffer);
|
|
1330
1499
|
if (event.type === "assistant") {
|
|
1331
|
-
const
|
|
1332
|
-
if (
|
|
1500
|
+
const isDelta = "timestamp_ms" in event;
|
|
1501
|
+
if (isDelta) {
|
|
1502
|
+
const text = event.message?.content?.[0]?.text || event.content;
|
|
1503
|
+
if (text) callbacks.onText(text);
|
|
1504
|
+
}
|
|
1333
1505
|
} else if (event.type === "result") {
|
|
1334
1506
|
endCalled = true;
|
|
1335
1507
|
callbacks.onEnd({ stopReason: event.is_error ? "error" : "end_turn" });
|
|
@@ -1342,9 +1514,16 @@ var CursorAcpProvider = class {
|
|
|
1342
1514
|
callbacks.onEnd({ stopReason: code === 0 ? "end_turn" : "error" });
|
|
1343
1515
|
}
|
|
1344
1516
|
if (code !== 0 && code !== null) {
|
|
1345
|
-
const
|
|
1346
|
-
|
|
1347
|
-
|
|
1517
|
+
const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
|
|
1518
|
+
if (isRateLimitError(stderr)) {
|
|
1519
|
+
const err = new Error("Rate limit exceeded");
|
|
1520
|
+
callbacks.onError(err);
|
|
1521
|
+
reject(err);
|
|
1522
|
+
} else {
|
|
1523
|
+
const err = new Error(this.buildErrorMessage(code, stderrChunks));
|
|
1524
|
+
callbacks.onError(err);
|
|
1525
|
+
reject(err);
|
|
1526
|
+
}
|
|
1348
1527
|
} else {
|
|
1349
1528
|
resolve2();
|
|
1350
1529
|
}
|
|
@@ -1371,28 +1550,48 @@ var CursorAcpProvider = class {
|
|
|
1371
1550
|
};
|
|
1372
1551
|
function isCursorAgentAvailable() {
|
|
1373
1552
|
try {
|
|
1374
|
-
const cmd =
|
|
1553
|
+
const cmd = IS_WINDOWS ? `where ${AGENT_BIN}` : `which ${AGENT_BIN}`;
|
|
1375
1554
|
execSync3(cmd, { stdio: "ignore" });
|
|
1376
1555
|
return true;
|
|
1377
1556
|
} catch {
|
|
1378
1557
|
return false;
|
|
1379
1558
|
}
|
|
1380
1559
|
}
|
|
1560
|
+
function isCursorLoggedIn() {
|
|
1561
|
+
try {
|
|
1562
|
+
const result = execSync3(`${AGENT_BIN} status`, { stdio: ["ignore", "pipe", "ignore"], timeout: 5e3 });
|
|
1563
|
+
return !result.toString().includes("not logged in");
|
|
1564
|
+
} catch {
|
|
1565
|
+
return false;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1381
1568
|
|
|
1382
1569
|
// src/llm/claude-cli.ts
|
|
1383
1570
|
import { spawn as spawn2, execSync as execSync4 } from "child_process";
|
|
1384
1571
|
var CLAUDE_CLI_BIN = "claude";
|
|
1385
|
-
var
|
|
1572
|
+
var DEFAULT_TIMEOUT_MS2 = 10 * 60 * 1e3;
|
|
1386
1573
|
var IS_WINDOWS2 = process.platform === "win32";
|
|
1574
|
+
function spawnClaude(args) {
|
|
1575
|
+
return IS_WINDOWS2 ? spawn2([CLAUDE_CLI_BIN, ...args].join(" "), {
|
|
1576
|
+
cwd: process.cwd(),
|
|
1577
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1578
|
+
env: process.env,
|
|
1579
|
+
shell: true
|
|
1580
|
+
}) : spawn2(CLAUDE_CLI_BIN, args, {
|
|
1581
|
+
cwd: process.cwd(),
|
|
1582
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1583
|
+
env: process.env
|
|
1584
|
+
});
|
|
1585
|
+
}
|
|
1387
1586
|
var ClaudeCliProvider = class {
|
|
1388
1587
|
defaultModel;
|
|
1389
1588
|
timeoutMs;
|
|
1390
1589
|
constructor(config) {
|
|
1391
1590
|
this.defaultModel = config.model || "default";
|
|
1392
1591
|
const envTimeout = process.env.CALIBER_CLAUDE_CLI_TIMEOUT_MS;
|
|
1393
|
-
this.timeoutMs = envTimeout ? parseInt(envTimeout, 10) :
|
|
1592
|
+
this.timeoutMs = envTimeout ? parseInt(envTimeout, 10) : DEFAULT_TIMEOUT_MS2;
|
|
1394
1593
|
if (!Number.isFinite(this.timeoutMs) || this.timeoutMs < 1e3) {
|
|
1395
|
-
this.timeoutMs =
|
|
1594
|
+
this.timeoutMs = DEFAULT_TIMEOUT_MS2;
|
|
1396
1595
|
}
|
|
1397
1596
|
}
|
|
1398
1597
|
async call(options) {
|
|
@@ -1403,23 +1602,16 @@ var ClaudeCliProvider = class {
|
|
|
1403
1602
|
const combined = this.buildCombinedPrompt(options);
|
|
1404
1603
|
const args = ["-p"];
|
|
1405
1604
|
if (options.model) args.push("--model", options.model);
|
|
1406
|
-
const child =
|
|
1407
|
-
cwd: process.cwd(),
|
|
1408
|
-
stdio: ["pipe", "pipe", "inherit"],
|
|
1409
|
-
env: process.env,
|
|
1410
|
-
shell: true
|
|
1411
|
-
}) : spawn2(CLAUDE_CLI_BIN, args, {
|
|
1412
|
-
cwd: process.cwd(),
|
|
1413
|
-
stdio: ["pipe", "pipe", "inherit"],
|
|
1414
|
-
env: process.env
|
|
1415
|
-
});
|
|
1605
|
+
const child = spawnClaude(args);
|
|
1416
1606
|
child.stdin.end(combined);
|
|
1417
1607
|
let settled = false;
|
|
1418
1608
|
const chunks = [];
|
|
1609
|
+
const stderrChunks = [];
|
|
1419
1610
|
child.stdout.on("data", (chunk) => {
|
|
1420
1611
|
chunks.push(chunk);
|
|
1421
1612
|
callbacks.onText(chunk.toString("utf-8"));
|
|
1422
1613
|
});
|
|
1614
|
+
child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
|
|
1423
1615
|
const timer = setTimeout(() => {
|
|
1424
1616
|
child.kill("SIGTERM");
|
|
1425
1617
|
if (!settled) {
|
|
@@ -1445,45 +1637,40 @@ var ClaudeCliProvider = class {
|
|
|
1445
1637
|
if (code === 0) {
|
|
1446
1638
|
callbacks.onEnd({ stopReason: "end_turn" });
|
|
1447
1639
|
} else {
|
|
1640
|
+
const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
|
|
1641
|
+
const friendly = parseSeatBasedError(stderr, code);
|
|
1448
1642
|
const stdout = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1449
|
-
const
|
|
1450
|
-
|
|
1643
|
+
const base = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
|
|
1644
|
+
const detail = friendly || stderr || (stdout ? stdout.slice(0, 200) : "");
|
|
1645
|
+
callbacks.onError(new Error(detail ? `${base}. ${detail}` : base));
|
|
1451
1646
|
}
|
|
1452
1647
|
});
|
|
1453
1648
|
}
|
|
1454
1649
|
buildCombinedPrompt(options) {
|
|
1455
1650
|
const streamOpts = options;
|
|
1456
1651
|
const hasHistory = streamOpts.messages && streamOpts.messages.length > 0;
|
|
1457
|
-
let combined = "";
|
|
1458
|
-
combined += "[[System]]\n" + options.system + "\n\n";
|
|
1652
|
+
let combined = options.system + "\n\n";
|
|
1459
1653
|
if (hasHistory) {
|
|
1460
1654
|
for (const msg of streamOpts.messages) {
|
|
1461
|
-
|
|
1462
|
-
${msg.content}
|
|
1655
|
+
const label = msg.role === "user" ? "User" : "Assistant";
|
|
1656
|
+
combined += `${label}: ${msg.content}
|
|
1463
1657
|
|
|
1464
1658
|
`;
|
|
1465
1659
|
}
|
|
1466
1660
|
}
|
|
1467
|
-
combined +=
|
|
1661
|
+
combined += options.prompt;
|
|
1468
1662
|
return combined;
|
|
1469
1663
|
}
|
|
1470
1664
|
runClaudePrint(combinedPrompt, model) {
|
|
1471
1665
|
return new Promise((resolve2, reject) => {
|
|
1472
1666
|
const args = ["-p"];
|
|
1473
1667
|
if (model) args.push("--model", model);
|
|
1474
|
-
const child =
|
|
1475
|
-
cwd: process.cwd(),
|
|
1476
|
-
stdio: ["pipe", "pipe", "inherit"],
|
|
1477
|
-
env: process.env,
|
|
1478
|
-
shell: true
|
|
1479
|
-
}) : spawn2(CLAUDE_CLI_BIN, args, {
|
|
1480
|
-
cwd: process.cwd(),
|
|
1481
|
-
stdio: ["pipe", "pipe", "inherit"],
|
|
1482
|
-
env: process.env
|
|
1483
|
-
});
|
|
1668
|
+
const child = spawnClaude(args);
|
|
1484
1669
|
child.stdin.end(combinedPrompt);
|
|
1485
1670
|
const chunks = [];
|
|
1671
|
+
const stderrChunks = [];
|
|
1486
1672
|
child.stdout.on("data", (chunk) => chunks.push(chunk));
|
|
1673
|
+
child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
|
|
1487
1674
|
child.on("error", (err) => {
|
|
1488
1675
|
clearTimeout(timer);
|
|
1489
1676
|
reject(err);
|
|
@@ -1494,8 +1681,11 @@ ${msg.content}
|
|
|
1494
1681
|
if (code === 0) {
|
|
1495
1682
|
resolve2(stdout);
|
|
1496
1683
|
} else {
|
|
1497
|
-
const
|
|
1498
|
-
|
|
1684
|
+
const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
|
|
1685
|
+
const friendly = parseSeatBasedError(stderr, code);
|
|
1686
|
+
const base = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
|
|
1687
|
+
const detail = friendly || stderr || (stdout ? stdout.slice(0, 200) : "");
|
|
1688
|
+
reject(new Error(detail ? `${base}. ${detail}` : base));
|
|
1499
1689
|
}
|
|
1500
1690
|
});
|
|
1501
1691
|
const timer = setTimeout(() => {
|
|
@@ -1669,6 +1859,7 @@ async function handleModelNotAvailable(failedModel, provider, config) {
|
|
|
1669
1859
|
}
|
|
1670
1860
|
|
|
1671
1861
|
// src/llm/index.ts
|
|
1862
|
+
init_types();
|
|
1672
1863
|
init_config();
|
|
1673
1864
|
var cachedProvider = null;
|
|
1674
1865
|
var cachedConfig = null;
|
|
@@ -1744,11 +1935,11 @@ async function llmCall(options) {
|
|
|
1744
1935
|
throw error;
|
|
1745
1936
|
}
|
|
1746
1937
|
if (isOverloaded(error) && attempt < MAX_RETRIES) {
|
|
1747
|
-
await new Promise((r) => setTimeout(r,
|
|
1938
|
+
await new Promise((r) => setTimeout(r, 1e3 * Math.pow(2, attempt - 1)));
|
|
1748
1939
|
continue;
|
|
1749
1940
|
}
|
|
1750
1941
|
if (isTransientError(error) && attempt < MAX_RETRIES) {
|
|
1751
|
-
await new Promise((r) => setTimeout(r,
|
|
1942
|
+
await new Promise((r) => setTimeout(r, 1e3 * Math.pow(2, attempt - 1)));
|
|
1752
1943
|
continue;
|
|
1753
1944
|
}
|
|
1754
1945
|
throw error;
|
|
@@ -1764,7 +1955,8 @@ async function validateModel(options) {
|
|
|
1764
1955
|
const provider = getProvider();
|
|
1765
1956
|
const config = cachedConfig;
|
|
1766
1957
|
if (!config) return;
|
|
1767
|
-
|
|
1958
|
+
const { isSeatBased: isSeatBased2 } = await Promise.resolve().then(() => (init_types(), types_exports));
|
|
1959
|
+
if (isSeatBased2(config.provider)) return;
|
|
1768
1960
|
const modelsToCheck = [config.model];
|
|
1769
1961
|
if (options?.fast) {
|
|
1770
1962
|
const { getFastModel: getFastModel2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
@@ -2212,12 +2404,112 @@ async function detectProjectStack(fileTree, suffixCounts) {
|
|
|
2212
2404
|
|
|
2213
2405
|
// src/fingerprint/index.ts
|
|
2214
2406
|
init_config();
|
|
2407
|
+
|
|
2408
|
+
// src/fingerprint/cache.ts
|
|
2409
|
+
import fs6 from "fs";
|
|
2410
|
+
import path5 from "path";
|
|
2411
|
+
import crypto from "crypto";
|
|
2412
|
+
import { execSync as execSync5 } from "child_process";
|
|
2413
|
+
var CACHE_VERSION = 1;
|
|
2414
|
+
var CACHE_DIR = ".caliber/cache";
|
|
2415
|
+
var CACHE_FILE = "fingerprint.json";
|
|
2416
|
+
function getCachePath(dir) {
|
|
2417
|
+
return path5.join(dir, CACHE_DIR, CACHE_FILE);
|
|
2418
|
+
}
|
|
2419
|
+
function getGitHead(dir) {
|
|
2420
|
+
try {
|
|
2421
|
+
return execSync5("git rev-parse HEAD", {
|
|
2422
|
+
cwd: dir,
|
|
2423
|
+
encoding: "utf-8",
|
|
2424
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
2425
|
+
timeout: 3e3
|
|
2426
|
+
}).trim();
|
|
2427
|
+
} catch {
|
|
2428
|
+
return "";
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
function getDirtySignature(dir) {
|
|
2432
|
+
try {
|
|
2433
|
+
const output = execSync5("git diff --name-only HEAD", {
|
|
2434
|
+
cwd: dir,
|
|
2435
|
+
encoding: "utf-8",
|
|
2436
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
2437
|
+
timeout: 3e3
|
|
2438
|
+
}).trim();
|
|
2439
|
+
return output.split("\n").slice(0, 100).join("\n");
|
|
2440
|
+
} catch {
|
|
2441
|
+
return "";
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
function computeTreeSignature(fileTree, dir) {
|
|
2445
|
+
const hash = crypto.createHash("sha256");
|
|
2446
|
+
hash.update(fileTree.join("\0"));
|
|
2447
|
+
hash.update("\n");
|
|
2448
|
+
hash.update(getDirtySignature(dir));
|
|
2449
|
+
return hash.digest("hex").slice(0, 16);
|
|
2450
|
+
}
|
|
2451
|
+
function loadFingerprintCache(dir, fileTree) {
|
|
2452
|
+
const cachePath = getCachePath(dir);
|
|
2453
|
+
try {
|
|
2454
|
+
if (!fs6.existsSync(cachePath)) return null;
|
|
2455
|
+
const raw = fs6.readFileSync(cachePath, "utf-8");
|
|
2456
|
+
const cache = JSON.parse(raw);
|
|
2457
|
+
if (cache.version !== CACHE_VERSION) return null;
|
|
2458
|
+
const currentHead = getGitHead(dir);
|
|
2459
|
+
if (currentHead && cache.gitHead !== currentHead) return null;
|
|
2460
|
+
const currentSig = computeTreeSignature(fileTree, dir);
|
|
2461
|
+
if (cache.treeSignature !== currentSig) return null;
|
|
2462
|
+
return {
|
|
2463
|
+
codeAnalysis: cache.codeAnalysis,
|
|
2464
|
+
languages: cache.languages,
|
|
2465
|
+
frameworks: cache.frameworks,
|
|
2466
|
+
tools: cache.tools
|
|
2467
|
+
};
|
|
2468
|
+
} catch {
|
|
2469
|
+
return null;
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
function saveFingerprintCache(dir, fileTree, codeAnalysis, languages, frameworks, tools) {
|
|
2473
|
+
const cachePath = getCachePath(dir);
|
|
2474
|
+
try {
|
|
2475
|
+
const cacheDir = path5.dirname(cachePath);
|
|
2476
|
+
if (!fs6.existsSync(cacheDir)) {
|
|
2477
|
+
fs6.mkdirSync(cacheDir, { recursive: true });
|
|
2478
|
+
}
|
|
2479
|
+
const cache = {
|
|
2480
|
+
version: CACHE_VERSION,
|
|
2481
|
+
gitHead: getGitHead(dir),
|
|
2482
|
+
treeSignature: computeTreeSignature(fileTree, dir),
|
|
2483
|
+
codeAnalysis,
|
|
2484
|
+
languages,
|
|
2485
|
+
frameworks,
|
|
2486
|
+
tools
|
|
2487
|
+
};
|
|
2488
|
+
fs6.writeFileSync(cachePath, JSON.stringify(cache), "utf-8");
|
|
2489
|
+
} catch {
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2493
|
+
// src/fingerprint/index.ts
|
|
2215
2494
|
async function collectFingerprint(dir) {
|
|
2216
2495
|
const gitRemoteUrl = getGitRemoteUrl();
|
|
2217
2496
|
const fileTree = getFileTree(dir);
|
|
2218
2497
|
const existingConfigs = readExistingConfigs(dir);
|
|
2219
|
-
const codeAnalysis = analyzeCode(dir);
|
|
2220
2498
|
const packageName = readPackageName(dir);
|
|
2499
|
+
const cached = loadFingerprintCache(dir, fileTree);
|
|
2500
|
+
if (cached) {
|
|
2501
|
+
return {
|
|
2502
|
+
gitRemoteUrl,
|
|
2503
|
+
packageName,
|
|
2504
|
+
languages: cached.languages,
|
|
2505
|
+
frameworks: cached.frameworks,
|
|
2506
|
+
tools: cached.tools,
|
|
2507
|
+
fileTree,
|
|
2508
|
+
existingConfigs,
|
|
2509
|
+
codeAnalysis: cached.codeAnalysis
|
|
2510
|
+
};
|
|
2511
|
+
}
|
|
2512
|
+
const codeAnalysis = analyzeCode(dir);
|
|
2221
2513
|
const fingerprint = {
|
|
2222
2514
|
gitRemoteUrl,
|
|
2223
2515
|
packageName,
|
|
@@ -2229,13 +2521,21 @@ async function collectFingerprint(dir) {
|
|
|
2229
2521
|
codeAnalysis
|
|
2230
2522
|
};
|
|
2231
2523
|
await enrichWithLLM(fingerprint);
|
|
2524
|
+
saveFingerprintCache(
|
|
2525
|
+
dir,
|
|
2526
|
+
fileTree,
|
|
2527
|
+
codeAnalysis,
|
|
2528
|
+
fingerprint.languages,
|
|
2529
|
+
fingerprint.frameworks,
|
|
2530
|
+
fingerprint.tools
|
|
2531
|
+
);
|
|
2232
2532
|
return fingerprint;
|
|
2233
2533
|
}
|
|
2234
2534
|
function readPackageName(dir) {
|
|
2235
2535
|
try {
|
|
2236
|
-
const pkgPath =
|
|
2237
|
-
if (!
|
|
2238
|
-
const pkg3 = JSON.parse(
|
|
2536
|
+
const pkgPath = path6.join(dir, "package.json");
|
|
2537
|
+
if (!fs7.existsSync(pkgPath)) return void 0;
|
|
2538
|
+
const pkg3 = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
|
|
2239
2539
|
return pkg3.name;
|
|
2240
2540
|
} catch {
|
|
2241
2541
|
return void 0;
|
|
@@ -2249,7 +2549,7 @@ async function enrichWithLLM(fingerprint) {
|
|
|
2249
2549
|
const suffixCounts = {};
|
|
2250
2550
|
for (const entry of fingerprint.fileTree) {
|
|
2251
2551
|
if (entry.endsWith("/")) continue;
|
|
2252
|
-
const ext =
|
|
2552
|
+
const ext = path6.extname(entry).toLowerCase();
|
|
2253
2553
|
if (ext) {
|
|
2254
2554
|
suffixCounts[ext] = (suffixCounts[ext] || 0) + 1;
|
|
2255
2555
|
}
|
|
@@ -2268,15 +2568,15 @@ init_config();
|
|
|
2268
2568
|
// src/utils/dependencies.ts
|
|
2269
2569
|
import { readFileSync } from "fs";
|
|
2270
2570
|
import { join } from "path";
|
|
2271
|
-
function readFileOrNull(
|
|
2571
|
+
function readFileOrNull(path30) {
|
|
2272
2572
|
try {
|
|
2273
|
-
return readFileSync(
|
|
2573
|
+
return readFileSync(path30, "utf-8");
|
|
2274
2574
|
} catch {
|
|
2275
2575
|
return null;
|
|
2276
2576
|
}
|
|
2277
2577
|
}
|
|
2278
|
-
function readJsonOrNull(
|
|
2279
|
-
const content = readFileOrNull(
|
|
2578
|
+
function readJsonOrNull(path30) {
|
|
2579
|
+
const content = readFileOrNull(path30);
|
|
2280
2580
|
if (!content) return null;
|
|
2281
2581
|
try {
|
|
2282
2582
|
return JSON.parse(content);
|
|
@@ -2500,15 +2800,14 @@ Generate the skill content following the instructions in the system prompt.`;
|
|
|
2500
2800
|
content
|
|
2501
2801
|
};
|
|
2502
2802
|
}
|
|
2503
|
-
async function
|
|
2803
|
+
async function streamGeneration(config) {
|
|
2504
2804
|
const provider = getProvider();
|
|
2505
|
-
const userMessage = buildGeneratePrompt(fingerprint, targetAgent, prompt, failingChecks, currentScore, passingChecks);
|
|
2506
2805
|
let attempt = 0;
|
|
2507
2806
|
const attemptGeneration = async () => {
|
|
2508
2807
|
attempt++;
|
|
2509
2808
|
const maxTokensForAttempt = Math.min(
|
|
2510
|
-
|
|
2511
|
-
|
|
2809
|
+
config.baseMaxTokens + attempt * config.tokenIncrement,
|
|
2810
|
+
config.maxTokensCap
|
|
2512
2811
|
);
|
|
2513
2812
|
return new Promise((resolve2) => {
|
|
2514
2813
|
let preJsonBuffer = "";
|
|
@@ -2518,8 +2817,8 @@ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failing
|
|
|
2518
2817
|
let stopReason = null;
|
|
2519
2818
|
provider.stream(
|
|
2520
2819
|
{
|
|
2521
|
-
system:
|
|
2522
|
-
prompt: userMessage,
|
|
2820
|
+
system: config.systemPrompt,
|
|
2821
|
+
prompt: config.userMessage,
|
|
2523
2822
|
maxTokens: maxTokensForAttempt
|
|
2524
2823
|
},
|
|
2525
2824
|
{
|
|
@@ -2532,7 +2831,7 @@ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failing
|
|
|
2532
2831
|
const trimmed = completedLines[i].trim();
|
|
2533
2832
|
if (trimmed.startsWith("STATUS:")) {
|
|
2534
2833
|
const status = trimmed.slice(7).trim();
|
|
2535
|
-
if (status && callbacks) callbacks.onStatus(status);
|
|
2834
|
+
if (status && config.callbacks) config.callbacks.onStatus(status);
|
|
2536
2835
|
}
|
|
2537
2836
|
}
|
|
2538
2837
|
sentStatuses = completedLines.length;
|
|
@@ -2562,7 +2861,7 @@ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failing
|
|
|
2562
2861
|
} catch {
|
|
2563
2862
|
}
|
|
2564
2863
|
if (!setup && stopReason === "max_tokens" && attempt < MAX_RETRIES2) {
|
|
2565
|
-
if (callbacks) callbacks.onStatus("Output was truncated, retrying with higher token limit...");
|
|
2864
|
+
if (config.callbacks) config.callbacks.onStatus("Output was truncated, retrying with higher token limit...");
|
|
2566
2865
|
setTimeout(() => attemptGeneration().then(resolve2), 1e3);
|
|
2567
2866
|
return;
|
|
2568
2867
|
}
|
|
@@ -2572,7 +2871,7 @@ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failing
|
|
|
2572
2871
|
explanation = explainMatch[1].trim();
|
|
2573
2872
|
}
|
|
2574
2873
|
if (setup) {
|
|
2575
|
-
if (callbacks) callbacks.onComplete(setup, explanation);
|
|
2874
|
+
if (config.callbacks) config.callbacks.onComplete(setup, explanation);
|
|
2576
2875
|
resolve2({ setup, explanation, stopReason: stopReason ?? void 0 });
|
|
2577
2876
|
} else {
|
|
2578
2877
|
resolve2({ setup: null, explanation, raw: preJsonBuffer, stopReason: stopReason ?? void 0 });
|
|
@@ -2580,117 +2879,43 @@ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failing
|
|
|
2580
2879
|
},
|
|
2581
2880
|
onError: (error) => {
|
|
2582
2881
|
if (isTransientError2(error) && attempt < MAX_RETRIES2) {
|
|
2583
|
-
if (callbacks) callbacks.onStatus("Connection interrupted, retrying...");
|
|
2882
|
+
if (config.callbacks) config.callbacks.onStatus("Connection interrupted, retrying...");
|
|
2584
2883
|
setTimeout(() => attemptGeneration().then(resolve2), 2e3);
|
|
2585
2884
|
return;
|
|
2586
2885
|
}
|
|
2587
|
-
if (callbacks) callbacks.onError(error.message);
|
|
2886
|
+
if (config.callbacks) config.callbacks.onError(error.message);
|
|
2588
2887
|
resolve2({ setup: null, raw: error.message, stopReason: "error" });
|
|
2589
2888
|
}
|
|
2590
2889
|
}
|
|
2591
2890
|
).catch((error) => {
|
|
2592
|
-
if (callbacks) callbacks.onError(error.message);
|
|
2891
|
+
if (config.callbacks) config.callbacks.onError(error.message);
|
|
2593
2892
|
resolve2({ setup: null, raw: error.message, stopReason: "error" });
|
|
2594
2893
|
});
|
|
2595
2894
|
});
|
|
2596
2895
|
};
|
|
2597
2896
|
return attemptGeneration();
|
|
2598
2897
|
}
|
|
2898
|
+
async function generateCore(fingerprint, targetAgent, prompt, callbacks, failingChecks, currentScore, passingChecks) {
|
|
2899
|
+
const userMessage = buildGeneratePrompt(fingerprint, targetAgent, prompt, failingChecks, currentScore, passingChecks);
|
|
2900
|
+
return streamGeneration({
|
|
2901
|
+
systemPrompt: CORE_GENERATION_PROMPT,
|
|
2902
|
+
userMessage,
|
|
2903
|
+
baseMaxTokens: CORE_MAX_TOKENS,
|
|
2904
|
+
tokenIncrement: 8e3,
|
|
2905
|
+
maxTokensCap: GENERATION_MAX_TOKENS,
|
|
2906
|
+
callbacks
|
|
2907
|
+
});
|
|
2908
|
+
}
|
|
2599
2909
|
async function generateMonolithic(fingerprint, targetAgent, prompt, callbacks, failingChecks, currentScore, passingChecks) {
|
|
2600
|
-
const provider = getProvider();
|
|
2601
2910
|
const userMessage = buildGeneratePrompt(fingerprint, targetAgent, prompt, failingChecks, currentScore, passingChecks);
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
let preJsonBuffer = "";
|
|
2611
|
-
let jsonContent = "";
|
|
2612
|
-
let inJson = false;
|
|
2613
|
-
let sentStatuses = 0;
|
|
2614
|
-
let stopReason = null;
|
|
2615
|
-
provider.stream(
|
|
2616
|
-
{
|
|
2617
|
-
system: GENERATION_SYSTEM_PROMPT,
|
|
2618
|
-
prompt: userMessage,
|
|
2619
|
-
maxTokens: maxTokensForAttempt
|
|
2620
|
-
},
|
|
2621
|
-
{
|
|
2622
|
-
onText: (text) => {
|
|
2623
|
-
if (!inJson) {
|
|
2624
|
-
preJsonBuffer += text;
|
|
2625
|
-
const lines = preJsonBuffer.split("\n");
|
|
2626
|
-
const completedLines = lines.slice(0, -1);
|
|
2627
|
-
for (let i = sentStatuses; i < completedLines.length; i++) {
|
|
2628
|
-
const trimmed = completedLines[i].trim();
|
|
2629
|
-
if (trimmed.startsWith("STATUS:")) {
|
|
2630
|
-
const status = trimmed.slice(7).trim();
|
|
2631
|
-
if (status && callbacks) callbacks.onStatus(status);
|
|
2632
|
-
}
|
|
2633
|
-
}
|
|
2634
|
-
sentStatuses = completedLines.length;
|
|
2635
|
-
const jsonStartMatch = preJsonBuffer.match(/(?:^|\n)\s*(?:```json\s*\n\s*)?\{(?=\s*")/);
|
|
2636
|
-
if (jsonStartMatch) {
|
|
2637
|
-
const matchIndex = preJsonBuffer.indexOf("{", jsonStartMatch.index);
|
|
2638
|
-
inJson = true;
|
|
2639
|
-
jsonContent = preJsonBuffer.slice(matchIndex);
|
|
2640
|
-
}
|
|
2641
|
-
} else {
|
|
2642
|
-
jsonContent += text;
|
|
2643
|
-
}
|
|
2644
|
-
},
|
|
2645
|
-
onEnd: (meta) => {
|
|
2646
|
-
stopReason = meta?.stopReason ?? null;
|
|
2647
|
-
let setup = null;
|
|
2648
|
-
let jsonToParse = (jsonContent || preJsonBuffer).replace(/```\s*$/g, "").trim();
|
|
2649
|
-
if (!jsonContent && preJsonBuffer) {
|
|
2650
|
-
const fallbackMatch = preJsonBuffer.match(/(?:^|\n)\s*(?:```json\s*\n\s*)?\{(?=\s*")/);
|
|
2651
|
-
if (fallbackMatch) {
|
|
2652
|
-
const matchIndex = preJsonBuffer.indexOf("{", fallbackMatch.index);
|
|
2653
|
-
jsonToParse = preJsonBuffer.slice(matchIndex).replace(/```\s*$/g, "").trim();
|
|
2654
|
-
}
|
|
2655
|
-
}
|
|
2656
|
-
try {
|
|
2657
|
-
setup = JSON.parse(jsonToParse);
|
|
2658
|
-
} catch {
|
|
2659
|
-
}
|
|
2660
|
-
if (!setup && stopReason === "max_tokens" && attempt < MAX_RETRIES2) {
|
|
2661
|
-
if (callbacks) callbacks.onStatus("Output was truncated, retrying with higher token limit...");
|
|
2662
|
-
setTimeout(() => attemptGeneration().then(resolve2), 1e3);
|
|
2663
|
-
return;
|
|
2664
|
-
}
|
|
2665
|
-
let explanation;
|
|
2666
|
-
const explainMatch = preJsonBuffer.match(/EXPLAIN:\s*\n([\s\S]*?)(?=\n\s*(`{3}|\{))/);
|
|
2667
|
-
if (explainMatch) {
|
|
2668
|
-
explanation = explainMatch[1].trim();
|
|
2669
|
-
}
|
|
2670
|
-
if (setup) {
|
|
2671
|
-
if (callbacks) callbacks.onComplete(setup, explanation);
|
|
2672
|
-
resolve2({ setup, explanation, stopReason: stopReason ?? void 0 });
|
|
2673
|
-
} else {
|
|
2674
|
-
resolve2({ setup: null, explanation, raw: preJsonBuffer, stopReason: stopReason ?? void 0 });
|
|
2675
|
-
}
|
|
2676
|
-
},
|
|
2677
|
-
onError: (error) => {
|
|
2678
|
-
if (isTransientError2(error) && attempt < MAX_RETRIES2) {
|
|
2679
|
-
if (callbacks) callbacks.onStatus("Connection interrupted, retrying...");
|
|
2680
|
-
setTimeout(() => attemptGeneration().then(resolve2), 2e3);
|
|
2681
|
-
return;
|
|
2682
|
-
}
|
|
2683
|
-
if (callbacks) callbacks.onError(error.message);
|
|
2684
|
-
resolve2({ setup: null, raw: error.message, stopReason: "error" });
|
|
2685
|
-
}
|
|
2686
|
-
}
|
|
2687
|
-
).catch((error) => {
|
|
2688
|
-
if (callbacks) callbacks.onError(error.message);
|
|
2689
|
-
resolve2({ setup: null, raw: error.message, stopReason: "error" });
|
|
2690
|
-
});
|
|
2691
|
-
});
|
|
2692
|
-
};
|
|
2693
|
-
return attemptGeneration();
|
|
2911
|
+
return streamGeneration({
|
|
2912
|
+
systemPrompt: GENERATION_SYSTEM_PROMPT,
|
|
2913
|
+
userMessage,
|
|
2914
|
+
baseMaxTokens: GENERATION_MAX_TOKENS,
|
|
2915
|
+
tokenIncrement: 16e3,
|
|
2916
|
+
maxTokensCap: MODEL_MAX_OUTPUT_TOKENS,
|
|
2917
|
+
callbacks
|
|
2918
|
+
});
|
|
2694
2919
|
}
|
|
2695
2920
|
async function generateSkillsForSetup(setup, fingerprint, targetAgent, onStatus) {
|
|
2696
2921
|
const skillTopics = collectSkillTopics(setup, targetAgent, fingerprint);
|
|
@@ -2723,7 +2948,6 @@ async function generateSkillsForSetup(setup, fingerprint, targetAgent, onStatus)
|
|
|
2723
2948
|
if (failed > 0) onStatus?.(`${succeeded} generated, ${failed} failed`);
|
|
2724
2949
|
return succeeded;
|
|
2725
2950
|
}
|
|
2726
|
-
var MAX_PROMPT_TOKENS = 12e4;
|
|
2727
2951
|
var LIMITS = {
|
|
2728
2952
|
FILE_TREE_ENTRIES: 500,
|
|
2729
2953
|
EXISTING_CONFIG_CHARS: 8e3,
|
|
@@ -2738,6 +2962,7 @@ function truncate(text, maxChars) {
|
|
|
2738
2962
|
}
|
|
2739
2963
|
function sampleFileTree(fileTree, codeAnalysisPaths, limit) {
|
|
2740
2964
|
if (fileTree.length <= limit) return fileTree;
|
|
2965
|
+
const fileTreeSet = new Set(fileTree);
|
|
2741
2966
|
const dirs = [];
|
|
2742
2967
|
const rootFiles = [];
|
|
2743
2968
|
const nestedFiles = [];
|
|
@@ -2763,7 +2988,7 @@ function sampleFileTree(fileTree, codeAnalysisPaths, limit) {
|
|
|
2763
2988
|
for (const f of rootFiles.slice(0, 50)) add(f);
|
|
2764
2989
|
for (const p of codeAnalysisPaths) {
|
|
2765
2990
|
if (result.length >= limit) break;
|
|
2766
|
-
if (
|
|
2991
|
+
if (fileTreeSet.has(p)) add(p);
|
|
2767
2992
|
}
|
|
2768
2993
|
for (const f of nestedFiles) {
|
|
2769
2994
|
if (result.length >= limit) break;
|
|
@@ -2887,20 +3112,25 @@ User instructions: ${prompt}`);
|
|
|
2887
3112
|
if (fingerprint.codeAnalysis) {
|
|
2888
3113
|
const ca = fingerprint.codeAnalysis;
|
|
2889
3114
|
const basePrompt = parts.join("\n");
|
|
3115
|
+
const maxPromptTokens = getMaxPromptTokens();
|
|
2890
3116
|
const baseTokens = estimateTokens(basePrompt);
|
|
2891
|
-
const tokenBudgetForCode = Math.max(0,
|
|
3117
|
+
const tokenBudgetForCode = Math.max(0, maxPromptTokens - baseTokens);
|
|
2892
3118
|
const codeLines = [];
|
|
2893
3119
|
let codeChars = 0;
|
|
2894
|
-
|
|
3120
|
+
const introLine = "Study these files to extract patterns for skills. Use the exact code patterns you see here.\n";
|
|
3121
|
+
codeLines.push(introLine);
|
|
3122
|
+
let runningCodeLen = introLine.length;
|
|
2895
3123
|
const sortedFiles = [...ca.files].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
2896
3124
|
let includedFiles = 0;
|
|
2897
3125
|
for (const f of sortedFiles) {
|
|
2898
3126
|
const entry = `[${f.path}]
|
|
2899
3127
|
${f.content}
|
|
2900
3128
|
`;
|
|
2901
|
-
|
|
3129
|
+
const projectedLen = runningCodeLen + 1 + entry.length;
|
|
3130
|
+
if (Math.ceil(projectedLen / 4) > tokenBudgetForCode && includedFiles > 0) break;
|
|
2902
3131
|
codeLines.push(entry);
|
|
2903
3132
|
codeChars += f.content.length;
|
|
3133
|
+
runningCodeLen = projectedLen;
|
|
2904
3134
|
includedFiles++;
|
|
2905
3135
|
}
|
|
2906
3136
|
const includedTokens = Math.ceil(codeChars / 4);
|
|
@@ -2971,20 +3201,20 @@ Return the complete updated AgentSetup JSON incorporating the user's changes. Re
|
|
|
2971
3201
|
}
|
|
2972
3202
|
|
|
2973
3203
|
// src/writers/index.ts
|
|
2974
|
-
import
|
|
3204
|
+
import fs13 from "fs";
|
|
2975
3205
|
|
|
2976
3206
|
// src/writers/claude/index.ts
|
|
2977
|
-
import
|
|
2978
|
-
import
|
|
3207
|
+
import fs8 from "fs";
|
|
3208
|
+
import path7 from "path";
|
|
2979
3209
|
function writeClaudeConfig(config) {
|
|
2980
3210
|
const written = [];
|
|
2981
|
-
|
|
3211
|
+
fs8.writeFileSync("CLAUDE.md", config.claudeMd);
|
|
2982
3212
|
written.push("CLAUDE.md");
|
|
2983
3213
|
if (config.skills?.length) {
|
|
2984
3214
|
for (const skill of config.skills) {
|
|
2985
|
-
const skillDir =
|
|
2986
|
-
if (!
|
|
2987
|
-
const skillPath =
|
|
3215
|
+
const skillDir = path7.join(".claude", "skills", skill.name);
|
|
3216
|
+
if (!fs8.existsSync(skillDir)) fs8.mkdirSync(skillDir, { recursive: true });
|
|
3217
|
+
const skillPath = path7.join(skillDir, "SKILL.md");
|
|
2988
3218
|
const frontmatter = [
|
|
2989
3219
|
"---",
|
|
2990
3220
|
`name: ${skill.name}`,
|
|
@@ -2992,49 +3222,49 @@ function writeClaudeConfig(config) {
|
|
|
2992
3222
|
"---",
|
|
2993
3223
|
""
|
|
2994
3224
|
].join("\n");
|
|
2995
|
-
|
|
3225
|
+
fs8.writeFileSync(skillPath, frontmatter + skill.content);
|
|
2996
3226
|
written.push(skillPath);
|
|
2997
3227
|
}
|
|
2998
3228
|
}
|
|
2999
3229
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
3000
3230
|
let existingServers = {};
|
|
3001
3231
|
try {
|
|
3002
|
-
if (
|
|
3003
|
-
const existing = JSON.parse(
|
|
3232
|
+
if (fs8.existsSync(".mcp.json")) {
|
|
3233
|
+
const existing = JSON.parse(fs8.readFileSync(".mcp.json", "utf-8"));
|
|
3004
3234
|
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
3005
3235
|
}
|
|
3006
3236
|
} catch {
|
|
3007
3237
|
}
|
|
3008
3238
|
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
3009
|
-
|
|
3239
|
+
fs8.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
3010
3240
|
written.push(".mcp.json");
|
|
3011
3241
|
}
|
|
3012
3242
|
return written;
|
|
3013
3243
|
}
|
|
3014
3244
|
|
|
3015
3245
|
// src/writers/cursor/index.ts
|
|
3016
|
-
import
|
|
3017
|
-
import
|
|
3246
|
+
import fs9 from "fs";
|
|
3247
|
+
import path8 from "path";
|
|
3018
3248
|
function writeCursorConfig(config) {
|
|
3019
3249
|
const written = [];
|
|
3020
3250
|
if (config.cursorrules) {
|
|
3021
|
-
|
|
3251
|
+
fs9.writeFileSync(".cursorrules", config.cursorrules);
|
|
3022
3252
|
written.push(".cursorrules");
|
|
3023
3253
|
}
|
|
3024
3254
|
if (config.rules?.length) {
|
|
3025
|
-
const rulesDir =
|
|
3026
|
-
if (!
|
|
3255
|
+
const rulesDir = path8.join(".cursor", "rules");
|
|
3256
|
+
if (!fs9.existsSync(rulesDir)) fs9.mkdirSync(rulesDir, { recursive: true });
|
|
3027
3257
|
for (const rule of config.rules) {
|
|
3028
|
-
const rulePath =
|
|
3029
|
-
|
|
3258
|
+
const rulePath = path8.join(rulesDir, rule.filename);
|
|
3259
|
+
fs9.writeFileSync(rulePath, rule.content);
|
|
3030
3260
|
written.push(rulePath);
|
|
3031
3261
|
}
|
|
3032
3262
|
}
|
|
3033
3263
|
if (config.skills?.length) {
|
|
3034
3264
|
for (const skill of config.skills) {
|
|
3035
|
-
const skillDir =
|
|
3036
|
-
if (!
|
|
3037
|
-
const skillPath =
|
|
3265
|
+
const skillDir = path8.join(".cursor", "skills", skill.name);
|
|
3266
|
+
if (!fs9.existsSync(skillDir)) fs9.mkdirSync(skillDir, { recursive: true });
|
|
3267
|
+
const skillPath = path8.join(skillDir, "SKILL.md");
|
|
3038
3268
|
const frontmatter = [
|
|
3039
3269
|
"---",
|
|
3040
3270
|
`name: ${skill.name}`,
|
|
@@ -3042,41 +3272,41 @@ function writeCursorConfig(config) {
|
|
|
3042
3272
|
"---",
|
|
3043
3273
|
""
|
|
3044
3274
|
].join("\n");
|
|
3045
|
-
|
|
3275
|
+
fs9.writeFileSync(skillPath, frontmatter + skill.content);
|
|
3046
3276
|
written.push(skillPath);
|
|
3047
3277
|
}
|
|
3048
3278
|
}
|
|
3049
3279
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
3050
3280
|
const cursorDir = ".cursor";
|
|
3051
|
-
if (!
|
|
3052
|
-
const mcpPath =
|
|
3281
|
+
if (!fs9.existsSync(cursorDir)) fs9.mkdirSync(cursorDir, { recursive: true });
|
|
3282
|
+
const mcpPath = path8.join(cursorDir, "mcp.json");
|
|
3053
3283
|
let existingServers = {};
|
|
3054
3284
|
try {
|
|
3055
|
-
if (
|
|
3056
|
-
const existing = JSON.parse(
|
|
3285
|
+
if (fs9.existsSync(mcpPath)) {
|
|
3286
|
+
const existing = JSON.parse(fs9.readFileSync(mcpPath, "utf-8"));
|
|
3057
3287
|
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
3058
3288
|
}
|
|
3059
3289
|
} catch {
|
|
3060
3290
|
}
|
|
3061
3291
|
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
3062
|
-
|
|
3292
|
+
fs9.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
3063
3293
|
written.push(mcpPath);
|
|
3064
3294
|
}
|
|
3065
3295
|
return written;
|
|
3066
3296
|
}
|
|
3067
3297
|
|
|
3068
3298
|
// src/writers/codex/index.ts
|
|
3069
|
-
import
|
|
3070
|
-
import
|
|
3299
|
+
import fs10 from "fs";
|
|
3300
|
+
import path9 from "path";
|
|
3071
3301
|
function writeCodexConfig(config) {
|
|
3072
3302
|
const written = [];
|
|
3073
|
-
|
|
3303
|
+
fs10.writeFileSync("AGENTS.md", config.agentsMd);
|
|
3074
3304
|
written.push("AGENTS.md");
|
|
3075
3305
|
if (config.skills?.length) {
|
|
3076
3306
|
for (const skill of config.skills) {
|
|
3077
|
-
const skillDir =
|
|
3078
|
-
if (!
|
|
3079
|
-
const skillPath =
|
|
3307
|
+
const skillDir = path9.join(".agents", "skills", skill.name);
|
|
3308
|
+
if (!fs10.existsSync(skillDir)) fs10.mkdirSync(skillDir, { recursive: true });
|
|
3309
|
+
const skillPath = path9.join(skillDir, "SKILL.md");
|
|
3080
3310
|
const frontmatter = [
|
|
3081
3311
|
"---",
|
|
3082
3312
|
`name: ${skill.name}`,
|
|
@@ -3084,7 +3314,7 @@ function writeCodexConfig(config) {
|
|
|
3084
3314
|
"---",
|
|
3085
3315
|
""
|
|
3086
3316
|
].join("\n");
|
|
3087
|
-
|
|
3317
|
+
fs10.writeFileSync(skillPath, frontmatter + skill.content);
|
|
3088
3318
|
written.push(skillPath);
|
|
3089
3319
|
}
|
|
3090
3320
|
}
|
|
@@ -3093,62 +3323,62 @@ function writeCodexConfig(config) {
|
|
|
3093
3323
|
|
|
3094
3324
|
// src/writers/backup.ts
|
|
3095
3325
|
init_constants();
|
|
3096
|
-
import
|
|
3097
|
-
import
|
|
3326
|
+
import fs11 from "fs";
|
|
3327
|
+
import path11 from "path";
|
|
3098
3328
|
function createBackup(files) {
|
|
3099
3329
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3100
|
-
const backupDir =
|
|
3330
|
+
const backupDir = path11.join(BACKUPS_DIR, timestamp);
|
|
3101
3331
|
for (const file of files) {
|
|
3102
|
-
if (!
|
|
3103
|
-
const dest =
|
|
3104
|
-
const destDir =
|
|
3105
|
-
if (!
|
|
3106
|
-
|
|
3332
|
+
if (!fs11.existsSync(file)) continue;
|
|
3333
|
+
const dest = path11.join(backupDir, file);
|
|
3334
|
+
const destDir = path11.dirname(dest);
|
|
3335
|
+
if (!fs11.existsSync(destDir)) {
|
|
3336
|
+
fs11.mkdirSync(destDir, { recursive: true });
|
|
3107
3337
|
}
|
|
3108
|
-
|
|
3338
|
+
fs11.copyFileSync(file, dest);
|
|
3109
3339
|
}
|
|
3110
3340
|
return backupDir;
|
|
3111
3341
|
}
|
|
3112
3342
|
function restoreBackup(backupDir, file) {
|
|
3113
|
-
const backupFile =
|
|
3114
|
-
if (!
|
|
3115
|
-
const destDir =
|
|
3116
|
-
if (!
|
|
3117
|
-
|
|
3343
|
+
const backupFile = path11.join(backupDir, file);
|
|
3344
|
+
if (!fs11.existsSync(backupFile)) return false;
|
|
3345
|
+
const destDir = path11.dirname(file);
|
|
3346
|
+
if (!fs11.existsSync(destDir)) {
|
|
3347
|
+
fs11.mkdirSync(destDir, { recursive: true });
|
|
3118
3348
|
}
|
|
3119
|
-
|
|
3349
|
+
fs11.copyFileSync(backupFile, file);
|
|
3120
3350
|
return true;
|
|
3121
3351
|
}
|
|
3122
3352
|
|
|
3123
3353
|
// src/writers/manifest.ts
|
|
3124
3354
|
init_constants();
|
|
3125
|
-
import
|
|
3126
|
-
import
|
|
3355
|
+
import fs12 from "fs";
|
|
3356
|
+
import crypto2 from "crypto";
|
|
3127
3357
|
function readManifest() {
|
|
3128
3358
|
try {
|
|
3129
|
-
if (!
|
|
3130
|
-
return JSON.parse(
|
|
3359
|
+
if (!fs12.existsSync(MANIFEST_FILE)) return null;
|
|
3360
|
+
return JSON.parse(fs12.readFileSync(MANIFEST_FILE, "utf-8"));
|
|
3131
3361
|
} catch {
|
|
3132
3362
|
return null;
|
|
3133
3363
|
}
|
|
3134
3364
|
}
|
|
3135
3365
|
function writeManifest(manifest) {
|
|
3136
|
-
if (!
|
|
3137
|
-
|
|
3366
|
+
if (!fs12.existsSync(CALIBER_DIR)) {
|
|
3367
|
+
fs12.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
3138
3368
|
}
|
|
3139
|
-
|
|
3369
|
+
fs12.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
|
|
3140
3370
|
}
|
|
3141
3371
|
function fileChecksum(filePath) {
|
|
3142
|
-
const content =
|
|
3143
|
-
return
|
|
3372
|
+
const content = fs12.readFileSync(filePath);
|
|
3373
|
+
return crypto2.createHash("sha256").update(content).digest("hex");
|
|
3144
3374
|
}
|
|
3145
3375
|
|
|
3146
3376
|
// src/writers/index.ts
|
|
3147
3377
|
function writeSetup(setup) {
|
|
3148
3378
|
const filesToWrite = getFilesToWrite(setup);
|
|
3149
|
-
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) =>
|
|
3379
|
+
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs13.existsSync(f));
|
|
3150
3380
|
const existingFiles = [
|
|
3151
|
-
...filesToWrite.filter((f) =>
|
|
3381
|
+
...filesToWrite.filter((f) => fs13.existsSync(f)),
|
|
3152
3382
|
...filesToDelete
|
|
3153
3383
|
];
|
|
3154
3384
|
const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
|
|
@@ -3164,7 +3394,7 @@ function writeSetup(setup) {
|
|
|
3164
3394
|
}
|
|
3165
3395
|
const deleted = [];
|
|
3166
3396
|
for (const filePath of filesToDelete) {
|
|
3167
|
-
|
|
3397
|
+
fs13.unlinkSync(filePath);
|
|
3168
3398
|
deleted.push(filePath);
|
|
3169
3399
|
}
|
|
3170
3400
|
ensureGitignore();
|
|
@@ -3194,8 +3424,8 @@ function undoSetup() {
|
|
|
3194
3424
|
const removed = [];
|
|
3195
3425
|
for (const entry of manifest.entries) {
|
|
3196
3426
|
if (entry.action === "created") {
|
|
3197
|
-
if (
|
|
3198
|
-
|
|
3427
|
+
if (fs13.existsSync(entry.path)) {
|
|
3428
|
+
fs13.unlinkSync(entry.path);
|
|
3199
3429
|
removed.push(entry.path);
|
|
3200
3430
|
}
|
|
3201
3431
|
} else if ((entry.action === "modified" || entry.action === "deleted") && manifest.backupDir) {
|
|
@@ -3205,8 +3435,8 @@ function undoSetup() {
|
|
|
3205
3435
|
}
|
|
3206
3436
|
}
|
|
3207
3437
|
const { MANIFEST_FILE: MANIFEST_FILE2 } = (init_constants(), __toCommonJS(constants_exports));
|
|
3208
|
-
if (
|
|
3209
|
-
|
|
3438
|
+
if (fs13.existsSync(MANIFEST_FILE2)) {
|
|
3439
|
+
fs13.unlinkSync(MANIFEST_FILE2);
|
|
3210
3440
|
}
|
|
3211
3441
|
return { restored, removed };
|
|
3212
3442
|
}
|
|
@@ -3241,23 +3471,23 @@ function getFilesToWrite(setup) {
|
|
|
3241
3471
|
}
|
|
3242
3472
|
function ensureGitignore() {
|
|
3243
3473
|
const gitignorePath = ".gitignore";
|
|
3244
|
-
if (
|
|
3245
|
-
const content =
|
|
3474
|
+
if (fs13.existsSync(gitignorePath)) {
|
|
3475
|
+
const content = fs13.readFileSync(gitignorePath, "utf-8");
|
|
3246
3476
|
if (!content.includes(".caliber/")) {
|
|
3247
|
-
|
|
3477
|
+
fs13.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
|
|
3248
3478
|
}
|
|
3249
3479
|
} else {
|
|
3250
|
-
|
|
3480
|
+
fs13.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
|
|
3251
3481
|
}
|
|
3252
3482
|
}
|
|
3253
3483
|
|
|
3254
3484
|
// src/writers/staging.ts
|
|
3255
3485
|
init_constants();
|
|
3256
|
-
import
|
|
3257
|
-
import
|
|
3258
|
-
var STAGED_DIR =
|
|
3259
|
-
var PROPOSED_DIR =
|
|
3260
|
-
var CURRENT_DIR =
|
|
3486
|
+
import fs14 from "fs";
|
|
3487
|
+
import path12 from "path";
|
|
3488
|
+
var STAGED_DIR = path12.join(CALIBER_DIR, "staged");
|
|
3489
|
+
var PROPOSED_DIR = path12.join(STAGED_DIR, "proposed");
|
|
3490
|
+
var CURRENT_DIR = path12.join(STAGED_DIR, "current");
|
|
3261
3491
|
function normalizeContent(content) {
|
|
3262
3492
|
return content.split("\n").map((line) => line.trimEnd()).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
3263
3493
|
}
|
|
@@ -3267,20 +3497,20 @@ function stageFiles(files, projectDir) {
|
|
|
3267
3497
|
let modifiedFiles = 0;
|
|
3268
3498
|
const stagedFiles = [];
|
|
3269
3499
|
for (const file of files) {
|
|
3270
|
-
const originalPath =
|
|
3271
|
-
if (
|
|
3272
|
-
const existing =
|
|
3500
|
+
const originalPath = path12.join(projectDir, file.path);
|
|
3501
|
+
if (fs14.existsSync(originalPath)) {
|
|
3502
|
+
const existing = fs14.readFileSync(originalPath, "utf-8");
|
|
3273
3503
|
if (normalizeContent(existing) === normalizeContent(file.content)) {
|
|
3274
3504
|
continue;
|
|
3275
3505
|
}
|
|
3276
3506
|
}
|
|
3277
|
-
const proposedPath =
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
if (
|
|
3281
|
-
const currentPath =
|
|
3282
|
-
|
|
3283
|
-
|
|
3507
|
+
const proposedPath = path12.join(PROPOSED_DIR, file.path);
|
|
3508
|
+
fs14.mkdirSync(path12.dirname(proposedPath), { recursive: true });
|
|
3509
|
+
fs14.writeFileSync(proposedPath, file.content);
|
|
3510
|
+
if (fs14.existsSync(originalPath)) {
|
|
3511
|
+
const currentPath = path12.join(CURRENT_DIR, file.path);
|
|
3512
|
+
fs14.mkdirSync(path12.dirname(currentPath), { recursive: true });
|
|
3513
|
+
fs14.copyFileSync(originalPath, currentPath);
|
|
3284
3514
|
modifiedFiles++;
|
|
3285
3515
|
stagedFiles.push({ relativePath: file.path, proposedPath, currentPath, originalPath, isNew: false });
|
|
3286
3516
|
} else {
|
|
@@ -3291,34 +3521,34 @@ function stageFiles(files, projectDir) {
|
|
|
3291
3521
|
return { newFiles, modifiedFiles, stagedFiles };
|
|
3292
3522
|
}
|
|
3293
3523
|
function cleanupStaging() {
|
|
3294
|
-
if (
|
|
3295
|
-
|
|
3524
|
+
if (fs14.existsSync(STAGED_DIR)) {
|
|
3525
|
+
fs14.rmSync(STAGED_DIR, { recursive: true, force: true });
|
|
3296
3526
|
}
|
|
3297
3527
|
}
|
|
3298
3528
|
|
|
3299
3529
|
// src/utils/review.ts
|
|
3300
3530
|
import chalk2 from "chalk";
|
|
3301
|
-
import
|
|
3531
|
+
import fs16 from "fs";
|
|
3302
3532
|
import select2 from "@inquirer/select";
|
|
3303
3533
|
import { createTwoFilesPatch } from "diff";
|
|
3304
3534
|
|
|
3305
3535
|
// src/utils/editor.ts
|
|
3306
|
-
import { execSync as
|
|
3307
|
-
import
|
|
3308
|
-
import
|
|
3536
|
+
import { execSync as execSync6, spawn as spawn3 } from "child_process";
|
|
3537
|
+
import fs15 from "fs";
|
|
3538
|
+
import path13 from "path";
|
|
3309
3539
|
import os4 from "os";
|
|
3310
3540
|
var IS_WINDOWS3 = process.platform === "win32";
|
|
3311
|
-
var DIFF_TEMP_DIR =
|
|
3541
|
+
var DIFF_TEMP_DIR = path13.join(os4.tmpdir(), "caliber-diff");
|
|
3312
3542
|
function getEmptyFilePath(proposedPath) {
|
|
3313
|
-
|
|
3314
|
-
const tempPath =
|
|
3315
|
-
|
|
3543
|
+
fs15.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
|
|
3544
|
+
const tempPath = path13.join(DIFF_TEMP_DIR, path13.basename(proposedPath));
|
|
3545
|
+
fs15.writeFileSync(tempPath, "");
|
|
3316
3546
|
return tempPath;
|
|
3317
3547
|
}
|
|
3318
3548
|
function commandExists(cmd) {
|
|
3319
3549
|
try {
|
|
3320
3550
|
const check = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
|
|
3321
|
-
|
|
3551
|
+
execSync6(check, { stdio: "ignore" });
|
|
3322
3552
|
return true;
|
|
3323
3553
|
} catch {
|
|
3324
3554
|
return false;
|
|
@@ -3383,8 +3613,8 @@ async function openReview(method, stagedFiles) {
|
|
|
3383
3613
|
return;
|
|
3384
3614
|
}
|
|
3385
3615
|
const fileInfos = stagedFiles.map((file) => {
|
|
3386
|
-
const proposed =
|
|
3387
|
-
const current = file.currentPath ?
|
|
3616
|
+
const proposed = fs16.readFileSync(file.proposedPath, "utf-8");
|
|
3617
|
+
const current = file.currentPath ? fs16.readFileSync(file.currentPath, "utf-8") : "";
|
|
3388
3618
|
const patch = createTwoFilesPatch(
|
|
3389
3619
|
file.isNew ? "/dev/null" : file.relativePath,
|
|
3390
3620
|
file.relativePath,
|
|
@@ -3547,7 +3777,7 @@ async function interactiveDiffExplorer(files) {
|
|
|
3547
3777
|
}
|
|
3548
3778
|
|
|
3549
3779
|
// src/commands/setup-files.ts
|
|
3550
|
-
import
|
|
3780
|
+
import fs17 from "fs";
|
|
3551
3781
|
function buildSkillContent(skill) {
|
|
3552
3782
|
const frontmatter = `---
|
|
3553
3783
|
name: ${skill.name}
|
|
@@ -3596,7 +3826,7 @@ function collectSetupFiles(setup, targetAgent) {
|
|
|
3596
3826
|
}
|
|
3597
3827
|
}
|
|
3598
3828
|
const codexTargeted = targetAgent ? targetAgent.includes("codex") : false;
|
|
3599
|
-
if (codexTargeted && !
|
|
3829
|
+
if (codexTargeted && !fs17.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
|
|
3600
3830
|
const agentRefs = [];
|
|
3601
3831
|
if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
|
|
3602
3832
|
if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
|
|
@@ -3613,13 +3843,13 @@ ${agentRefs.join(" ")}
|
|
|
3613
3843
|
}
|
|
3614
3844
|
|
|
3615
3845
|
// src/lib/hooks.ts
|
|
3616
|
-
import
|
|
3617
|
-
import
|
|
3618
|
-
import { execSync as
|
|
3846
|
+
import fs19 from "fs";
|
|
3847
|
+
import path14 from "path";
|
|
3848
|
+
import { execSync as execSync8 } from "child_process";
|
|
3619
3849
|
|
|
3620
3850
|
// src/lib/resolve-caliber.ts
|
|
3621
|
-
import
|
|
3622
|
-
import { execSync as
|
|
3851
|
+
import fs18 from "fs";
|
|
3852
|
+
import { execSync as execSync7 } from "child_process";
|
|
3623
3853
|
var _resolved = null;
|
|
3624
3854
|
function resolveCaliber() {
|
|
3625
3855
|
if (_resolved) return _resolved;
|
|
@@ -3630,7 +3860,7 @@ function resolveCaliber() {
|
|
|
3630
3860
|
}
|
|
3631
3861
|
try {
|
|
3632
3862
|
const whichCmd = process.platform === "win32" ? "where caliber" : "which caliber";
|
|
3633
|
-
const found =
|
|
3863
|
+
const found = execSync7(whichCmd, {
|
|
3634
3864
|
encoding: "utf-8",
|
|
3635
3865
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3636
3866
|
}).trim();
|
|
@@ -3641,7 +3871,7 @@ function resolveCaliber() {
|
|
|
3641
3871
|
} catch {
|
|
3642
3872
|
}
|
|
3643
3873
|
const binPath = process.argv[1];
|
|
3644
|
-
if (binPath &&
|
|
3874
|
+
if (binPath && fs18.existsSync(binPath)) {
|
|
3645
3875
|
_resolved = binPath;
|
|
3646
3876
|
return _resolved;
|
|
3647
3877
|
}
|
|
@@ -3657,24 +3887,24 @@ function isCaliberCommand(command, subcommandTail) {
|
|
|
3657
3887
|
}
|
|
3658
3888
|
|
|
3659
3889
|
// src/lib/hooks.ts
|
|
3660
|
-
var SETTINGS_PATH =
|
|
3890
|
+
var SETTINGS_PATH = path14.join(".claude", "settings.json");
|
|
3661
3891
|
var REFRESH_TAIL = "refresh --quiet";
|
|
3662
3892
|
var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
|
|
3663
3893
|
function getHookCommand() {
|
|
3664
3894
|
return `${resolveCaliber()} ${REFRESH_TAIL}`;
|
|
3665
3895
|
}
|
|
3666
3896
|
function readSettings() {
|
|
3667
|
-
if (!
|
|
3897
|
+
if (!fs19.existsSync(SETTINGS_PATH)) return {};
|
|
3668
3898
|
try {
|
|
3669
|
-
return JSON.parse(
|
|
3899
|
+
return JSON.parse(fs19.readFileSync(SETTINGS_PATH, "utf-8"));
|
|
3670
3900
|
} catch {
|
|
3671
3901
|
return {};
|
|
3672
3902
|
}
|
|
3673
3903
|
}
|
|
3674
3904
|
function writeSettings(settings) {
|
|
3675
|
-
const dir =
|
|
3676
|
-
if (!
|
|
3677
|
-
|
|
3905
|
+
const dir = path14.dirname(SETTINGS_PATH);
|
|
3906
|
+
if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
|
|
3907
|
+
fs19.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
3678
3908
|
}
|
|
3679
3909
|
function findHookIndex(sessionEnd) {
|
|
3680
3910
|
return sessionEnd.findIndex(
|
|
@@ -3736,20 +3966,20 @@ ${PRECOMMIT_END}`;
|
|
|
3736
3966
|
}
|
|
3737
3967
|
function getGitHooksDir() {
|
|
3738
3968
|
try {
|
|
3739
|
-
const gitDir =
|
|
3740
|
-
return
|
|
3969
|
+
const gitDir = execSync8("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
3970
|
+
return path14.join(gitDir, "hooks");
|
|
3741
3971
|
} catch {
|
|
3742
3972
|
return null;
|
|
3743
3973
|
}
|
|
3744
3974
|
}
|
|
3745
3975
|
function getPreCommitPath() {
|
|
3746
3976
|
const hooksDir = getGitHooksDir();
|
|
3747
|
-
return hooksDir ?
|
|
3977
|
+
return hooksDir ? path14.join(hooksDir, "pre-commit") : null;
|
|
3748
3978
|
}
|
|
3749
3979
|
function isPreCommitHookInstalled() {
|
|
3750
3980
|
const hookPath = getPreCommitPath();
|
|
3751
|
-
if (!hookPath || !
|
|
3752
|
-
const content =
|
|
3981
|
+
if (!hookPath || !fs19.existsSync(hookPath)) return false;
|
|
3982
|
+
const content = fs19.readFileSync(hookPath, "utf-8");
|
|
3753
3983
|
return content.includes(PRECOMMIT_START);
|
|
3754
3984
|
}
|
|
3755
3985
|
function installPreCommitHook() {
|
|
@@ -3758,43 +3988,43 @@ function installPreCommitHook() {
|
|
|
3758
3988
|
}
|
|
3759
3989
|
const hookPath = getPreCommitPath();
|
|
3760
3990
|
if (!hookPath) return { installed: false, alreadyInstalled: false };
|
|
3761
|
-
const hooksDir =
|
|
3762
|
-
if (!
|
|
3991
|
+
const hooksDir = path14.dirname(hookPath);
|
|
3992
|
+
if (!fs19.existsSync(hooksDir)) fs19.mkdirSync(hooksDir, { recursive: true });
|
|
3763
3993
|
let content = "";
|
|
3764
|
-
if (
|
|
3765
|
-
content =
|
|
3994
|
+
if (fs19.existsSync(hookPath)) {
|
|
3995
|
+
content = fs19.readFileSync(hookPath, "utf-8");
|
|
3766
3996
|
if (!content.endsWith("\n")) content += "\n";
|
|
3767
3997
|
content += "\n" + getPrecommitBlock() + "\n";
|
|
3768
3998
|
} else {
|
|
3769
3999
|
content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
|
|
3770
4000
|
}
|
|
3771
|
-
|
|
3772
|
-
|
|
4001
|
+
fs19.writeFileSync(hookPath, content);
|
|
4002
|
+
fs19.chmodSync(hookPath, 493);
|
|
3773
4003
|
return { installed: true, alreadyInstalled: false };
|
|
3774
4004
|
}
|
|
3775
4005
|
function removePreCommitHook() {
|
|
3776
4006
|
const hookPath = getPreCommitPath();
|
|
3777
|
-
if (!hookPath || !
|
|
4007
|
+
if (!hookPath || !fs19.existsSync(hookPath)) {
|
|
3778
4008
|
return { removed: false, notFound: true };
|
|
3779
4009
|
}
|
|
3780
|
-
let content =
|
|
4010
|
+
let content = fs19.readFileSync(hookPath, "utf-8");
|
|
3781
4011
|
if (!content.includes(PRECOMMIT_START)) {
|
|
3782
4012
|
return { removed: false, notFound: true };
|
|
3783
4013
|
}
|
|
3784
4014
|
const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
|
|
3785
4015
|
content = content.replace(regex, "\n");
|
|
3786
4016
|
if (content.trim() === "#!/bin/sh" || content.trim() === "") {
|
|
3787
|
-
|
|
4017
|
+
fs19.unlinkSync(hookPath);
|
|
3788
4018
|
} else {
|
|
3789
|
-
|
|
4019
|
+
fs19.writeFileSync(hookPath, content);
|
|
3790
4020
|
}
|
|
3791
4021
|
return { removed: true, notFound: false };
|
|
3792
4022
|
}
|
|
3793
4023
|
|
|
3794
4024
|
// src/lib/learning-hooks.ts
|
|
3795
|
-
import
|
|
3796
|
-
import
|
|
3797
|
-
var SETTINGS_PATH2 =
|
|
4025
|
+
import fs20 from "fs";
|
|
4026
|
+
import path15 from "path";
|
|
4027
|
+
var SETTINGS_PATH2 = path15.join(".claude", "settings.json");
|
|
3798
4028
|
var HOOK_TAILS = [
|
|
3799
4029
|
{ event: "PostToolUse", tail: "learn observe", description: "Caliber: recording tool usage for session learning" },
|
|
3800
4030
|
{ event: "PostToolUseFailure", tail: "learn observe --failure", description: "Caliber: recording tool failure for session learning" },
|
|
@@ -3811,17 +4041,17 @@ function getHookConfigs() {
|
|
|
3811
4041
|
}));
|
|
3812
4042
|
}
|
|
3813
4043
|
function readSettings2() {
|
|
3814
|
-
if (!
|
|
4044
|
+
if (!fs20.existsSync(SETTINGS_PATH2)) return {};
|
|
3815
4045
|
try {
|
|
3816
|
-
return JSON.parse(
|
|
4046
|
+
return JSON.parse(fs20.readFileSync(SETTINGS_PATH2, "utf-8"));
|
|
3817
4047
|
} catch {
|
|
3818
4048
|
return {};
|
|
3819
4049
|
}
|
|
3820
4050
|
}
|
|
3821
4051
|
function writeSettings2(settings) {
|
|
3822
|
-
const dir =
|
|
3823
|
-
if (!
|
|
3824
|
-
|
|
4052
|
+
const dir = path15.dirname(SETTINGS_PATH2);
|
|
4053
|
+
if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
|
|
4054
|
+
fs20.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
|
|
3825
4055
|
}
|
|
3826
4056
|
function hasLearningHook(matchers, tail) {
|
|
3827
4057
|
return matchers.some((entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, tail)));
|
|
@@ -3855,7 +4085,7 @@ function installLearningHooks() {
|
|
|
3855
4085
|
writeSettings2(settings);
|
|
3856
4086
|
return { installed: true, alreadyInstalled: false };
|
|
3857
4087
|
}
|
|
3858
|
-
var CURSOR_HOOKS_PATH =
|
|
4088
|
+
var CURSOR_HOOKS_PATH = path15.join(".cursor", "hooks.json");
|
|
3859
4089
|
var CURSOR_HOOK_EVENTS = [
|
|
3860
4090
|
{ event: "postToolUse", tail: "learn observe" },
|
|
3861
4091
|
{ event: "postToolUseFailure", tail: "learn observe --failure" },
|
|
@@ -3863,17 +4093,17 @@ var CURSOR_HOOK_EVENTS = [
|
|
|
3863
4093
|
{ event: "sessionEnd", tail: "learn finalize" }
|
|
3864
4094
|
];
|
|
3865
4095
|
function readCursorHooks() {
|
|
3866
|
-
if (!
|
|
4096
|
+
if (!fs20.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
|
|
3867
4097
|
try {
|
|
3868
|
-
return JSON.parse(
|
|
4098
|
+
return JSON.parse(fs20.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
|
|
3869
4099
|
} catch {
|
|
3870
4100
|
return { version: 1, hooks: {} };
|
|
3871
4101
|
}
|
|
3872
4102
|
}
|
|
3873
4103
|
function writeCursorHooks(config) {
|
|
3874
|
-
const dir =
|
|
3875
|
-
if (!
|
|
3876
|
-
|
|
4104
|
+
const dir = path15.dirname(CURSOR_HOOKS_PATH);
|
|
4105
|
+
if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
|
|
4106
|
+
fs20.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
|
|
3877
4107
|
}
|
|
3878
4108
|
function hasCursorHook(entries, tail) {
|
|
3879
4109
|
return entries.some((e) => isCaliberCommand(e.command, tail));
|
|
@@ -3943,10 +4173,10 @@ function removeLearningHooks() {
|
|
|
3943
4173
|
|
|
3944
4174
|
// src/lib/state.ts
|
|
3945
4175
|
init_constants();
|
|
3946
|
-
import
|
|
3947
|
-
import
|
|
3948
|
-
import { execSync as
|
|
3949
|
-
var STATE_FILE =
|
|
4176
|
+
import fs21 from "fs";
|
|
4177
|
+
import path16 from "path";
|
|
4178
|
+
import { execSync as execSync9 } from "child_process";
|
|
4179
|
+
var STATE_FILE = path16.join(CALIBER_DIR, ".caliber-state.json");
|
|
3950
4180
|
function normalizeTargetAgent(value) {
|
|
3951
4181
|
if (Array.isArray(value)) return value;
|
|
3952
4182
|
if (typeof value === "string") {
|
|
@@ -3957,8 +4187,8 @@ function normalizeTargetAgent(value) {
|
|
|
3957
4187
|
}
|
|
3958
4188
|
function readState() {
|
|
3959
4189
|
try {
|
|
3960
|
-
if (!
|
|
3961
|
-
const raw = JSON.parse(
|
|
4190
|
+
if (!fs21.existsSync(STATE_FILE)) return null;
|
|
4191
|
+
const raw = JSON.parse(fs21.readFileSync(STATE_FILE, "utf-8"));
|
|
3962
4192
|
if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
|
|
3963
4193
|
return raw;
|
|
3964
4194
|
} catch {
|
|
@@ -3966,14 +4196,14 @@ function readState() {
|
|
|
3966
4196
|
}
|
|
3967
4197
|
}
|
|
3968
4198
|
function writeState(state) {
|
|
3969
|
-
if (!
|
|
3970
|
-
|
|
4199
|
+
if (!fs21.existsSync(CALIBER_DIR)) {
|
|
4200
|
+
fs21.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
3971
4201
|
}
|
|
3972
|
-
|
|
4202
|
+
fs21.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
|
|
3973
4203
|
}
|
|
3974
4204
|
function getCurrentHeadSha() {
|
|
3975
4205
|
try {
|
|
3976
|
-
return
|
|
4206
|
+
return execSync9("git rev-parse HEAD", {
|
|
3977
4207
|
encoding: "utf-8",
|
|
3978
4208
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3979
4209
|
}).trim();
|
|
@@ -4126,8 +4356,11 @@ async function runInteractiveProviderSetup(options) {
|
|
|
4126
4356
|
console.log(chalk5.dim(" Then run ") + chalk5.hex("#83D1EB")("agent login") + chalk5.dim(" to authenticate.\n"));
|
|
4127
4357
|
const proceed = await confirm({ message: "Continue anyway?" });
|
|
4128
4358
|
if (!proceed) throw new Error("__exit__");
|
|
4129
|
-
} else {
|
|
4130
|
-
console.log(chalk5.
|
|
4359
|
+
} else if (!isCursorLoggedIn()) {
|
|
4360
|
+
console.log(chalk5.yellow("\n Cursor Agent CLI found but not logged in."));
|
|
4361
|
+
console.log(chalk5.dim(" Run ") + chalk5.hex("#83D1EB")("agent login") + chalk5.dim(" to authenticate.\n"));
|
|
4362
|
+
const proceed = await confirm({ message: "Continue anyway?" });
|
|
4363
|
+
if (!proceed) throw new Error("__exit__");
|
|
4131
4364
|
}
|
|
4132
4365
|
break;
|
|
4133
4366
|
}
|
|
@@ -4918,7 +5151,7 @@ function checkGrounding(dir) {
|
|
|
4918
5151
|
|
|
4919
5152
|
// src/scoring/checks/accuracy.ts
|
|
4920
5153
|
import { existsSync as existsSync4, statSync as statSync2 } from "fs";
|
|
4921
|
-
import { execSync as
|
|
5154
|
+
import { execSync as execSync10 } from "child_process";
|
|
4922
5155
|
import { join as join5 } from "path";
|
|
4923
5156
|
function validateReferences(dir) {
|
|
4924
5157
|
const configContent = collectPrimaryConfigContent(dir);
|
|
@@ -4927,13 +5160,13 @@ function validateReferences(dir) {
|
|
|
4927
5160
|
}
|
|
4928
5161
|
function detectGitDrift(dir) {
|
|
4929
5162
|
try {
|
|
4930
|
-
|
|
5163
|
+
execSync10("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] });
|
|
4931
5164
|
} catch {
|
|
4932
5165
|
return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: false };
|
|
4933
5166
|
}
|
|
4934
5167
|
const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules", ".cursor/rules"];
|
|
4935
5168
|
try {
|
|
4936
|
-
const headTimestamp =
|
|
5169
|
+
const headTimestamp = execSync10(
|
|
4937
5170
|
"git log -1 --format=%ct HEAD",
|
|
4938
5171
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
4939
5172
|
).trim();
|
|
@@ -4954,7 +5187,7 @@ function detectGitDrift(dir) {
|
|
|
4954
5187
|
let latestConfigCommitHash = null;
|
|
4955
5188
|
for (const file of configFiles) {
|
|
4956
5189
|
try {
|
|
4957
|
-
const hash =
|
|
5190
|
+
const hash = execSync10(
|
|
4958
5191
|
`git log -1 --format=%H -- "${file}"`,
|
|
4959
5192
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
4960
5193
|
).trim();
|
|
@@ -4963,7 +5196,7 @@ function detectGitDrift(dir) {
|
|
|
4963
5196
|
latestConfigCommitHash = hash;
|
|
4964
5197
|
} else {
|
|
4965
5198
|
try {
|
|
4966
|
-
|
|
5199
|
+
execSync10(
|
|
4967
5200
|
`git merge-base --is-ancestor ${latestConfigCommitHash} ${hash}`,
|
|
4968
5201
|
{ cwd: dir, stdio: ["pipe", "pipe", "pipe"] }
|
|
4969
5202
|
);
|
|
@@ -4978,12 +5211,12 @@ function detectGitDrift(dir) {
|
|
|
4978
5211
|
return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: true };
|
|
4979
5212
|
}
|
|
4980
5213
|
try {
|
|
4981
|
-
const countStr =
|
|
5214
|
+
const countStr = execSync10(
|
|
4982
5215
|
`git rev-list --count ${latestConfigCommitHash}..HEAD`,
|
|
4983
5216
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
4984
5217
|
).trim();
|
|
4985
5218
|
const commitsSince = parseInt(countStr, 10) || 0;
|
|
4986
|
-
const lastDate =
|
|
5219
|
+
const lastDate = execSync10(
|
|
4987
5220
|
`git log -1 --format=%ci ${latestConfigCommitHash}`,
|
|
4988
5221
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
4989
5222
|
).trim();
|
|
@@ -5055,12 +5288,12 @@ function checkAccuracy(dir) {
|
|
|
5055
5288
|
|
|
5056
5289
|
// src/scoring/checks/freshness.ts
|
|
5057
5290
|
import { existsSync as existsSync5, statSync as statSync3 } from "fs";
|
|
5058
|
-
import { execSync as
|
|
5291
|
+
import { execSync as execSync11 } from "child_process";
|
|
5059
5292
|
import { join as join6 } from "path";
|
|
5060
5293
|
function getCommitsSinceConfigUpdate(dir) {
|
|
5061
5294
|
const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules"];
|
|
5062
5295
|
try {
|
|
5063
|
-
const headTimestamp =
|
|
5296
|
+
const headTimestamp = execSync11(
|
|
5064
5297
|
"git log -1 --format=%ct HEAD",
|
|
5065
5298
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
5066
5299
|
).trim();
|
|
@@ -5080,12 +5313,12 @@ function getCommitsSinceConfigUpdate(dir) {
|
|
|
5080
5313
|
}
|
|
5081
5314
|
for (const file of configFiles) {
|
|
5082
5315
|
try {
|
|
5083
|
-
const hash =
|
|
5316
|
+
const hash = execSync11(
|
|
5084
5317
|
`git log -1 --format=%H -- "${file}"`,
|
|
5085
5318
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
5086
5319
|
).trim();
|
|
5087
5320
|
if (hash) {
|
|
5088
|
-
const countStr =
|
|
5321
|
+
const countStr = execSync11(
|
|
5089
5322
|
`git rev-list --count ${hash}..HEAD`,
|
|
5090
5323
|
{ cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
5091
5324
|
).trim();
|
|
@@ -5203,11 +5436,11 @@ function checkFreshness(dir) {
|
|
|
5203
5436
|
|
|
5204
5437
|
// src/scoring/checks/bonus.ts
|
|
5205
5438
|
import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
|
|
5206
|
-
import { execSync as
|
|
5439
|
+
import { execSync as execSync12 } from "child_process";
|
|
5207
5440
|
import { join as join7 } from "path";
|
|
5208
5441
|
function hasPreCommitHook(dir) {
|
|
5209
5442
|
try {
|
|
5210
|
-
const gitDir =
|
|
5443
|
+
const gitDir = execSync12("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
5211
5444
|
const hookPath = join7(gitDir, "hooks", "pre-commit");
|
|
5212
5445
|
const content = readFileOrNull2(hookPath);
|
|
5213
5446
|
return content ? content.includes("caliber") : false;
|
|
@@ -5321,22 +5554,22 @@ function checkBonus(dir) {
|
|
|
5321
5554
|
|
|
5322
5555
|
// src/scoring/dismissed.ts
|
|
5323
5556
|
init_constants();
|
|
5324
|
-
import
|
|
5325
|
-
import
|
|
5326
|
-
var DISMISSED_FILE =
|
|
5557
|
+
import fs22 from "fs";
|
|
5558
|
+
import path17 from "path";
|
|
5559
|
+
var DISMISSED_FILE = path17.join(CALIBER_DIR, "dismissed-checks.json");
|
|
5327
5560
|
function readDismissedChecks() {
|
|
5328
5561
|
try {
|
|
5329
|
-
if (!
|
|
5330
|
-
return JSON.parse(
|
|
5562
|
+
if (!fs22.existsSync(DISMISSED_FILE)) return [];
|
|
5563
|
+
return JSON.parse(fs22.readFileSync(DISMISSED_FILE, "utf-8"));
|
|
5331
5564
|
} catch {
|
|
5332
5565
|
return [];
|
|
5333
5566
|
}
|
|
5334
5567
|
}
|
|
5335
5568
|
function writeDismissedChecks(checks) {
|
|
5336
|
-
if (!
|
|
5337
|
-
|
|
5569
|
+
if (!fs22.existsSync(CALIBER_DIR)) {
|
|
5570
|
+
fs22.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
5338
5571
|
}
|
|
5339
|
-
|
|
5572
|
+
fs22.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
|
|
5340
5573
|
}
|
|
5341
5574
|
function getDismissedIds() {
|
|
5342
5575
|
return new Set(readDismissedChecks().map((c) => c.id));
|
|
@@ -5581,13 +5814,13 @@ import { mkdirSync, readFileSync as readFileSync4, readdirSync as readdirSync4,
|
|
|
5581
5814
|
import { join as join9, dirname as dirname2 } from "path";
|
|
5582
5815
|
|
|
5583
5816
|
// src/scanner/index.ts
|
|
5584
|
-
import
|
|
5585
|
-
import
|
|
5586
|
-
import
|
|
5817
|
+
import fs23 from "fs";
|
|
5818
|
+
import path18 from "path";
|
|
5819
|
+
import crypto3 from "crypto";
|
|
5587
5820
|
function scanLocalState(dir) {
|
|
5588
5821
|
const items = [];
|
|
5589
|
-
const claudeMdPath =
|
|
5590
|
-
if (
|
|
5822
|
+
const claudeMdPath = path18.join(dir, "CLAUDE.md");
|
|
5823
|
+
if (fs23.existsSync(claudeMdPath)) {
|
|
5591
5824
|
items.push({
|
|
5592
5825
|
type: "rule",
|
|
5593
5826
|
platform: "claude",
|
|
@@ -5596,10 +5829,10 @@ function scanLocalState(dir) {
|
|
|
5596
5829
|
path: claudeMdPath
|
|
5597
5830
|
});
|
|
5598
5831
|
}
|
|
5599
|
-
const skillsDir =
|
|
5600
|
-
if (
|
|
5601
|
-
for (const file of
|
|
5602
|
-
const filePath =
|
|
5832
|
+
const skillsDir = path18.join(dir, ".claude", "skills");
|
|
5833
|
+
if (fs23.existsSync(skillsDir)) {
|
|
5834
|
+
for (const file of fs23.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
|
|
5835
|
+
const filePath = path18.join(skillsDir, file);
|
|
5603
5836
|
items.push({
|
|
5604
5837
|
type: "skill",
|
|
5605
5838
|
platform: "claude",
|
|
@@ -5609,10 +5842,10 @@ function scanLocalState(dir) {
|
|
|
5609
5842
|
});
|
|
5610
5843
|
}
|
|
5611
5844
|
}
|
|
5612
|
-
const mcpJsonPath =
|
|
5613
|
-
if (
|
|
5845
|
+
const mcpJsonPath = path18.join(dir, ".mcp.json");
|
|
5846
|
+
if (fs23.existsSync(mcpJsonPath)) {
|
|
5614
5847
|
try {
|
|
5615
|
-
const mcpJson = JSON.parse(
|
|
5848
|
+
const mcpJson = JSON.parse(fs23.readFileSync(mcpJsonPath, "utf-8"));
|
|
5616
5849
|
if (mcpJson.mcpServers) {
|
|
5617
5850
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
5618
5851
|
items.push({
|
|
@@ -5627,8 +5860,8 @@ function scanLocalState(dir) {
|
|
|
5627
5860
|
} catch {
|
|
5628
5861
|
}
|
|
5629
5862
|
}
|
|
5630
|
-
const agentsMdPath =
|
|
5631
|
-
if (
|
|
5863
|
+
const agentsMdPath = path18.join(dir, "AGENTS.md");
|
|
5864
|
+
if (fs23.existsSync(agentsMdPath)) {
|
|
5632
5865
|
items.push({
|
|
5633
5866
|
type: "rule",
|
|
5634
5867
|
platform: "codex",
|
|
@@ -5637,12 +5870,12 @@ function scanLocalState(dir) {
|
|
|
5637
5870
|
path: agentsMdPath
|
|
5638
5871
|
});
|
|
5639
5872
|
}
|
|
5640
|
-
const codexSkillsDir =
|
|
5641
|
-
if (
|
|
5873
|
+
const codexSkillsDir = path18.join(dir, ".agents", "skills");
|
|
5874
|
+
if (fs23.existsSync(codexSkillsDir)) {
|
|
5642
5875
|
try {
|
|
5643
|
-
for (const name of
|
|
5644
|
-
const skillFile =
|
|
5645
|
-
if (
|
|
5876
|
+
for (const name of fs23.readdirSync(codexSkillsDir)) {
|
|
5877
|
+
const skillFile = path18.join(codexSkillsDir, name, "SKILL.md");
|
|
5878
|
+
if (fs23.existsSync(skillFile)) {
|
|
5646
5879
|
items.push({
|
|
5647
5880
|
type: "skill",
|
|
5648
5881
|
platform: "codex",
|
|
@@ -5655,8 +5888,8 @@ function scanLocalState(dir) {
|
|
|
5655
5888
|
} catch {
|
|
5656
5889
|
}
|
|
5657
5890
|
}
|
|
5658
|
-
const cursorrulesPath =
|
|
5659
|
-
if (
|
|
5891
|
+
const cursorrulesPath = path18.join(dir, ".cursorrules");
|
|
5892
|
+
if (fs23.existsSync(cursorrulesPath)) {
|
|
5660
5893
|
items.push({
|
|
5661
5894
|
type: "rule",
|
|
5662
5895
|
platform: "cursor",
|
|
@@ -5665,10 +5898,10 @@ function scanLocalState(dir) {
|
|
|
5665
5898
|
path: cursorrulesPath
|
|
5666
5899
|
});
|
|
5667
5900
|
}
|
|
5668
|
-
const cursorRulesDir =
|
|
5669
|
-
if (
|
|
5670
|
-
for (const file of
|
|
5671
|
-
const filePath =
|
|
5901
|
+
const cursorRulesDir = path18.join(dir, ".cursor", "rules");
|
|
5902
|
+
if (fs23.existsSync(cursorRulesDir)) {
|
|
5903
|
+
for (const file of fs23.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
|
|
5904
|
+
const filePath = path18.join(cursorRulesDir, file);
|
|
5672
5905
|
items.push({
|
|
5673
5906
|
type: "rule",
|
|
5674
5907
|
platform: "cursor",
|
|
@@ -5678,12 +5911,12 @@ function scanLocalState(dir) {
|
|
|
5678
5911
|
});
|
|
5679
5912
|
}
|
|
5680
5913
|
}
|
|
5681
|
-
const cursorSkillsDir =
|
|
5682
|
-
if (
|
|
5914
|
+
const cursorSkillsDir = path18.join(dir, ".cursor", "skills");
|
|
5915
|
+
if (fs23.existsSync(cursorSkillsDir)) {
|
|
5683
5916
|
try {
|
|
5684
|
-
for (const name of
|
|
5685
|
-
const skillFile =
|
|
5686
|
-
if (
|
|
5917
|
+
for (const name of fs23.readdirSync(cursorSkillsDir)) {
|
|
5918
|
+
const skillFile = path18.join(cursorSkillsDir, name, "SKILL.md");
|
|
5919
|
+
if (fs23.existsSync(skillFile)) {
|
|
5687
5920
|
items.push({
|
|
5688
5921
|
type: "skill",
|
|
5689
5922
|
platform: "cursor",
|
|
@@ -5696,10 +5929,10 @@ function scanLocalState(dir) {
|
|
|
5696
5929
|
} catch {
|
|
5697
5930
|
}
|
|
5698
5931
|
}
|
|
5699
|
-
const cursorMcpPath =
|
|
5700
|
-
if (
|
|
5932
|
+
const cursorMcpPath = path18.join(dir, ".cursor", "mcp.json");
|
|
5933
|
+
if (fs23.existsSync(cursorMcpPath)) {
|
|
5701
5934
|
try {
|
|
5702
|
-
const mcpJson = JSON.parse(
|
|
5935
|
+
const mcpJson = JSON.parse(fs23.readFileSync(cursorMcpPath, "utf-8"));
|
|
5703
5936
|
if (mcpJson.mcpServers) {
|
|
5704
5937
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
5705
5938
|
items.push({
|
|
@@ -5717,11 +5950,11 @@ function scanLocalState(dir) {
|
|
|
5717
5950
|
return items;
|
|
5718
5951
|
}
|
|
5719
5952
|
function hashFile(filePath) {
|
|
5720
|
-
const text =
|
|
5721
|
-
return
|
|
5953
|
+
const text = fs23.readFileSync(filePath, "utf-8");
|
|
5954
|
+
return crypto3.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
|
|
5722
5955
|
}
|
|
5723
5956
|
function hashJson(obj) {
|
|
5724
|
-
return
|
|
5957
|
+
return crypto3.createHash("sha256").update(JSON.stringify(obj)).digest("hex");
|
|
5725
5958
|
}
|
|
5726
5959
|
|
|
5727
5960
|
// src/commands/recommend.ts
|
|
@@ -5732,40 +5965,40 @@ import { PostHog } from "posthog-node";
|
|
|
5732
5965
|
import chalk7 from "chalk";
|
|
5733
5966
|
|
|
5734
5967
|
// src/telemetry/config.ts
|
|
5735
|
-
import
|
|
5736
|
-
import
|
|
5968
|
+
import fs24 from "fs";
|
|
5969
|
+
import path19 from "path";
|
|
5737
5970
|
import os5 from "os";
|
|
5738
|
-
import
|
|
5739
|
-
import { execSync as
|
|
5740
|
-
var CONFIG_DIR2 =
|
|
5741
|
-
var CONFIG_FILE2 =
|
|
5971
|
+
import crypto4 from "crypto";
|
|
5972
|
+
import { execSync as execSync13 } from "child_process";
|
|
5973
|
+
var CONFIG_DIR2 = path19.join(os5.homedir(), ".caliber");
|
|
5974
|
+
var CONFIG_FILE2 = path19.join(CONFIG_DIR2, "config.json");
|
|
5742
5975
|
var runtimeDisabled = false;
|
|
5743
5976
|
function readConfig() {
|
|
5744
5977
|
try {
|
|
5745
|
-
if (!
|
|
5746
|
-
return JSON.parse(
|
|
5978
|
+
if (!fs24.existsSync(CONFIG_FILE2)) return {};
|
|
5979
|
+
return JSON.parse(fs24.readFileSync(CONFIG_FILE2, "utf-8"));
|
|
5747
5980
|
} catch {
|
|
5748
5981
|
return {};
|
|
5749
5982
|
}
|
|
5750
5983
|
}
|
|
5751
5984
|
function writeConfig(config) {
|
|
5752
|
-
if (!
|
|
5753
|
-
|
|
5985
|
+
if (!fs24.existsSync(CONFIG_DIR2)) {
|
|
5986
|
+
fs24.mkdirSync(CONFIG_DIR2, { recursive: true });
|
|
5754
5987
|
}
|
|
5755
|
-
|
|
5988
|
+
fs24.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
|
|
5756
5989
|
}
|
|
5757
5990
|
function getMachineId() {
|
|
5758
5991
|
const config = readConfig();
|
|
5759
5992
|
if (config.machineId) return config.machineId;
|
|
5760
|
-
const machineId =
|
|
5993
|
+
const machineId = crypto4.randomUUID();
|
|
5761
5994
|
writeConfig({ ...config, machineId });
|
|
5762
5995
|
return machineId;
|
|
5763
5996
|
}
|
|
5764
5997
|
function getGitEmailHash() {
|
|
5765
5998
|
try {
|
|
5766
|
-
const email =
|
|
5999
|
+
const email = execSync13("git config user.email", { encoding: "utf-8" }).trim();
|
|
5767
6000
|
if (!email) return void 0;
|
|
5768
|
-
return
|
|
6001
|
+
return crypto4.createHash("sha256").update(email).digest("hex");
|
|
5769
6002
|
} catch {
|
|
5770
6003
|
return void 0;
|
|
5771
6004
|
}
|
|
@@ -6708,11 +6941,11 @@ function countIssuePoints(issues) {
|
|
|
6708
6941
|
}
|
|
6709
6942
|
async function scoreAndRefine(setup, dir, sessionHistory, callbacks) {
|
|
6710
6943
|
const existsCache = /* @__PURE__ */ new Map();
|
|
6711
|
-
const cachedExists = (
|
|
6712
|
-
const cached = existsCache.get(
|
|
6944
|
+
const cachedExists = (path30) => {
|
|
6945
|
+
const cached = existsCache.get(path30);
|
|
6713
6946
|
if (cached !== void 0) return cached;
|
|
6714
|
-
const result = existsSync9(
|
|
6715
|
-
existsCache.set(
|
|
6947
|
+
const result = existsSync9(path30);
|
|
6948
|
+
existsCache.set(path30, result);
|
|
6716
6949
|
return result;
|
|
6717
6950
|
};
|
|
6718
6951
|
const projectStructure = collectProjectStructure(dir);
|
|
@@ -6851,8 +7084,8 @@ async function runScoreRefineWithSpinner(setup, dir, sessionHistory) {
|
|
|
6851
7084
|
}
|
|
6852
7085
|
|
|
6853
7086
|
// src/lib/debug-report.ts
|
|
6854
|
-
import
|
|
6855
|
-
import
|
|
7087
|
+
import fs25 from "fs";
|
|
7088
|
+
import path20 from "path";
|
|
6856
7089
|
var DebugReport = class {
|
|
6857
7090
|
sections = [];
|
|
6858
7091
|
startTime;
|
|
@@ -6921,11 +7154,11 @@ var DebugReport = class {
|
|
|
6921
7154
|
lines.push(`| **Total** | **${formatMs(totalMs)}** |`);
|
|
6922
7155
|
lines.push("");
|
|
6923
7156
|
}
|
|
6924
|
-
const dir =
|
|
6925
|
-
if (!
|
|
6926
|
-
|
|
7157
|
+
const dir = path20.dirname(outputPath);
|
|
7158
|
+
if (!fs25.existsSync(dir)) {
|
|
7159
|
+
fs25.mkdirSync(dir, { recursive: true });
|
|
6927
7160
|
}
|
|
6928
|
-
|
|
7161
|
+
fs25.writeFileSync(outputPath, lines.join("\n"));
|
|
6929
7162
|
}
|
|
6930
7163
|
};
|
|
6931
7164
|
function formatMs(ms) {
|
|
@@ -7421,11 +7654,10 @@ async function initCommand(options) {
|
|
|
7421
7654
|
try {
|
|
7422
7655
|
display.update(TASK_STACK, "running");
|
|
7423
7656
|
fingerprint = await collectFingerprint(process.cwd());
|
|
7424
|
-
const
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
display.update(TASK_STACK, "done", stackSummary);
|
|
7657
|
+
const stackParts = [...fingerprint.languages, ...fingerprint.frameworks];
|
|
7658
|
+
const stackSummary = stackParts.join(", ") || "no languages";
|
|
7659
|
+
const largeRepoNote = fingerprint.fileTree.length > 5e3 ? ` (${fingerprint.fileTree.length.toLocaleString()} files, smart sampling active)` : "";
|
|
7660
|
+
display.update(TASK_STACK, "done", stackSummary + largeRepoNote);
|
|
7429
7661
|
trackInitProjectDiscovered(fingerprint.languages.length, fingerprint.frameworks.length, fingerprint.fileTree.length);
|
|
7430
7662
|
log(options.verbose, `Fingerprint: ${fingerprint.languages.length} languages, ${fingerprint.frameworks.length} frameworks, ${fingerprint.fileTree.length} files`);
|
|
7431
7663
|
if (report) {
|
|
@@ -7661,7 +7893,7 @@ async function initCommand(options) {
|
|
|
7661
7893
|
}
|
|
7662
7894
|
const writeSpinner = ora3("Writing config files...").start();
|
|
7663
7895
|
try {
|
|
7664
|
-
if (targetAgent.includes("codex") && !
|
|
7896
|
+
if (targetAgent.includes("codex") && !fs26.existsSync("AGENTS.md") && !generatedSetup.codex) {
|
|
7665
7897
|
const claude = generatedSetup.claude;
|
|
7666
7898
|
const cursor = generatedSetup.cursor;
|
|
7667
7899
|
const agentRefs = [];
|
|
@@ -7845,9 +8077,9 @@ ${agentRefs.join(" ")}
|
|
|
7845
8077
|
}
|
|
7846
8078
|
if (report) {
|
|
7847
8079
|
report.markStep("Finished");
|
|
7848
|
-
const reportPath =
|
|
8080
|
+
const reportPath = path21.join(process.cwd(), ".caliber", "debug-report.md");
|
|
7849
8081
|
report.write(reportPath);
|
|
7850
|
-
console.log(chalk11.dim(` Debug report written to ${
|
|
8082
|
+
console.log(chalk11.dim(` Debug report written to ${path21.relative(process.cwd(), reportPath)}
|
|
7851
8083
|
`));
|
|
7852
8084
|
}
|
|
7853
8085
|
}
|
|
@@ -7894,7 +8126,7 @@ async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
|
|
|
7894
8126
|
}
|
|
7895
8127
|
function summarizeSetup(action, setup) {
|
|
7896
8128
|
const descriptions = setup.fileDescriptions;
|
|
7897
|
-
const files = descriptions ? Object.entries(descriptions).map(([
|
|
8129
|
+
const files = descriptions ? Object.entries(descriptions).map(([path30, desc]) => ` ${path30}: ${desc}`).join("\n") : Object.keys(setup).filter((k) => k !== "targetAgent" && k !== "fileDescriptions").join(", ");
|
|
7898
8130
|
return `${action}. Files:
|
|
7899
8131
|
${files}`;
|
|
7900
8132
|
}
|
|
@@ -8028,7 +8260,7 @@ function printSetupSummary(setup) {
|
|
|
8028
8260
|
};
|
|
8029
8261
|
if (claude) {
|
|
8030
8262
|
if (claude.claudeMd) {
|
|
8031
|
-
const icon =
|
|
8263
|
+
const icon = fs26.existsSync("CLAUDE.md") ? chalk11.yellow("~") : chalk11.green("+");
|
|
8032
8264
|
const desc = getDescription("CLAUDE.md");
|
|
8033
8265
|
console.log(` ${icon} ${chalk11.bold("CLAUDE.md")}`);
|
|
8034
8266
|
if (desc) console.log(chalk11.dim(` ${desc}`));
|
|
@@ -8038,7 +8270,7 @@ function printSetupSummary(setup) {
|
|
|
8038
8270
|
if (Array.isArray(skills) && skills.length > 0) {
|
|
8039
8271
|
for (const skill of skills) {
|
|
8040
8272
|
const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
|
|
8041
|
-
const icon =
|
|
8273
|
+
const icon = fs26.existsSync(skillPath) ? chalk11.yellow("~") : chalk11.green("+");
|
|
8042
8274
|
const desc = getDescription(skillPath);
|
|
8043
8275
|
console.log(` ${icon} ${chalk11.bold(skillPath)}`);
|
|
8044
8276
|
console.log(chalk11.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -8049,7 +8281,7 @@ function printSetupSummary(setup) {
|
|
|
8049
8281
|
const codex = setup.codex;
|
|
8050
8282
|
if (codex) {
|
|
8051
8283
|
if (codex.agentsMd) {
|
|
8052
|
-
const icon =
|
|
8284
|
+
const icon = fs26.existsSync("AGENTS.md") ? chalk11.yellow("~") : chalk11.green("+");
|
|
8053
8285
|
const desc = getDescription("AGENTS.md");
|
|
8054
8286
|
console.log(` ${icon} ${chalk11.bold("AGENTS.md")}`);
|
|
8055
8287
|
if (desc) console.log(chalk11.dim(` ${desc}`));
|
|
@@ -8059,7 +8291,7 @@ function printSetupSummary(setup) {
|
|
|
8059
8291
|
if (Array.isArray(codexSkills) && codexSkills.length > 0) {
|
|
8060
8292
|
for (const skill of codexSkills) {
|
|
8061
8293
|
const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
|
|
8062
|
-
const icon =
|
|
8294
|
+
const icon = fs26.existsSync(skillPath) ? chalk11.yellow("~") : chalk11.green("+");
|
|
8063
8295
|
const desc = getDescription(skillPath);
|
|
8064
8296
|
console.log(` ${icon} ${chalk11.bold(skillPath)}`);
|
|
8065
8297
|
console.log(chalk11.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -8069,7 +8301,7 @@ function printSetupSummary(setup) {
|
|
|
8069
8301
|
}
|
|
8070
8302
|
if (cursor) {
|
|
8071
8303
|
if (cursor.cursorrules) {
|
|
8072
|
-
const icon =
|
|
8304
|
+
const icon = fs26.existsSync(".cursorrules") ? chalk11.yellow("~") : chalk11.green("+");
|
|
8073
8305
|
const desc = getDescription(".cursorrules");
|
|
8074
8306
|
console.log(` ${icon} ${chalk11.bold(".cursorrules")}`);
|
|
8075
8307
|
if (desc) console.log(chalk11.dim(` ${desc}`));
|
|
@@ -8079,7 +8311,7 @@ function printSetupSummary(setup) {
|
|
|
8079
8311
|
if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
|
|
8080
8312
|
for (const skill of cursorSkills) {
|
|
8081
8313
|
const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
|
|
8082
|
-
const icon =
|
|
8314
|
+
const icon = fs26.existsSync(skillPath) ? chalk11.yellow("~") : chalk11.green("+");
|
|
8083
8315
|
const desc = getDescription(skillPath);
|
|
8084
8316
|
console.log(` ${icon} ${chalk11.bold(skillPath)}`);
|
|
8085
8317
|
console.log(chalk11.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -8090,7 +8322,7 @@ function printSetupSummary(setup) {
|
|
|
8090
8322
|
if (Array.isArray(rules) && rules.length > 0) {
|
|
8091
8323
|
for (const rule of rules) {
|
|
8092
8324
|
const rulePath = `.cursor/rules/${rule.filename}`;
|
|
8093
|
-
const icon =
|
|
8325
|
+
const icon = fs26.existsSync(rulePath) ? chalk11.yellow("~") : chalk11.green("+");
|
|
8094
8326
|
const desc = getDescription(rulePath);
|
|
8095
8327
|
console.log(` ${icon} ${chalk11.bold(rulePath)}`);
|
|
8096
8328
|
if (desc) {
|
|
@@ -8154,8 +8386,8 @@ function ensurePermissions(fingerprint) {
|
|
|
8154
8386
|
const settingsPath = ".claude/settings.json";
|
|
8155
8387
|
let settings = {};
|
|
8156
8388
|
try {
|
|
8157
|
-
if (
|
|
8158
|
-
settings = JSON.parse(
|
|
8389
|
+
if (fs26.existsSync(settingsPath)) {
|
|
8390
|
+
settings = JSON.parse(fs26.readFileSync(settingsPath, "utf-8"));
|
|
8159
8391
|
}
|
|
8160
8392
|
} catch {
|
|
8161
8393
|
}
|
|
@@ -8164,8 +8396,8 @@ function ensurePermissions(fingerprint) {
|
|
|
8164
8396
|
if (Array.isArray(allow) && allow.length > 0) return;
|
|
8165
8397
|
permissions.allow = derivePermissions(fingerprint);
|
|
8166
8398
|
settings.permissions = permissions;
|
|
8167
|
-
if (!
|
|
8168
|
-
|
|
8399
|
+
if (!fs26.existsSync(".claude")) fs26.mkdirSync(".claude", { recursive: true });
|
|
8400
|
+
fs26.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
8169
8401
|
}
|
|
8170
8402
|
function displayTokenUsage() {
|
|
8171
8403
|
const summary = getUsageSummary();
|
|
@@ -8189,7 +8421,7 @@ function displayTokenUsage() {
|
|
|
8189
8421
|
}
|
|
8190
8422
|
function writeErrorLog(config, rawOutput, error, stopReason) {
|
|
8191
8423
|
try {
|
|
8192
|
-
const logPath =
|
|
8424
|
+
const logPath = path21.join(process.cwd(), ".caliber", "error-log.md");
|
|
8193
8425
|
const lines = [
|
|
8194
8426
|
`# Generation Error \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
8195
8427
|
"",
|
|
@@ -8202,8 +8434,8 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
|
|
|
8202
8434
|
lines.push("## Error", "```", error, "```", "");
|
|
8203
8435
|
}
|
|
8204
8436
|
lines.push("## Raw LLM Output", "```", rawOutput || "(empty)", "```");
|
|
8205
|
-
|
|
8206
|
-
|
|
8437
|
+
fs26.mkdirSync(path21.join(process.cwd(), ".caliber"), { recursive: true });
|
|
8438
|
+
fs26.writeFileSync(logPath, lines.join("\n"));
|
|
8207
8439
|
console.log(chalk11.dim(`
|
|
8208
8440
|
Error log written to .caliber/error-log.md`));
|
|
8209
8441
|
} catch {
|
|
@@ -8244,7 +8476,7 @@ function undoCommand() {
|
|
|
8244
8476
|
|
|
8245
8477
|
// src/commands/status.ts
|
|
8246
8478
|
import chalk13 from "chalk";
|
|
8247
|
-
import
|
|
8479
|
+
import fs27 from "fs";
|
|
8248
8480
|
init_config();
|
|
8249
8481
|
async function statusCommand(options) {
|
|
8250
8482
|
const config = loadConfig();
|
|
@@ -8271,7 +8503,7 @@ async function statusCommand(options) {
|
|
|
8271
8503
|
}
|
|
8272
8504
|
console.log(` Files managed: ${chalk13.cyan(manifest.entries.length.toString())}`);
|
|
8273
8505
|
for (const entry of manifest.entries) {
|
|
8274
|
-
const exists =
|
|
8506
|
+
const exists = fs27.existsSync(entry.path);
|
|
8275
8507
|
const icon = exists ? chalk13.green("\u2713") : chalk13.red("\u2717");
|
|
8276
8508
|
console.log(` ${icon} ${entry.path} (${entry.action})`);
|
|
8277
8509
|
}
|
|
@@ -8453,13 +8685,13 @@ async function scoreCommand(options) {
|
|
|
8453
8685
|
}
|
|
8454
8686
|
|
|
8455
8687
|
// src/commands/refresh.ts
|
|
8456
|
-
import
|
|
8457
|
-
import
|
|
8688
|
+
import fs31 from "fs";
|
|
8689
|
+
import path25 from "path";
|
|
8458
8690
|
import chalk16 from "chalk";
|
|
8459
8691
|
import ora6 from "ora";
|
|
8460
8692
|
|
|
8461
8693
|
// src/lib/git-diff.ts
|
|
8462
|
-
import { execSync as
|
|
8694
|
+
import { execSync as execSync14 } from "child_process";
|
|
8463
8695
|
var MAX_DIFF_BYTES = 1e5;
|
|
8464
8696
|
var DOC_PATTERNS = [
|
|
8465
8697
|
"CLAUDE.md",
|
|
@@ -8474,7 +8706,7 @@ function excludeArgs() {
|
|
|
8474
8706
|
}
|
|
8475
8707
|
function safeExec(cmd) {
|
|
8476
8708
|
try {
|
|
8477
|
-
return
|
|
8709
|
+
return execSync14(cmd, {
|
|
8478
8710
|
encoding: "utf-8",
|
|
8479
8711
|
stdio: ["pipe", "pipe", "pipe"],
|
|
8480
8712
|
maxBuffer: 10 * 1024 * 1024
|
|
@@ -8532,37 +8764,37 @@ function collectDiff(lastSha) {
|
|
|
8532
8764
|
}
|
|
8533
8765
|
|
|
8534
8766
|
// src/writers/refresh.ts
|
|
8535
|
-
import
|
|
8536
|
-
import
|
|
8767
|
+
import fs28 from "fs";
|
|
8768
|
+
import path22 from "path";
|
|
8537
8769
|
function writeRefreshDocs(docs) {
|
|
8538
8770
|
const written = [];
|
|
8539
8771
|
if (docs.claudeMd) {
|
|
8540
|
-
|
|
8772
|
+
fs28.writeFileSync("CLAUDE.md", docs.claudeMd);
|
|
8541
8773
|
written.push("CLAUDE.md");
|
|
8542
8774
|
}
|
|
8543
8775
|
if (docs.readmeMd) {
|
|
8544
|
-
|
|
8776
|
+
fs28.writeFileSync("README.md", docs.readmeMd);
|
|
8545
8777
|
written.push("README.md");
|
|
8546
8778
|
}
|
|
8547
8779
|
if (docs.cursorrules) {
|
|
8548
|
-
|
|
8780
|
+
fs28.writeFileSync(".cursorrules", docs.cursorrules);
|
|
8549
8781
|
written.push(".cursorrules");
|
|
8550
8782
|
}
|
|
8551
8783
|
if (docs.cursorRules) {
|
|
8552
|
-
const rulesDir =
|
|
8553
|
-
if (!
|
|
8784
|
+
const rulesDir = path22.join(".cursor", "rules");
|
|
8785
|
+
if (!fs28.existsSync(rulesDir)) fs28.mkdirSync(rulesDir, { recursive: true });
|
|
8554
8786
|
for (const rule of docs.cursorRules) {
|
|
8555
|
-
const filePath =
|
|
8556
|
-
|
|
8787
|
+
const filePath = path22.join(rulesDir, rule.filename);
|
|
8788
|
+
fs28.writeFileSync(filePath, rule.content);
|
|
8557
8789
|
written.push(filePath);
|
|
8558
8790
|
}
|
|
8559
8791
|
}
|
|
8560
8792
|
if (docs.claudeSkills) {
|
|
8561
|
-
const skillsDir =
|
|
8562
|
-
if (!
|
|
8793
|
+
const skillsDir = path22.join(".claude", "skills");
|
|
8794
|
+
if (!fs28.existsSync(skillsDir)) fs28.mkdirSync(skillsDir, { recursive: true });
|
|
8563
8795
|
for (const skill of docs.claudeSkills) {
|
|
8564
|
-
const filePath =
|
|
8565
|
-
|
|
8796
|
+
const filePath = path22.join(skillsDir, skill.filename);
|
|
8797
|
+
fs28.writeFileSync(filePath, skill.content);
|
|
8566
8798
|
written.push(filePath);
|
|
8567
8799
|
}
|
|
8568
8800
|
}
|
|
@@ -8639,8 +8871,8 @@ Changed files: ${diff.changedFiles.join(", ")}`);
|
|
|
8639
8871
|
}
|
|
8640
8872
|
|
|
8641
8873
|
// src/learner/writer.ts
|
|
8642
|
-
import
|
|
8643
|
-
import
|
|
8874
|
+
import fs29 from "fs";
|
|
8875
|
+
import path23 from "path";
|
|
8644
8876
|
var LEARNINGS_FILE = "CALIBER_LEARNINGS.md";
|
|
8645
8877
|
var LEARNINGS_HEADER = `# Caliber Learnings
|
|
8646
8878
|
|
|
@@ -8724,16 +8956,16 @@ function deduplicateLearnedItems(existing, incoming) {
|
|
|
8724
8956
|
function writeLearnedSection(content) {
|
|
8725
8957
|
const existingSection = readLearnedSection();
|
|
8726
8958
|
const { merged, newCount, newItems } = deduplicateLearnedItems(existingSection, content);
|
|
8727
|
-
|
|
8959
|
+
fs29.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + merged + "\n");
|
|
8728
8960
|
return { newCount, newItems };
|
|
8729
8961
|
}
|
|
8730
8962
|
function writeLearnedSkill(skill) {
|
|
8731
|
-
const skillDir =
|
|
8732
|
-
if (!
|
|
8733
|
-
const skillPath =
|
|
8734
|
-
if (!skill.isNew &&
|
|
8735
|
-
const existing =
|
|
8736
|
-
|
|
8963
|
+
const skillDir = path23.join(".claude", "skills", skill.name);
|
|
8964
|
+
if (!fs29.existsSync(skillDir)) fs29.mkdirSync(skillDir, { recursive: true });
|
|
8965
|
+
const skillPath = path23.join(skillDir, "SKILL.md");
|
|
8966
|
+
if (!skill.isNew && fs29.existsSync(skillPath)) {
|
|
8967
|
+
const existing = fs29.readFileSync(skillPath, "utf-8");
|
|
8968
|
+
fs29.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
|
|
8737
8969
|
} else {
|
|
8738
8970
|
const frontmatter = [
|
|
8739
8971
|
"---",
|
|
@@ -8742,37 +8974,37 @@ function writeLearnedSkill(skill) {
|
|
|
8742
8974
|
"---",
|
|
8743
8975
|
""
|
|
8744
8976
|
].join("\n");
|
|
8745
|
-
|
|
8977
|
+
fs29.writeFileSync(skillPath, frontmatter + skill.content);
|
|
8746
8978
|
}
|
|
8747
8979
|
return skillPath;
|
|
8748
8980
|
}
|
|
8749
8981
|
function readLearnedSection() {
|
|
8750
|
-
if (
|
|
8751
|
-
const content2 =
|
|
8982
|
+
if (fs29.existsSync(LEARNINGS_FILE)) {
|
|
8983
|
+
const content2 = fs29.readFileSync(LEARNINGS_FILE, "utf-8");
|
|
8752
8984
|
const bullets = content2.split("\n").filter((l) => l.startsWith("- ")).join("\n");
|
|
8753
8985
|
return bullets || null;
|
|
8754
8986
|
}
|
|
8755
8987
|
const claudeMdPath = "CLAUDE.md";
|
|
8756
|
-
if (!
|
|
8757
|
-
const content =
|
|
8988
|
+
if (!fs29.existsSync(claudeMdPath)) return null;
|
|
8989
|
+
const content = fs29.readFileSync(claudeMdPath, "utf-8");
|
|
8758
8990
|
const startIdx = content.indexOf(LEARNED_START);
|
|
8759
8991
|
const endIdx = content.indexOf(LEARNED_END);
|
|
8760
8992
|
if (startIdx === -1 || endIdx === -1) return null;
|
|
8761
8993
|
return content.slice(startIdx + LEARNED_START.length, endIdx).trim() || null;
|
|
8762
8994
|
}
|
|
8763
8995
|
function migrateInlineLearnings() {
|
|
8764
|
-
if (
|
|
8996
|
+
if (fs29.existsSync(LEARNINGS_FILE)) return false;
|
|
8765
8997
|
const claudeMdPath = "CLAUDE.md";
|
|
8766
|
-
if (!
|
|
8767
|
-
const content =
|
|
8998
|
+
if (!fs29.existsSync(claudeMdPath)) return false;
|
|
8999
|
+
const content = fs29.readFileSync(claudeMdPath, "utf-8");
|
|
8768
9000
|
const startIdx = content.indexOf(LEARNED_START);
|
|
8769
9001
|
const endIdx = content.indexOf(LEARNED_END);
|
|
8770
9002
|
if (startIdx === -1 || endIdx === -1) return false;
|
|
8771
9003
|
const section = content.slice(startIdx + LEARNED_START.length, endIdx).trim();
|
|
8772
9004
|
if (!section) return false;
|
|
8773
|
-
|
|
9005
|
+
fs29.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
|
|
8774
9006
|
const cleaned = content.slice(0, startIdx) + content.slice(endIdx + LEARNED_END.length);
|
|
8775
|
-
|
|
9007
|
+
fs29.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
|
|
8776
9008
|
return true;
|
|
8777
9009
|
}
|
|
8778
9010
|
|
|
@@ -8784,11 +9016,11 @@ function log2(quiet, ...args) {
|
|
|
8784
9016
|
function discoverGitRepos(parentDir) {
|
|
8785
9017
|
const repos = [];
|
|
8786
9018
|
try {
|
|
8787
|
-
const entries =
|
|
9019
|
+
const entries = fs31.readdirSync(parentDir, { withFileTypes: true });
|
|
8788
9020
|
for (const entry of entries) {
|
|
8789
9021
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
8790
|
-
const childPath =
|
|
8791
|
-
if (
|
|
9022
|
+
const childPath = path25.join(parentDir, entry.name);
|
|
9023
|
+
if (fs31.existsSync(path25.join(childPath, ".git"))) {
|
|
8792
9024
|
repos.push(childPath);
|
|
8793
9025
|
}
|
|
8794
9026
|
}
|
|
@@ -8891,7 +9123,7 @@ async function refreshCommand(options) {
|
|
|
8891
9123
|
`));
|
|
8892
9124
|
const originalDir = process.cwd();
|
|
8893
9125
|
for (const repo of repos) {
|
|
8894
|
-
const repoName =
|
|
9126
|
+
const repoName = path25.basename(repo);
|
|
8895
9127
|
try {
|
|
8896
9128
|
process.chdir(repo);
|
|
8897
9129
|
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
@@ -9115,7 +9347,7 @@ async function configCommand() {
|
|
|
9115
9347
|
}
|
|
9116
9348
|
|
|
9117
9349
|
// src/commands/learn.ts
|
|
9118
|
-
import
|
|
9350
|
+
import fs34 from "fs";
|
|
9119
9351
|
import chalk19 from "chalk";
|
|
9120
9352
|
|
|
9121
9353
|
// src/learner/stdin.ts
|
|
@@ -9147,8 +9379,8 @@ function readStdin() {
|
|
|
9147
9379
|
|
|
9148
9380
|
// src/learner/storage.ts
|
|
9149
9381
|
init_constants();
|
|
9150
|
-
import
|
|
9151
|
-
import
|
|
9382
|
+
import fs32 from "fs";
|
|
9383
|
+
import path26 from "path";
|
|
9152
9384
|
var MAX_RESPONSE_LENGTH = 2e3;
|
|
9153
9385
|
var DEFAULT_STATE = {
|
|
9154
9386
|
sessionId: null,
|
|
@@ -9156,15 +9388,15 @@ var DEFAULT_STATE = {
|
|
|
9156
9388
|
lastAnalysisTimestamp: null
|
|
9157
9389
|
};
|
|
9158
9390
|
function ensureLearningDir() {
|
|
9159
|
-
if (!
|
|
9160
|
-
|
|
9391
|
+
if (!fs32.existsSync(LEARNING_DIR)) {
|
|
9392
|
+
fs32.mkdirSync(LEARNING_DIR, { recursive: true });
|
|
9161
9393
|
}
|
|
9162
9394
|
}
|
|
9163
9395
|
function sessionFilePath() {
|
|
9164
|
-
return
|
|
9396
|
+
return path26.join(LEARNING_DIR, LEARNING_SESSION_FILE);
|
|
9165
9397
|
}
|
|
9166
9398
|
function stateFilePath() {
|
|
9167
|
-
return
|
|
9399
|
+
return path26.join(LEARNING_DIR, LEARNING_STATE_FILE);
|
|
9168
9400
|
}
|
|
9169
9401
|
function truncateResponse(response) {
|
|
9170
9402
|
const str = JSON.stringify(response);
|
|
@@ -9175,29 +9407,29 @@ function appendEvent(event) {
|
|
|
9175
9407
|
ensureLearningDir();
|
|
9176
9408
|
const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
|
|
9177
9409
|
const filePath = sessionFilePath();
|
|
9178
|
-
|
|
9410
|
+
fs32.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
|
|
9179
9411
|
const count = getEventCount();
|
|
9180
9412
|
if (count > LEARNING_MAX_EVENTS) {
|
|
9181
|
-
const lines =
|
|
9413
|
+
const lines = fs32.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
9182
9414
|
const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
|
|
9183
|
-
|
|
9415
|
+
fs32.writeFileSync(filePath, kept.join("\n") + "\n");
|
|
9184
9416
|
}
|
|
9185
9417
|
}
|
|
9186
9418
|
function appendPromptEvent(event) {
|
|
9187
9419
|
ensureLearningDir();
|
|
9188
9420
|
const filePath = sessionFilePath();
|
|
9189
|
-
|
|
9421
|
+
fs32.appendFileSync(filePath, JSON.stringify(event) + "\n");
|
|
9190
9422
|
const count = getEventCount();
|
|
9191
9423
|
if (count > LEARNING_MAX_EVENTS) {
|
|
9192
|
-
const lines =
|
|
9424
|
+
const lines = fs32.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
9193
9425
|
const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
|
|
9194
|
-
|
|
9426
|
+
fs32.writeFileSync(filePath, kept.join("\n") + "\n");
|
|
9195
9427
|
}
|
|
9196
9428
|
}
|
|
9197
9429
|
function readAllEvents() {
|
|
9198
9430
|
const filePath = sessionFilePath();
|
|
9199
|
-
if (!
|
|
9200
|
-
const lines =
|
|
9431
|
+
if (!fs32.existsSync(filePath)) return [];
|
|
9432
|
+
const lines = fs32.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
9201
9433
|
const events = [];
|
|
9202
9434
|
for (const line of lines) {
|
|
9203
9435
|
try {
|
|
@@ -9209,26 +9441,26 @@ function readAllEvents() {
|
|
|
9209
9441
|
}
|
|
9210
9442
|
function getEventCount() {
|
|
9211
9443
|
const filePath = sessionFilePath();
|
|
9212
|
-
if (!
|
|
9213
|
-
const content =
|
|
9444
|
+
if (!fs32.existsSync(filePath)) return 0;
|
|
9445
|
+
const content = fs32.readFileSync(filePath, "utf-8");
|
|
9214
9446
|
return content.split("\n").filter(Boolean).length;
|
|
9215
9447
|
}
|
|
9216
9448
|
function clearSession() {
|
|
9217
9449
|
const filePath = sessionFilePath();
|
|
9218
|
-
if (
|
|
9450
|
+
if (fs32.existsSync(filePath)) fs32.unlinkSync(filePath);
|
|
9219
9451
|
}
|
|
9220
9452
|
function readState2() {
|
|
9221
9453
|
const filePath = stateFilePath();
|
|
9222
|
-
if (!
|
|
9454
|
+
if (!fs32.existsSync(filePath)) return { ...DEFAULT_STATE };
|
|
9223
9455
|
try {
|
|
9224
|
-
return JSON.parse(
|
|
9456
|
+
return JSON.parse(fs32.readFileSync(filePath, "utf-8"));
|
|
9225
9457
|
} catch {
|
|
9226
9458
|
return { ...DEFAULT_STATE };
|
|
9227
9459
|
}
|
|
9228
9460
|
}
|
|
9229
9461
|
function writeState2(state) {
|
|
9230
9462
|
ensureLearningDir();
|
|
9231
|
-
|
|
9463
|
+
fs32.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
|
|
9232
9464
|
}
|
|
9233
9465
|
function resetState() {
|
|
9234
9466
|
writeState2({ ...DEFAULT_STATE });
|
|
@@ -9236,14 +9468,14 @@ function resetState() {
|
|
|
9236
9468
|
var LOCK_FILE2 = "finalize.lock";
|
|
9237
9469
|
var LOCK_STALE_MS = 5 * 60 * 1e3;
|
|
9238
9470
|
function lockFilePath() {
|
|
9239
|
-
return
|
|
9471
|
+
return path26.join(LEARNING_DIR, LOCK_FILE2);
|
|
9240
9472
|
}
|
|
9241
9473
|
function acquireFinalizeLock() {
|
|
9242
9474
|
ensureLearningDir();
|
|
9243
9475
|
const lockPath = lockFilePath();
|
|
9244
|
-
if (
|
|
9476
|
+
if (fs32.existsSync(lockPath)) {
|
|
9245
9477
|
try {
|
|
9246
|
-
const stat =
|
|
9478
|
+
const stat = fs32.statSync(lockPath);
|
|
9247
9479
|
if (Date.now() - stat.mtimeMs < LOCK_STALE_MS) {
|
|
9248
9480
|
return false;
|
|
9249
9481
|
}
|
|
@@ -9251,7 +9483,7 @@ function acquireFinalizeLock() {
|
|
|
9251
9483
|
}
|
|
9252
9484
|
}
|
|
9253
9485
|
try {
|
|
9254
|
-
|
|
9486
|
+
fs32.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
9255
9487
|
return true;
|
|
9256
9488
|
} catch {
|
|
9257
9489
|
return false;
|
|
@@ -9260,7 +9492,7 @@ function acquireFinalizeLock() {
|
|
|
9260
9492
|
function releaseFinalizeLock() {
|
|
9261
9493
|
const lockPath = lockFilePath();
|
|
9262
9494
|
try {
|
|
9263
|
-
if (
|
|
9495
|
+
if (fs32.existsSync(lockPath)) fs32.unlinkSync(lockPath);
|
|
9264
9496
|
} catch {
|
|
9265
9497
|
}
|
|
9266
9498
|
}
|
|
@@ -9306,7 +9538,7 @@ function sanitizeSecrets(text) {
|
|
|
9306
9538
|
|
|
9307
9539
|
// src/ai/learn.ts
|
|
9308
9540
|
init_config();
|
|
9309
|
-
var
|
|
9541
|
+
var MAX_PROMPT_TOKENS = 1e5;
|
|
9310
9542
|
function formatEventsForPrompt(events) {
|
|
9311
9543
|
return events.map((e, i) => {
|
|
9312
9544
|
if (e.hook_event_name === "UserPromptSubmit") {
|
|
@@ -9354,7 +9586,7 @@ function parseAnalysisResponse(raw) {
|
|
|
9354
9586
|
}
|
|
9355
9587
|
}
|
|
9356
9588
|
async function analyzeEvents(events, existingClaudeMd, existingLearnedSection, existingSkills) {
|
|
9357
|
-
const fittedEvents = trimEventsToFit(events,
|
|
9589
|
+
const fittedEvents = trimEventsToFit(events, MAX_PROMPT_TOKENS - 1e4);
|
|
9358
9590
|
const eventsText = formatEventsForPrompt(fittedEvents);
|
|
9359
9591
|
const contextParts = [];
|
|
9360
9592
|
if (existingClaudeMd) {
|
|
@@ -9416,8 +9648,8 @@ init_config();
|
|
|
9416
9648
|
|
|
9417
9649
|
// src/learner/roi.ts
|
|
9418
9650
|
init_constants();
|
|
9419
|
-
import
|
|
9420
|
-
import
|
|
9651
|
+
import fs33 from "fs";
|
|
9652
|
+
import path27 from "path";
|
|
9421
9653
|
var DEFAULT_TOTALS = {
|
|
9422
9654
|
totalWasteTokens: 0,
|
|
9423
9655
|
totalWasteSeconds: 0,
|
|
@@ -9431,22 +9663,22 @@ var DEFAULT_TOTALS = {
|
|
|
9431
9663
|
lastSessionTimestamp: ""
|
|
9432
9664
|
};
|
|
9433
9665
|
function roiFilePath() {
|
|
9434
|
-
return
|
|
9666
|
+
return path27.join(LEARNING_DIR, LEARNING_ROI_FILE);
|
|
9435
9667
|
}
|
|
9436
9668
|
function readROIStats() {
|
|
9437
9669
|
const filePath = roiFilePath();
|
|
9438
|
-
if (!
|
|
9670
|
+
if (!fs33.existsSync(filePath)) {
|
|
9439
9671
|
return { learnings: [], sessions: [], totals: { ...DEFAULT_TOTALS } };
|
|
9440
9672
|
}
|
|
9441
9673
|
try {
|
|
9442
|
-
return JSON.parse(
|
|
9674
|
+
return JSON.parse(fs33.readFileSync(filePath, "utf-8"));
|
|
9443
9675
|
} catch {
|
|
9444
9676
|
return { learnings: [], sessions: [], totals: { ...DEFAULT_TOTALS } };
|
|
9445
9677
|
}
|
|
9446
9678
|
}
|
|
9447
9679
|
function writeROIStats(stats) {
|
|
9448
9680
|
ensureLearningDir();
|
|
9449
|
-
|
|
9681
|
+
fs33.writeFileSync(roiFilePath(), JSON.stringify(stats, null, 2));
|
|
9450
9682
|
}
|
|
9451
9683
|
function recalculateTotals(stats) {
|
|
9452
9684
|
const totals = stats.totals;
|
|
@@ -9699,7 +9931,7 @@ async function learnFinalizeCommand(options) {
|
|
|
9699
9931
|
}
|
|
9700
9932
|
async function learnInstallCommand() {
|
|
9701
9933
|
let anyInstalled = false;
|
|
9702
|
-
if (
|
|
9934
|
+
if (fs34.existsSync(".claude")) {
|
|
9703
9935
|
const r = installLearningHooks();
|
|
9704
9936
|
if (r.installed) {
|
|
9705
9937
|
console.log(chalk19.green("\u2713") + " Claude Code learning hooks installed");
|
|
@@ -9708,7 +9940,7 @@ async function learnInstallCommand() {
|
|
|
9708
9940
|
console.log(chalk19.dim(" Claude Code hooks already installed"));
|
|
9709
9941
|
}
|
|
9710
9942
|
}
|
|
9711
|
-
if (
|
|
9943
|
+
if (fs34.existsSync(".cursor")) {
|
|
9712
9944
|
const r = installCursorLearningHooks();
|
|
9713
9945
|
if (r.installed) {
|
|
9714
9946
|
console.log(chalk19.green("\u2713") + " Cursor learning hooks installed");
|
|
@@ -9717,7 +9949,7 @@ async function learnInstallCommand() {
|
|
|
9717
9949
|
console.log(chalk19.dim(" Cursor hooks already installed"));
|
|
9718
9950
|
}
|
|
9719
9951
|
}
|
|
9720
|
-
if (!
|
|
9952
|
+
if (!fs34.existsSync(".claude") && !fs34.existsSync(".cursor")) {
|
|
9721
9953
|
console.log(chalk19.yellow("No .claude/ or .cursor/ directory found."));
|
|
9722
9954
|
console.log(chalk19.dim(" Run `caliber init` first, or create the directory manually."));
|
|
9723
9955
|
return;
|
|
@@ -9789,9 +10021,9 @@ Learned items in CALIBER_LEARNINGS.md: ${chalk19.cyan(String(lineCount))}`);
|
|
|
9789
10021
|
}
|
|
9790
10022
|
|
|
9791
10023
|
// src/cli.ts
|
|
9792
|
-
var __dirname =
|
|
10024
|
+
var __dirname = path28.dirname(fileURLToPath(import.meta.url));
|
|
9793
10025
|
var pkg = JSON.parse(
|
|
9794
|
-
|
|
10026
|
+
fs35.readFileSync(path28.resolve(__dirname, "..", "package.json"), "utf-8")
|
|
9795
10027
|
);
|
|
9796
10028
|
var program = new Command();
|
|
9797
10029
|
var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
|
|
@@ -9865,16 +10097,16 @@ learn.command("remove").description("Remove learning hooks from .claude/settings
|
|
|
9865
10097
|
learn.command("status").description("Show learning system status").action(tracked("learn:status", learnStatusCommand));
|
|
9866
10098
|
|
|
9867
10099
|
// src/utils/version-check.ts
|
|
9868
|
-
import
|
|
9869
|
-
import
|
|
10100
|
+
import fs36 from "fs";
|
|
10101
|
+
import path29 from "path";
|
|
9870
10102
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9871
|
-
import { execSync as
|
|
10103
|
+
import { execSync as execSync15 } from "child_process";
|
|
9872
10104
|
import chalk20 from "chalk";
|
|
9873
10105
|
import ora7 from "ora";
|
|
9874
10106
|
import confirm2 from "@inquirer/confirm";
|
|
9875
|
-
var __dirname_vc =
|
|
10107
|
+
var __dirname_vc = path29.dirname(fileURLToPath2(import.meta.url));
|
|
9876
10108
|
var pkg2 = JSON.parse(
|
|
9877
|
-
|
|
10109
|
+
fs36.readFileSync(path29.resolve(__dirname_vc, "..", "package.json"), "utf-8")
|
|
9878
10110
|
);
|
|
9879
10111
|
function getChannel(version) {
|
|
9880
10112
|
const match = version.match(/-(dev|next)\./);
|
|
@@ -9898,9 +10130,9 @@ function isNewer(registry, current) {
|
|
|
9898
10130
|
}
|
|
9899
10131
|
function getInstalledVersion() {
|
|
9900
10132
|
try {
|
|
9901
|
-
const globalRoot =
|
|
9902
|
-
const pkgPath =
|
|
9903
|
-
return JSON.parse(
|
|
10133
|
+
const globalRoot = execSync15("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
10134
|
+
const pkgPath = path29.join(globalRoot, "@rely-ai", "caliber", "package.json");
|
|
10135
|
+
return JSON.parse(fs36.readFileSync(pkgPath, "utf-8")).version;
|
|
9904
10136
|
} catch {
|
|
9905
10137
|
return null;
|
|
9906
10138
|
}
|
|
@@ -9946,7 +10178,7 @@ Update available: ${current} -> ${latest}`)
|
|
|
9946
10178
|
const tag = channel === "latest" ? latest : channel;
|
|
9947
10179
|
const spinner = ora7("Updating caliber...").start();
|
|
9948
10180
|
try {
|
|
9949
|
-
|
|
10181
|
+
execSync15(`npm install -g @rely-ai/caliber@${tag}`, {
|
|
9950
10182
|
stdio: "pipe",
|
|
9951
10183
|
timeout: 12e4,
|
|
9952
10184
|
env: { ...process.env, npm_config_fund: "false", npm_config_audit: "false" }
|
|
@@ -9963,7 +10195,7 @@ Update available: ${current} -> ${latest}`)
|
|
|
9963
10195
|
console.log(chalk20.dim(`
|
|
9964
10196
|
Restarting: caliber ${args.join(" ")}
|
|
9965
10197
|
`));
|
|
9966
|
-
|
|
10198
|
+
execSync15(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
|
|
9967
10199
|
stdio: "inherit",
|
|
9968
10200
|
env: { ...process.env, CALIBER_SKIP_UPDATE_CHECK: "1" }
|
|
9969
10201
|
});
|