@rely-ai/caliber 1.37.0 → 1.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +711 -366
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -1099,14 +1099,14 @@ __export(lock_exports, {
|
|
|
1099
1099
|
isCaliberRunning: () => isCaliberRunning,
|
|
1100
1100
|
releaseLock: () => releaseLock
|
|
1101
1101
|
});
|
|
1102
|
-
import
|
|
1103
|
-
import
|
|
1102
|
+
import fs40 from "fs";
|
|
1103
|
+
import path33 from "path";
|
|
1104
1104
|
import os8 from "os";
|
|
1105
1105
|
import crypto5 from "crypto";
|
|
1106
1106
|
function buildLockPath() {
|
|
1107
1107
|
const cwd = process.cwd();
|
|
1108
1108
|
const hash = crypto5.createHash("md5").update(cwd).digest("hex").slice(0, 8);
|
|
1109
|
-
return
|
|
1109
|
+
return path33.join(os8.tmpdir(), `.caliber-${hash}.lock`);
|
|
1110
1110
|
}
|
|
1111
1111
|
function getLockFile() {
|
|
1112
1112
|
if (!_lockPath) _lockPath = buildLockPath();
|
|
@@ -1115,8 +1115,8 @@ function getLockFile() {
|
|
|
1115
1115
|
function isCaliberRunning() {
|
|
1116
1116
|
try {
|
|
1117
1117
|
const lockFile = buildLockPath();
|
|
1118
|
-
if (!
|
|
1119
|
-
const raw =
|
|
1118
|
+
if (!fs40.existsSync(lockFile)) return false;
|
|
1119
|
+
const raw = fs40.readFileSync(lockFile, "utf-8").trim();
|
|
1120
1120
|
const { pid, ts } = JSON.parse(raw);
|
|
1121
1121
|
if (Date.now() - ts > STALE_MS) return false;
|
|
1122
1122
|
try {
|
|
@@ -1131,14 +1131,14 @@ function isCaliberRunning() {
|
|
|
1131
1131
|
}
|
|
1132
1132
|
function acquireLock() {
|
|
1133
1133
|
try {
|
|
1134
|
-
|
|
1134
|
+
fs40.writeFileSync(getLockFile(), JSON.stringify({ pid: process.pid, ts: Date.now() }));
|
|
1135
1135
|
} catch {
|
|
1136
1136
|
}
|
|
1137
1137
|
}
|
|
1138
1138
|
function releaseLock() {
|
|
1139
1139
|
try {
|
|
1140
1140
|
const lockFile = getLockFile();
|
|
1141
|
-
if (
|
|
1141
|
+
if (fs40.existsSync(lockFile)) fs40.unlinkSync(lockFile);
|
|
1142
1142
|
} catch {
|
|
1143
1143
|
}
|
|
1144
1144
|
}
|
|
@@ -1153,8 +1153,8 @@ var init_lock = __esm({
|
|
|
1153
1153
|
|
|
1154
1154
|
// src/cli.ts
|
|
1155
1155
|
import { Command } from "commander";
|
|
1156
|
-
import
|
|
1157
|
-
import
|
|
1156
|
+
import fs51 from "fs";
|
|
1157
|
+
import path42 from "path";
|
|
1158
1158
|
import { fileURLToPath } from "url";
|
|
1159
1159
|
|
|
1160
1160
|
// src/commands/init.ts
|
|
@@ -2352,6 +2352,55 @@ function isRateLimitError(stderr) {
|
|
|
2352
2352
|
return /rate limit|too many requests|429/i.test(stderr);
|
|
2353
2353
|
}
|
|
2354
2354
|
|
|
2355
|
+
// src/llm/utils.ts
|
|
2356
|
+
function extractJson(text) {
|
|
2357
|
+
const startIdx = text.search(/[[{]/);
|
|
2358
|
+
if (startIdx === -1) return null;
|
|
2359
|
+
let depth = 0;
|
|
2360
|
+
let inString = false;
|
|
2361
|
+
let escaped = false;
|
|
2362
|
+
for (let i = startIdx; i < text.length; i++) {
|
|
2363
|
+
const ch = text[i];
|
|
2364
|
+
if (escaped) {
|
|
2365
|
+
escaped = false;
|
|
2366
|
+
continue;
|
|
2367
|
+
}
|
|
2368
|
+
if (ch === "\\" && inString) {
|
|
2369
|
+
escaped = true;
|
|
2370
|
+
continue;
|
|
2371
|
+
}
|
|
2372
|
+
if (ch === '"') {
|
|
2373
|
+
inString = !inString;
|
|
2374
|
+
continue;
|
|
2375
|
+
}
|
|
2376
|
+
if (inString) continue;
|
|
2377
|
+
if (ch === "{" || ch === "[") depth++;
|
|
2378
|
+
if (ch === "}" || ch === "]") {
|
|
2379
|
+
depth--;
|
|
2380
|
+
if (depth === 0) return text.slice(startIdx, i + 1);
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
return null;
|
|
2384
|
+
}
|
|
2385
|
+
function stripMarkdownFences(text) {
|
|
2386
|
+
return text.replace(/^```(?:json)?\s*/im, "").replace(/```\s*$/m, "").trim();
|
|
2387
|
+
}
|
|
2388
|
+
function parseJsonResponse(raw) {
|
|
2389
|
+
const cleaned = stripMarkdownFences(raw);
|
|
2390
|
+
try {
|
|
2391
|
+
return JSON.parse(cleaned);
|
|
2392
|
+
} catch {
|
|
2393
|
+
}
|
|
2394
|
+
const json = extractJson(cleaned);
|
|
2395
|
+
if (!json) {
|
|
2396
|
+
throw new Error(`No JSON found in LLM response: ${raw.slice(0, 200)}`);
|
|
2397
|
+
}
|
|
2398
|
+
return JSON.parse(json);
|
|
2399
|
+
}
|
|
2400
|
+
function estimateTokens(text) {
|
|
2401
|
+
return Math.ceil(text.length / 4);
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2355
2404
|
// src/llm/cursor-acp.ts
|
|
2356
2405
|
var AGENT_BIN = "agent";
|
|
2357
2406
|
var IS_WINDOWS = process.platform === "win32";
|
|
@@ -2376,12 +2425,33 @@ var CursorAcpProvider = class {
|
|
|
2376
2425
|
async call(options) {
|
|
2377
2426
|
const prompt = this.buildPrompt(options);
|
|
2378
2427
|
const model = options.model || this.defaultModel;
|
|
2379
|
-
|
|
2428
|
+
const result = await this.runPrint(model, prompt);
|
|
2429
|
+
trackUsage(model, {
|
|
2430
|
+
inputTokens: estimateTokens(prompt),
|
|
2431
|
+
outputTokens: estimateTokens(result)
|
|
2432
|
+
});
|
|
2433
|
+
return result;
|
|
2380
2434
|
}
|
|
2381
2435
|
async stream(options, callbacks) {
|
|
2382
2436
|
const prompt = this.buildPrompt(options);
|
|
2383
2437
|
const model = options.model || this.defaultModel;
|
|
2384
|
-
|
|
2438
|
+
const inputEstimate = estimateTokens(prompt);
|
|
2439
|
+
let outputChars = 0;
|
|
2440
|
+
const wrappedCallbacks = {
|
|
2441
|
+
onText: (text) => {
|
|
2442
|
+
outputChars += text.length;
|
|
2443
|
+
callbacks.onText(text);
|
|
2444
|
+
},
|
|
2445
|
+
onEnd: (meta) => {
|
|
2446
|
+
trackUsage(model, {
|
|
2447
|
+
inputTokens: inputEstimate,
|
|
2448
|
+
outputTokens: Math.ceil(outputChars / 4)
|
|
2449
|
+
});
|
|
2450
|
+
callbacks.onEnd(meta);
|
|
2451
|
+
},
|
|
2452
|
+
onError: (err) => callbacks.onError(err)
|
|
2453
|
+
};
|
|
2454
|
+
return this.runPrintStream(model, prompt, wrappedCallbacks);
|
|
2385
2455
|
}
|
|
2386
2456
|
/**
|
|
2387
2457
|
* Pre-spawn an agent process so it's ready when the first call comes.
|
|
@@ -2473,7 +2543,11 @@ var CursorAcpProvider = class {
|
|
|
2473
2543
|
this.killWithEscalation(child);
|
|
2474
2544
|
if (!settled) {
|
|
2475
2545
|
settled = true;
|
|
2476
|
-
reject(
|
|
2546
|
+
reject(
|
|
2547
|
+
new Error(
|
|
2548
|
+
`Cursor agent timed out after ${this.timeoutMs / 1e3}s. Set CALIBER_CURSOR_TIMEOUT_MS to increase.`
|
|
2549
|
+
)
|
|
2550
|
+
);
|
|
2477
2551
|
}
|
|
2478
2552
|
}, this.timeoutMs);
|
|
2479
2553
|
timer.unref();
|
|
@@ -2509,7 +2583,9 @@ var CursorAcpProvider = class {
|
|
|
2509
2583
|
this.killWithEscalation(child);
|
|
2510
2584
|
if (!settled) {
|
|
2511
2585
|
settled = true;
|
|
2512
|
-
const err = new Error(
|
|
2586
|
+
const err = new Error(
|
|
2587
|
+
`Cursor agent timed out after ${this.timeoutMs / 1e3}s. Set CALIBER_CURSOR_TIMEOUT_MS to increase.`
|
|
2588
|
+
);
|
|
2513
2589
|
callbacks.onError(err);
|
|
2514
2590
|
reject(err);
|
|
2515
2591
|
}
|
|
@@ -2616,7 +2692,10 @@ function isCursorAgentAvailable() {
|
|
|
2616
2692
|
}
|
|
2617
2693
|
function isCursorLoggedIn() {
|
|
2618
2694
|
try {
|
|
2619
|
-
const result = execSync5(`${AGENT_BIN} status`, {
|
|
2695
|
+
const result = execSync5(`${AGENT_BIN} status`, {
|
|
2696
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
2697
|
+
timeout: 5e3
|
|
2698
|
+
});
|
|
2620
2699
|
return !result.toString().includes("not logged in");
|
|
2621
2700
|
} catch {
|
|
2622
2701
|
return false;
|
|
@@ -2653,20 +2732,29 @@ var ClaudeCliProvider = class {
|
|
|
2653
2732
|
}
|
|
2654
2733
|
async call(options) {
|
|
2655
2734
|
const combined = this.buildCombinedPrompt(options);
|
|
2656
|
-
|
|
2735
|
+
const result = await this.runClaudePrint(combined, options.model);
|
|
2736
|
+
trackUsage(options.model || this.defaultModel, {
|
|
2737
|
+
inputTokens: estimateTokens(combined),
|
|
2738
|
+
outputTokens: estimateTokens(result)
|
|
2739
|
+
});
|
|
2740
|
+
return result;
|
|
2657
2741
|
}
|
|
2658
2742
|
async stream(options, callbacks) {
|
|
2659
2743
|
const combined = this.buildCombinedPrompt(options);
|
|
2744
|
+
const inputEstimate = estimateTokens(combined);
|
|
2660
2745
|
const args = ["-p"];
|
|
2661
2746
|
if (options.model) args.push("--model", options.model);
|
|
2662
2747
|
const child = spawnClaude(args);
|
|
2663
2748
|
child.stdin.end(combined);
|
|
2664
2749
|
let settled = false;
|
|
2750
|
+
let outputChars = 0;
|
|
2665
2751
|
const chunks = [];
|
|
2666
2752
|
const stderrChunks = [];
|
|
2667
2753
|
child.stdout.on("data", (chunk) => {
|
|
2668
2754
|
chunks.push(chunk);
|
|
2669
|
-
|
|
2755
|
+
const text = chunk.toString("utf-8");
|
|
2756
|
+
outputChars += text.length;
|
|
2757
|
+
callbacks.onText(text);
|
|
2670
2758
|
});
|
|
2671
2759
|
child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
|
|
2672
2760
|
const timer = setTimeout(() => {
|
|
@@ -2692,6 +2780,11 @@ var ClaudeCliProvider = class {
|
|
|
2692
2780
|
if (settled) return;
|
|
2693
2781
|
settled = true;
|
|
2694
2782
|
if (code === 0) {
|
|
2783
|
+
const model = options.model || this.defaultModel;
|
|
2784
|
+
trackUsage(model, {
|
|
2785
|
+
inputTokens: inputEstimate,
|
|
2786
|
+
outputTokens: Math.ceil(outputChars / 4)
|
|
2787
|
+
});
|
|
2695
2788
|
callbacks.onEnd({ stopReason: "end_turn" });
|
|
2696
2789
|
} else {
|
|
2697
2790
|
const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
|
|
@@ -2766,55 +2859,6 @@ function isClaudeCliAvailable() {
|
|
|
2766
2859
|
}
|
|
2767
2860
|
}
|
|
2768
2861
|
|
|
2769
|
-
// src/llm/utils.ts
|
|
2770
|
-
function extractJson(text) {
|
|
2771
|
-
const startIdx = text.search(/[[{]/);
|
|
2772
|
-
if (startIdx === -1) return null;
|
|
2773
|
-
let depth = 0;
|
|
2774
|
-
let inString = false;
|
|
2775
|
-
let escaped = false;
|
|
2776
|
-
for (let i = startIdx; i < text.length; i++) {
|
|
2777
|
-
const ch = text[i];
|
|
2778
|
-
if (escaped) {
|
|
2779
|
-
escaped = false;
|
|
2780
|
-
continue;
|
|
2781
|
-
}
|
|
2782
|
-
if (ch === "\\" && inString) {
|
|
2783
|
-
escaped = true;
|
|
2784
|
-
continue;
|
|
2785
|
-
}
|
|
2786
|
-
if (ch === '"') {
|
|
2787
|
-
inString = !inString;
|
|
2788
|
-
continue;
|
|
2789
|
-
}
|
|
2790
|
-
if (inString) continue;
|
|
2791
|
-
if (ch === "{" || ch === "[") depth++;
|
|
2792
|
-
if (ch === "}" || ch === "]") {
|
|
2793
|
-
depth--;
|
|
2794
|
-
if (depth === 0) return text.slice(startIdx, i + 1);
|
|
2795
|
-
}
|
|
2796
|
-
}
|
|
2797
|
-
return null;
|
|
2798
|
-
}
|
|
2799
|
-
function stripMarkdownFences(text) {
|
|
2800
|
-
return text.replace(/^```(?:json)?\s*/im, "").replace(/```\s*$/m, "").trim();
|
|
2801
|
-
}
|
|
2802
|
-
function parseJsonResponse(raw) {
|
|
2803
|
-
const cleaned = stripMarkdownFences(raw);
|
|
2804
|
-
try {
|
|
2805
|
-
return JSON.parse(cleaned);
|
|
2806
|
-
} catch {
|
|
2807
|
-
}
|
|
2808
|
-
const json = extractJson(cleaned);
|
|
2809
|
-
if (!json) {
|
|
2810
|
-
throw new Error(`No JSON found in LLM response: ${raw.slice(0, 200)}`);
|
|
2811
|
-
}
|
|
2812
|
-
return JSON.parse(json);
|
|
2813
|
-
}
|
|
2814
|
-
function estimateTokens(text) {
|
|
2815
|
-
return Math.ceil(text.length / 4);
|
|
2816
|
-
}
|
|
2817
|
-
|
|
2818
2862
|
// src/llm/model-recovery.ts
|
|
2819
2863
|
init_config();
|
|
2820
2864
|
init_resolve_caliber();
|
|
@@ -3651,7 +3695,7 @@ function getDetectedWorkspaces(dir) {
|
|
|
3651
3695
|
|
|
3652
3696
|
// src/fingerprint/index.ts
|
|
3653
3697
|
async function collectFingerprint(dir) {
|
|
3654
|
-
const gitRemoteUrl = getGitRemoteUrl();
|
|
3698
|
+
const gitRemoteUrl = getGitRemoteUrl(dir);
|
|
3655
3699
|
const fileTree = getFileTree(dir);
|
|
3656
3700
|
const existingConfigs = readExistingConfigs(dir);
|
|
3657
3701
|
const packageName = readPackageName(dir);
|
|
@@ -4654,15 +4698,15 @@ init_config();
|
|
|
4654
4698
|
// src/utils/dependencies.ts
|
|
4655
4699
|
import { readFileSync as readFileSync2 } from "fs";
|
|
4656
4700
|
import { join as join2 } from "path";
|
|
4657
|
-
function readFileOrNull2(
|
|
4701
|
+
function readFileOrNull2(path44) {
|
|
4658
4702
|
try {
|
|
4659
|
-
return readFileSync2(
|
|
4703
|
+
return readFileSync2(path44, "utf-8");
|
|
4660
4704
|
} catch {
|
|
4661
4705
|
return null;
|
|
4662
4706
|
}
|
|
4663
4707
|
}
|
|
4664
|
-
function readJsonOrNull(
|
|
4665
|
-
const content = readFileOrNull2(
|
|
4708
|
+
function readJsonOrNull(path44) {
|
|
4709
|
+
const content = readFileOrNull2(path44);
|
|
4666
4710
|
if (!content) return null;
|
|
4667
4711
|
try {
|
|
4668
4712
|
return JSON.parse(content);
|
|
@@ -4761,9 +4805,25 @@ function isTransientError2(error) {
|
|
|
4761
4805
|
async function generateSetup(fingerprint, targetAgent, prompt, callbacks, failingChecks, currentScore, passingChecks, options) {
|
|
4762
4806
|
const isTargetedFix = failingChecks && failingChecks.length > 0 && currentScore !== void 0 && currentScore >= 95 || options?.forceTargetedFix;
|
|
4763
4807
|
if (isTargetedFix) {
|
|
4764
|
-
return generateMonolithic(
|
|
4808
|
+
return generateMonolithic(
|
|
4809
|
+
fingerprint,
|
|
4810
|
+
targetAgent,
|
|
4811
|
+
prompt,
|
|
4812
|
+
callbacks,
|
|
4813
|
+
failingChecks,
|
|
4814
|
+
currentScore,
|
|
4815
|
+
passingChecks
|
|
4816
|
+
);
|
|
4765
4817
|
}
|
|
4766
|
-
const coreResult = await generateCore(
|
|
4818
|
+
const coreResult = await generateCore(
|
|
4819
|
+
fingerprint,
|
|
4820
|
+
targetAgent,
|
|
4821
|
+
prompt,
|
|
4822
|
+
callbacks,
|
|
4823
|
+
failingChecks,
|
|
4824
|
+
currentScore,
|
|
4825
|
+
passingChecks
|
|
4826
|
+
);
|
|
4767
4827
|
if (!coreResult.setup) {
|
|
4768
4828
|
return coreResult;
|
|
4769
4829
|
}
|
|
@@ -4812,6 +4872,7 @@ function mergeSkillResults(results, setup) {
|
|
|
4812
4872
|
}
|
|
4813
4873
|
return { succeeded, failed };
|
|
4814
4874
|
}
|
|
4875
|
+
var MAX_SKILL_TOPICS = 5;
|
|
4815
4876
|
function collectSkillTopics(setup, targetAgent, fingerprint) {
|
|
4816
4877
|
const topics = [];
|
|
4817
4878
|
for (const platform of ["claude", "codex", "opencode", "cursor"]) {
|
|
@@ -4832,12 +4893,18 @@ function collectSkillTopics(setup, targetAgent, fingerprint) {
|
|
|
4832
4893
|
delete platformObj.skillTopics;
|
|
4833
4894
|
}
|
|
4834
4895
|
}
|
|
4835
|
-
return topics;
|
|
4896
|
+
return topics.slice(0, MAX_SKILL_TOPICS);
|
|
4836
4897
|
}
|
|
4837
4898
|
function getDefaultSkillTopics(fingerprint) {
|
|
4838
4899
|
const topics = [
|
|
4839
|
-
{
|
|
4840
|
-
|
|
4900
|
+
{
|
|
4901
|
+
name: "development-workflow",
|
|
4902
|
+
description: "Development setup and common workflows. Use when starting development, running the project, or setting up the environment."
|
|
4903
|
+
},
|
|
4904
|
+
{
|
|
4905
|
+
name: "testing-guide",
|
|
4906
|
+
description: "Testing patterns and commands. Use when writing tests, running test suites, or debugging test failures."
|
|
4907
|
+
}
|
|
4841
4908
|
];
|
|
4842
4909
|
if (fingerprint.frameworks.length > 0) {
|
|
4843
4910
|
topics.push({
|
|
@@ -4855,8 +4922,10 @@ function getDefaultSkillTopics(fingerprint) {
|
|
|
4855
4922
|
function buildSkillContext(fingerprint, setup, allDeps) {
|
|
4856
4923
|
const parts = [];
|
|
4857
4924
|
if (fingerprint.packageName) parts.push(`Project: ${fingerprint.packageName}`);
|
|
4858
|
-
if (fingerprint.languages.length > 0)
|
|
4859
|
-
|
|
4925
|
+
if (fingerprint.languages.length > 0)
|
|
4926
|
+
parts.push(`Languages: ${fingerprint.languages.join(", ")}`);
|
|
4927
|
+
if (fingerprint.frameworks.length > 0)
|
|
4928
|
+
parts.push(`Frameworks: ${fingerprint.frameworks.join(", ")}`);
|
|
4860
4929
|
const claude = setup.claude;
|
|
4861
4930
|
const claudeMd = claude?.claudeMd;
|
|
4862
4931
|
if (claudeMd) {
|
|
@@ -4943,7 +5012,9 @@ async function streamGeneration(config) {
|
|
|
4943
5012
|
}
|
|
4944
5013
|
}
|
|
4945
5014
|
sentStatuses = completedLines.length;
|
|
4946
|
-
const jsonStartMatch = preJsonBuffer.match(
|
|
5015
|
+
const jsonStartMatch = preJsonBuffer.match(
|
|
5016
|
+
/(?:^|\n)\s*(?:```json\s*\n\s*)?\{(?=\s*")/
|
|
5017
|
+
);
|
|
4947
5018
|
if (jsonStartMatch) {
|
|
4948
5019
|
const matchIndex = preJsonBuffer.indexOf("{", jsonStartMatch.index);
|
|
4949
5020
|
inJson = true;
|
|
@@ -4958,7 +5029,9 @@ async function streamGeneration(config) {
|
|
|
4958
5029
|
let setup = null;
|
|
4959
5030
|
let jsonToParse = (jsonContent || preJsonBuffer).replace(/```\s*$/g, "").trim();
|
|
4960
5031
|
if (!jsonContent && preJsonBuffer) {
|
|
4961
|
-
const fallbackMatch = preJsonBuffer.match(
|
|
5032
|
+
const fallbackMatch = preJsonBuffer.match(
|
|
5033
|
+
/(?:^|\n)\s*(?:```json\s*\n\s*)?\{(?=\s*")/
|
|
5034
|
+
);
|
|
4962
5035
|
if (fallbackMatch) {
|
|
4963
5036
|
const matchIndex = preJsonBuffer.indexOf("{", fallbackMatch.index);
|
|
4964
5037
|
jsonToParse = preJsonBuffer.slice(matchIndex).replace(/```\s*$/g, "").trim();
|
|
@@ -4969,7 +5042,10 @@ async function streamGeneration(config) {
|
|
|
4969
5042
|
} catch {
|
|
4970
5043
|
}
|
|
4971
5044
|
if (!setup && stopReason === "max_tokens" && attempt < MAX_RETRIES2) {
|
|
4972
|
-
if (config.callbacks)
|
|
5045
|
+
if (config.callbacks)
|
|
5046
|
+
config.callbacks.onStatus(
|
|
5047
|
+
"Output was truncated, retrying with higher token limit..."
|
|
5048
|
+
);
|
|
4973
5049
|
setTimeout(() => attemptGeneration().then(resolve3), 1e3);
|
|
4974
5050
|
return;
|
|
4975
5051
|
}
|
|
@@ -4982,12 +5058,18 @@ async function streamGeneration(config) {
|
|
|
4982
5058
|
if (config.callbacks) config.callbacks.onComplete(setup, explanation);
|
|
4983
5059
|
resolve3({ setup, explanation, stopReason: stopReason ?? void 0 });
|
|
4984
5060
|
} else {
|
|
4985
|
-
resolve3({
|
|
5061
|
+
resolve3({
|
|
5062
|
+
setup: null,
|
|
5063
|
+
explanation,
|
|
5064
|
+
raw: preJsonBuffer,
|
|
5065
|
+
stopReason: stopReason ?? void 0
|
|
5066
|
+
});
|
|
4986
5067
|
}
|
|
4987
5068
|
},
|
|
4988
5069
|
onError: (error) => {
|
|
4989
5070
|
if (isTransientError2(error) && attempt < MAX_RETRIES2) {
|
|
4990
|
-
if (config.callbacks)
|
|
5071
|
+
if (config.callbacks)
|
|
5072
|
+
config.callbacks.onStatus("Connection interrupted, retrying...");
|
|
4991
5073
|
setTimeout(() => attemptGeneration().then(resolve3), 2e3);
|
|
4992
5074
|
return;
|
|
4993
5075
|
}
|
|
@@ -5004,7 +5086,14 @@ async function streamGeneration(config) {
|
|
|
5004
5086
|
return attemptGeneration();
|
|
5005
5087
|
}
|
|
5006
5088
|
async function generateCore(fingerprint, targetAgent, prompt, callbacks, failingChecks, currentScore, passingChecks) {
|
|
5007
|
-
const userMessage = buildGeneratePrompt(
|
|
5089
|
+
const userMessage = buildGeneratePrompt(
|
|
5090
|
+
fingerprint,
|
|
5091
|
+
targetAgent,
|
|
5092
|
+
prompt,
|
|
5093
|
+
failingChecks,
|
|
5094
|
+
currentScore,
|
|
5095
|
+
passingChecks
|
|
5096
|
+
);
|
|
5008
5097
|
return streamGeneration({
|
|
5009
5098
|
systemPrompt: CORE_GENERATION_PROMPT,
|
|
5010
5099
|
userMessage,
|
|
@@ -5015,7 +5104,14 @@ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failing
|
|
|
5015
5104
|
});
|
|
5016
5105
|
}
|
|
5017
5106
|
async function generateMonolithic(fingerprint, targetAgent, prompt, callbacks, failingChecks, currentScore, passingChecks) {
|
|
5018
|
-
const userMessage = buildGeneratePrompt(
|
|
5107
|
+
const userMessage = buildGeneratePrompt(
|
|
5108
|
+
fingerprint,
|
|
5109
|
+
targetAgent,
|
|
5110
|
+
prompt,
|
|
5111
|
+
failingChecks,
|
|
5112
|
+
currentScore,
|
|
5113
|
+
passingChecks
|
|
5114
|
+
);
|
|
5019
5115
|
return streamGeneration({
|
|
5020
5116
|
systemPrompt: GENERATION_SYSTEM_PROMPT,
|
|
5021
5117
|
userMessage,
|
|
@@ -5096,9 +5192,11 @@ function buildGeneratePrompt(fingerprint, targetAgent, prompt, failingChecks, cu
|
|
|
5096
5192
|
const isTargetedFix = failingChecks && failingChecks.length > 0 && currentScore !== void 0 && currentScore >= 95;
|
|
5097
5193
|
if (isTargetedFix) {
|
|
5098
5194
|
parts.push(`TARGETED FIX MODE \u2014 current score: ${currentScore}/100, target: ${targetAgent}`);
|
|
5099
|
-
parts.push(
|
|
5195
|
+
parts.push(
|
|
5196
|
+
`
|
|
5100
5197
|
The existing config is already high quality. ONLY fix these specific failing checks:
|
|
5101
|
-
`
|
|
5198
|
+
`
|
|
5199
|
+
);
|
|
5102
5200
|
for (const check of failingChecks) {
|
|
5103
5201
|
if (check.fix) {
|
|
5104
5202
|
parts.push(`- **${check.name}**`);
|
|
@@ -5129,15 +5227,19 @@ IMPORTANT RULES FOR TARGETED FIX:
|
|
|
5129
5227
|
- For grounding issues: Add references to the listed project directories in the appropriate sections.
|
|
5130
5228
|
- Every path or name you reference MUST exist in the project \u2014 use the file tree provided below.`);
|
|
5131
5229
|
} else if (hasExistingConfigs) {
|
|
5132
|
-
parts.push(
|
|
5230
|
+
parts.push(
|
|
5231
|
+
`Audit and improve the existing coding agent configuration for target: ${targetAgent}`
|
|
5232
|
+
);
|
|
5133
5233
|
} else {
|
|
5134
5234
|
parts.push(`Generate an initial coding agent configuration for target: ${targetAgent}`);
|
|
5135
5235
|
}
|
|
5136
5236
|
if (fingerprint.gitRemoteUrl) parts.push(`
|
|
5137
5237
|
Git remote: ${fingerprint.gitRemoteUrl}`);
|
|
5138
5238
|
if (fingerprint.packageName) parts.push(`Package name: ${fingerprint.packageName}`);
|
|
5139
|
-
if (fingerprint.languages.length > 0)
|
|
5140
|
-
|
|
5239
|
+
if (fingerprint.languages.length > 0)
|
|
5240
|
+
parts.push(`Languages: ${fingerprint.languages.join(", ")}`);
|
|
5241
|
+
if (fingerprint.frameworks.length > 0)
|
|
5242
|
+
parts.push(`Frameworks: ${fingerprint.frameworks.join(", ")}`);
|
|
5141
5243
|
if (fingerprint.description) parts.push(`Project description: ${fingerprint.description}`);
|
|
5142
5244
|
if (fingerprint.fileTree.length > 0) {
|
|
5143
5245
|
const caPaths = fingerprint.codeAnalysis?.files.map((f) => f.path) ?? [];
|
|
@@ -5146,36 +5248,52 @@ Git remote: ${fingerprint.gitRemoteUrl}`);
|
|
|
5146
5248
|
File tree (${tree.length}/${fingerprint.fileTree.length}):
|
|
5147
5249
|
${tree.join("\n")}`);
|
|
5148
5250
|
}
|
|
5149
|
-
if (existing.claudeMd)
|
|
5251
|
+
if (existing.claudeMd)
|
|
5252
|
+
parts.push(
|
|
5253
|
+
`
|
|
5150
5254
|
Existing CLAUDE.md:
|
|
5151
|
-
${truncate(existing.claudeMd, LIMITS.EXISTING_CONFIG_CHARS)}`
|
|
5152
|
-
|
|
5255
|
+
${truncate(existing.claudeMd, LIMITS.EXISTING_CONFIG_CHARS)}`
|
|
5256
|
+
);
|
|
5257
|
+
if (existing.agentsMd)
|
|
5258
|
+
parts.push(
|
|
5259
|
+
`
|
|
5153
5260
|
Existing AGENTS.md:
|
|
5154
|
-
${truncate(existing.agentsMd, LIMITS.EXISTING_CONFIG_CHARS)}`
|
|
5155
|
-
|
|
5261
|
+
${truncate(existing.agentsMd, LIMITS.EXISTING_CONFIG_CHARS)}`
|
|
5262
|
+
);
|
|
5263
|
+
if (existing.readmeMd)
|
|
5264
|
+
parts.push(
|
|
5265
|
+
`
|
|
5156
5266
|
Existing README.md:
|
|
5157
|
-
${truncate(existing.readmeMd, LIMITS.EXISTING_CONFIG_CHARS)}`
|
|
5267
|
+
${truncate(existing.readmeMd, LIMITS.EXISTING_CONFIG_CHARS)}`
|
|
5268
|
+
);
|
|
5158
5269
|
if (existing.claudeSkills?.length) {
|
|
5159
5270
|
parts.push("\n--- Existing Claude Skills ---");
|
|
5160
5271
|
for (const skill of existing.claudeSkills.slice(0, LIMITS.SKILLS_MAX)) {
|
|
5161
|
-
parts.push(
|
|
5272
|
+
parts.push(
|
|
5273
|
+
`
|
|
5162
5274
|
[.claude/skills/${skill.filename}]
|
|
5163
|
-
${truncate(skill.content, LIMITS.SKILL_CHARS)}`
|
|
5275
|
+
${truncate(skill.content, LIMITS.SKILL_CHARS)}`
|
|
5276
|
+
);
|
|
5164
5277
|
}
|
|
5165
5278
|
if (existing.claudeSkills.length > LIMITS.SKILLS_MAX) {
|
|
5166
5279
|
parts.push(`
|
|
5167
5280
|
(${existing.claudeSkills.length - LIMITS.SKILLS_MAX} more skills omitted)`);
|
|
5168
5281
|
}
|
|
5169
5282
|
}
|
|
5170
|
-
if (existing.cursorrules)
|
|
5283
|
+
if (existing.cursorrules)
|
|
5284
|
+
parts.push(
|
|
5285
|
+
`
|
|
5171
5286
|
Existing .cursorrules:
|
|
5172
|
-
${truncate(existing.cursorrules, LIMITS.EXISTING_CONFIG_CHARS)}`
|
|
5287
|
+
${truncate(existing.cursorrules, LIMITS.EXISTING_CONFIG_CHARS)}`
|
|
5288
|
+
);
|
|
5173
5289
|
if (existing.cursorRules?.length) {
|
|
5174
5290
|
parts.push("\n--- Existing Cursor Rules ---");
|
|
5175
5291
|
for (const rule of existing.cursorRules.slice(0, LIMITS.RULES_MAX)) {
|
|
5176
|
-
parts.push(
|
|
5292
|
+
parts.push(
|
|
5293
|
+
`
|
|
5177
5294
|
[.cursor/rules/${rule.filename}]
|
|
5178
|
-
${truncate(rule.content, LIMITS.SKILL_CHARS)}`
|
|
5295
|
+
${truncate(rule.content, LIMITS.SKILL_CHARS)}`
|
|
5296
|
+
);
|
|
5179
5297
|
}
|
|
5180
5298
|
if (existing.cursorRules.length > LIMITS.RULES_MAX) {
|
|
5181
5299
|
parts.push(`
|
|
@@ -5185,9 +5303,11 @@ ${truncate(rule.content, LIMITS.SKILL_CHARS)}`);
|
|
|
5185
5303
|
if (existing.cursorSkills?.length) {
|
|
5186
5304
|
parts.push("\n--- Existing Cursor Skills ---");
|
|
5187
5305
|
for (const skill of existing.cursorSkills.slice(0, LIMITS.SKILLS_MAX)) {
|
|
5188
|
-
parts.push(
|
|
5306
|
+
parts.push(
|
|
5307
|
+
`
|
|
5189
5308
|
[.cursor/skills/${skill.name}/SKILL.md]
|
|
5190
|
-
${truncate(skill.content, LIMITS.SKILL_CHARS)}`
|
|
5309
|
+
${truncate(skill.content, LIMITS.SKILL_CHARS)}`
|
|
5310
|
+
);
|
|
5191
5311
|
}
|
|
5192
5312
|
if (existing.cursorSkills.length > LIMITS.SKILLS_MAX) {
|
|
5193
5313
|
parts.push(`
|
|
@@ -5195,9 +5315,11 @@ ${truncate(skill.content, LIMITS.SKILL_CHARS)}`);
|
|
|
5195
5315
|
}
|
|
5196
5316
|
}
|
|
5197
5317
|
if (existing.personalLearnings) {
|
|
5198
|
-
parts.push(
|
|
5318
|
+
parts.push(
|
|
5319
|
+
`
|
|
5199
5320
|
--- Personal Learnings (developer-specific, include in generated configs) ---
|
|
5200
|
-
${existing.personalLearnings}`
|
|
5321
|
+
${existing.personalLearnings}`
|
|
5322
|
+
);
|
|
5201
5323
|
}
|
|
5202
5324
|
const allDeps = extractAllDeps(process.cwd());
|
|
5203
5325
|
if (allDeps.length > 0) {
|
|
@@ -5263,7 +5385,10 @@ import fs12 from "fs";
|
|
|
5263
5385
|
import path12 from "path";
|
|
5264
5386
|
function writeClaudeConfig(config) {
|
|
5265
5387
|
const written = [];
|
|
5266
|
-
fs12.writeFileSync(
|
|
5388
|
+
fs12.writeFileSync(
|
|
5389
|
+
"CLAUDE.md",
|
|
5390
|
+
appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(config.claudeMd)))
|
|
5391
|
+
);
|
|
5267
5392
|
written.push("CLAUDE.md");
|
|
5268
5393
|
if (config.skills?.length) {
|
|
5269
5394
|
for (const skill of config.skills) {
|
|
@@ -7501,8 +7626,8 @@ function trackInitSkillsSearch(searched, installedCount) {
|
|
|
7501
7626
|
function trackInitScoreRegression(oldScore, newScore) {
|
|
7502
7627
|
trackEvent("init_score_regression", { old_score: oldScore, new_score: newScore });
|
|
7503
7628
|
}
|
|
7504
|
-
function trackInitCompleted(
|
|
7505
|
-
trackEvent("init_completed", { path:
|
|
7629
|
+
function trackInitCompleted(path44, score) {
|
|
7630
|
+
trackEvent("init_completed", { path: path44, score });
|
|
7506
7631
|
}
|
|
7507
7632
|
function trackRegenerateCompleted(action, durationMs) {
|
|
7508
7633
|
trackEvent("regenerate_completed", { action, duration_ms: durationMs });
|
|
@@ -7864,10 +7989,13 @@ async function searchSkills(fingerprint, targetPlatforms, onStatus) {
|
|
|
7864
7989
|
if (!newCandidates.length) {
|
|
7865
7990
|
return { results: [], contentMap: /* @__PURE__ */ new Map() };
|
|
7866
7991
|
}
|
|
7867
|
-
onStatus?.(`Scoring ${newCandidates.length} candidates...`);
|
|
7868
7992
|
let results;
|
|
7869
7993
|
const config = loadConfig();
|
|
7870
|
-
if (
|
|
7994
|
+
if (newCandidates.length <= 5) {
|
|
7995
|
+
onStatus?.("Few candidates \u2014 skipping scoring");
|
|
7996
|
+
results = newCandidates.map((c) => ({ ...c, score: 70 }));
|
|
7997
|
+
} else if (config) {
|
|
7998
|
+
onStatus?.(`Scoring ${newCandidates.length} candidates...`);
|
|
7871
7999
|
try {
|
|
7872
8000
|
const projectContext = buildProjectContext(fingerprint, targetPlatforms);
|
|
7873
8001
|
results = await scoreWithLLM(newCandidates, projectContext, technologies);
|
|
@@ -8286,7 +8414,8 @@ function printSkills(recs) {
|
|
|
8286
8414
|
// src/ai/score-refine.ts
|
|
8287
8415
|
import { existsSync as existsSync9 } from "fs";
|
|
8288
8416
|
import ora2 from "ora";
|
|
8289
|
-
var MAX_REFINE_ITERATIONS =
|
|
8417
|
+
var MAX_REFINE_ITERATIONS = 1;
|
|
8418
|
+
var MIN_POINTS_TO_REFINE = 4;
|
|
8290
8419
|
function extractConfigContent(setup) {
|
|
8291
8420
|
const claude = setup.claude;
|
|
8292
8421
|
const codex = setup.codex;
|
|
@@ -8296,7 +8425,11 @@ function extractConfigContent(setup) {
|
|
|
8296
8425
|
cursorrules = cursor.cursorrules;
|
|
8297
8426
|
}
|
|
8298
8427
|
const skills = [];
|
|
8299
|
-
for (const [platform, obj] of [
|
|
8428
|
+
for (const [platform, obj] of [
|
|
8429
|
+
["claude", claude],
|
|
8430
|
+
["codex", codex],
|
|
8431
|
+
["cursor", cursor]
|
|
8432
|
+
]) {
|
|
8300
8433
|
const platformSkills = obj?.skills;
|
|
8301
8434
|
if (Array.isArray(platformSkills)) {
|
|
8302
8435
|
for (const skill of platformSkills) {
|
|
@@ -8315,8 +8448,12 @@ function extractConfigContent(setup) {
|
|
|
8315
8448
|
}
|
|
8316
8449
|
function buildGroundingFixInstruction(unmentionedTopDirs, projectStructure) {
|
|
8317
8450
|
const dirDescriptions = unmentionedTopDirs.slice(0, 8).map((dir) => {
|
|
8318
|
-
const subdirs = projectStructure.dirs.filter(
|
|
8319
|
-
|
|
8451
|
+
const subdirs = projectStructure.dirs.filter(
|
|
8452
|
+
(d) => d.startsWith(`${dir}/`) && !d.includes("/", dir.length + 1)
|
|
8453
|
+
);
|
|
8454
|
+
const files = projectStructure.files.filter(
|
|
8455
|
+
(f) => f.startsWith(`${dir}/`) && !f.includes("/", dir.length + 1)
|
|
8456
|
+
);
|
|
8320
8457
|
const children = [...subdirs.slice(0, 4), ...files.slice(0, 2)];
|
|
8321
8458
|
const childList = children.map((c) => c.split("/").pop()).join(", ");
|
|
8322
8459
|
return childList ? `- \`${dir}/\` (contains: ${childList})` : `- \`${dir}/\``;
|
|
@@ -8361,7 +8498,9 @@ function validateSetup(setup, dir, checkExists = existsSync9, projectStructure)
|
|
|
8361
8498
|
const content = claudeMd ?? agentsMd ?? "";
|
|
8362
8499
|
if (content) {
|
|
8363
8500
|
const structure2 = analyzeMarkdownStructure(content);
|
|
8364
|
-
const blockThreshold = CODE_BLOCK_THRESHOLDS.find(
|
|
8501
|
+
const blockThreshold = CODE_BLOCK_THRESHOLDS.find(
|
|
8502
|
+
(t) => structure2.codeBlockCount >= t.minBlocks
|
|
8503
|
+
);
|
|
8365
8504
|
const blockPoints = blockThreshold?.points ?? 0;
|
|
8366
8505
|
const maxBlockPoints = CODE_BLOCK_THRESHOLDS[0].points;
|
|
8367
8506
|
if (blockPoints < maxBlockPoints && structure2.codeBlockCount < CODE_BLOCK_THRESHOLDS[0].minBlocks) {
|
|
@@ -8486,7 +8625,9 @@ function buildFeedbackMessage(issues) {
|
|
|
8486
8625
|
];
|
|
8487
8626
|
for (let i = 0; i < issues.length; i++) {
|
|
8488
8627
|
const issue = issues[i];
|
|
8489
|
-
lines.push(
|
|
8628
|
+
lines.push(
|
|
8629
|
+
`${i + 1}. ${issue.check.toUpperCase()} (-${issue.pointsLost} pts): ${issue.detail}`
|
|
8630
|
+
);
|
|
8490
8631
|
lines.push(` Action: ${issue.fixInstruction}
|
|
8491
8632
|
`);
|
|
8492
8633
|
}
|
|
@@ -8495,20 +8636,22 @@ function buildFeedbackMessage(issues) {
|
|
|
8495
8636
|
function countIssuePoints(issues) {
|
|
8496
8637
|
return issues.reduce((sum, i) => sum + i.pointsLost, 0);
|
|
8497
8638
|
}
|
|
8498
|
-
async function scoreAndRefine(setup, dir, sessionHistory, callbacks) {
|
|
8639
|
+
async function scoreAndRefine(setup, dir, sessionHistory, callbacks, options) {
|
|
8640
|
+
const maxIterations = options?.thorough ? 3 : MAX_REFINE_ITERATIONS;
|
|
8641
|
+
const minPoints = options?.thorough ? 1 : MIN_POINTS_TO_REFINE;
|
|
8499
8642
|
const existsCache = /* @__PURE__ */ new Map();
|
|
8500
|
-
const cachedExists = (
|
|
8501
|
-
const cached = existsCache.get(
|
|
8643
|
+
const cachedExists = (path44) => {
|
|
8644
|
+
const cached = existsCache.get(path44);
|
|
8502
8645
|
if (cached !== void 0) return cached;
|
|
8503
|
-
const result = existsSync9(
|
|
8504
|
-
existsCache.set(
|
|
8646
|
+
const result = existsSync9(path44);
|
|
8647
|
+
existsCache.set(path44, result);
|
|
8505
8648
|
return result;
|
|
8506
8649
|
};
|
|
8507
8650
|
const projectStructure = collectProjectStructure(dir);
|
|
8508
8651
|
let currentSetup = setup;
|
|
8509
8652
|
let bestSetup = setup;
|
|
8510
8653
|
let bestLostPoints = Infinity;
|
|
8511
|
-
for (let iteration = 0; iteration <
|
|
8654
|
+
for (let iteration = 0; iteration < maxIterations; iteration++) {
|
|
8512
8655
|
const issues = validateSetup(currentSetup, dir, cachedExists, projectStructure);
|
|
8513
8656
|
const lostPoints = countIssuePoints(issues);
|
|
8514
8657
|
if (lostPoints < bestLostPoints) {
|
|
@@ -8519,10 +8662,16 @@ async function scoreAndRefine(setup, dir, sessionHistory, callbacks) {
|
|
|
8519
8662
|
if (callbacks?.onStatus) callbacks.onStatus("Config passes all scoring checks");
|
|
8520
8663
|
return bestSetup;
|
|
8521
8664
|
}
|
|
8665
|
+
if (lostPoints < minPoints) {
|
|
8666
|
+
if (callbacks?.onStatus) callbacks.onStatus("Minor issues \u2014 skipping refinement");
|
|
8667
|
+
return bestSetup;
|
|
8668
|
+
}
|
|
8522
8669
|
const pointIssues = issues.filter((i) => i.pointsLost > 0);
|
|
8523
8670
|
const pointIssueNames = pointIssues.map((i) => i.check).join(", ");
|
|
8524
8671
|
if (callbacks?.onStatus) {
|
|
8525
|
-
callbacks.onStatus(
|
|
8672
|
+
callbacks.onStatus(
|
|
8673
|
+
`Fixing ${pointIssues.length} scoring issue${pointIssues.length === 1 ? "" : "s"}: ${pointIssueNames}...`
|
|
8674
|
+
);
|
|
8526
8675
|
}
|
|
8527
8676
|
const refined = await applyTargetedFixes(currentSetup, issues);
|
|
8528
8677
|
if (!refined) {
|
|
@@ -8557,17 +8706,19 @@ async function applyTargetedFixes(setup, issues) {
|
|
|
8557
8706
|
}
|
|
8558
8707
|
for (const skill of skills) {
|
|
8559
8708
|
if (failingChecks.has(`Skill quality: ${skill.name}`)) {
|
|
8560
|
-
targets.push({
|
|
8709
|
+
targets.push({
|
|
8710
|
+
key: `skill:${skill.name}`,
|
|
8711
|
+
label: `Skill: ${skill.name}`,
|
|
8712
|
+
content: skill.content
|
|
8713
|
+
});
|
|
8561
8714
|
}
|
|
8562
8715
|
}
|
|
8563
8716
|
if (targets.length === 0) return null;
|
|
8564
8717
|
const feedbackMessage = buildFeedbackMessage(issues);
|
|
8565
|
-
const contentBlock = targets.map(
|
|
8566
|
-
(t) => `### ${t.label}
|
|
8718
|
+
const contentBlock = targets.map((t) => `### ${t.label}
|
|
8567
8719
|
\`\`\`markdown
|
|
8568
8720
|
${t.content}
|
|
8569
|
-
\`\`\``
|
|
8570
|
-
).join("\n\n");
|
|
8721
|
+
\`\`\``).join("\n\n");
|
|
8571
8722
|
const prompt = [
|
|
8572
8723
|
"Here are the config files with scoring issues:\n",
|
|
8573
8724
|
contentBlock,
|
|
@@ -8618,14 +8769,20 @@ Return ONLY the fixed content as a JSON object with keys ${targets.map((t) => `"
|
|
|
8618
8769
|
return null;
|
|
8619
8770
|
}
|
|
8620
8771
|
}
|
|
8621
|
-
async function runScoreRefineWithSpinner(setup, dir, sessionHistory) {
|
|
8772
|
+
async function runScoreRefineWithSpinner(setup, dir, sessionHistory, options) {
|
|
8622
8773
|
const spinner = ora2("Validating config against scoring criteria...").start();
|
|
8623
8774
|
try {
|
|
8624
|
-
const refined = await scoreAndRefine(
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8775
|
+
const refined = await scoreAndRefine(
|
|
8776
|
+
setup,
|
|
8777
|
+
dir,
|
|
8778
|
+
sessionHistory,
|
|
8779
|
+
{
|
|
8780
|
+
onStatus: (msg) => {
|
|
8781
|
+
spinner.text = msg;
|
|
8782
|
+
}
|
|
8783
|
+
},
|
|
8784
|
+
options
|
|
8785
|
+
);
|
|
8629
8786
|
if (refined !== setup) {
|
|
8630
8787
|
spinner.succeed("Config refined based on scoring feedback");
|
|
8631
8788
|
} else {
|
|
@@ -9365,6 +9522,7 @@ async function refineLoop(currentSetup, sessionHistory, summarizeSetup2, printSu
|
|
|
9365
9522
|
// src/commands/init-display.ts
|
|
9366
9523
|
import chalk12 from "chalk";
|
|
9367
9524
|
import fs31 from "fs";
|
|
9525
|
+
init_types();
|
|
9368
9526
|
function formatWhatChanged(setup) {
|
|
9369
9527
|
const lines = [];
|
|
9370
9528
|
const claude = setup.claude;
|
|
@@ -9534,7 +9692,11 @@ function displayTokenUsage() {
|
|
|
9534
9692
|
console.log(chalk12.dim(" Token tracking not available for this provider.\n"));
|
|
9535
9693
|
return;
|
|
9536
9694
|
}
|
|
9537
|
-
|
|
9695
|
+
const config = loadConfig();
|
|
9696
|
+
const isEstimated = config != null && isSeatBased(config.provider);
|
|
9697
|
+
const label = isEstimated ? "Estimated token usage:" : "Token usage:";
|
|
9698
|
+
console.log(chalk12.bold(` ${label}
|
|
9699
|
+
`));
|
|
9538
9700
|
let totalIn = 0;
|
|
9539
9701
|
let totalOut = 0;
|
|
9540
9702
|
for (const m of summary) {
|
|
@@ -9552,6 +9714,9 @@ function displayTokenUsage() {
|
|
|
9552
9714
|
` ${chalk12.dim("Total")}: ${totalIn.toLocaleString()} in / ${totalOut.toLocaleString()} out`
|
|
9553
9715
|
);
|
|
9554
9716
|
}
|
|
9717
|
+
if (isEstimated) {
|
|
9718
|
+
console.log(chalk12.dim(" (Estimated from character count)"));
|
|
9719
|
+
}
|
|
9555
9720
|
console.log("");
|
|
9556
9721
|
}
|
|
9557
9722
|
|
|
@@ -10233,9 +10398,15 @@ async function initCommand(options) {
|
|
|
10233
10398
|
content: summarizeSetup("Initial generation", generatedSetup)
|
|
10234
10399
|
});
|
|
10235
10400
|
try {
|
|
10236
|
-
const refined = await scoreAndRefine(
|
|
10237
|
-
|
|
10238
|
-
|
|
10401
|
+
const refined = await scoreAndRefine(
|
|
10402
|
+
generatedSetup,
|
|
10403
|
+
process.cwd(),
|
|
10404
|
+
sessionHistory,
|
|
10405
|
+
{
|
|
10406
|
+
onStatus: (msg) => display.update(TASK_SCORE_REFINE, "running", msg)
|
|
10407
|
+
},
|
|
10408
|
+
{ thorough: options.thorough }
|
|
10409
|
+
);
|
|
10239
10410
|
if (refined !== generatedSetup) {
|
|
10240
10411
|
display.update(TASK_SCORE_REFINE, "done", "Refined");
|
|
10241
10412
|
generatedSetup = refined;
|
|
@@ -10880,8 +11051,8 @@ async function scoreCommand(options) {
|
|
|
10880
11051
|
}
|
|
10881
11052
|
|
|
10882
11053
|
// src/commands/refresh.ts
|
|
10883
|
-
import
|
|
10884
|
-
import
|
|
11054
|
+
import fs41 from "fs";
|
|
11055
|
+
import path34 from "path";
|
|
10885
11056
|
import chalk19 from "chalk";
|
|
10886
11057
|
import ora6 from "ora";
|
|
10887
11058
|
|
|
@@ -10968,51 +11139,87 @@ function collectDiff(lastSha) {
|
|
|
10968
11139
|
const summary = parts.join(", ") || "no changes";
|
|
10969
11140
|
return { hasChanges, committedDiff, stagedDiff, unstagedDiff, changedFiles, summary };
|
|
10970
11141
|
}
|
|
11142
|
+
function scopeDiffToDir(diff, dir, allConfigDirs) {
|
|
11143
|
+
if (dir === ".") {
|
|
11144
|
+
const otherDirs = allConfigDirs.filter((d) => d !== ".");
|
|
11145
|
+
if (otherDirs.length === 0) return diff;
|
|
11146
|
+
const changedFiles2 = diff.changedFiles.filter(
|
|
11147
|
+
(f) => !otherDirs.some((d) => f.startsWith(`${d}/`))
|
|
11148
|
+
);
|
|
11149
|
+
const hasChanges2 = changedFiles2.length > 0;
|
|
11150
|
+
return {
|
|
11151
|
+
...diff,
|
|
11152
|
+
changedFiles: changedFiles2,
|
|
11153
|
+
hasChanges: hasChanges2,
|
|
11154
|
+
summary: hasChanges2 ? `${changedFiles2.length} files changed` : "no changes"
|
|
11155
|
+
};
|
|
11156
|
+
}
|
|
11157
|
+
const prefix = `${dir}/`;
|
|
11158
|
+
const changedFiles = diff.changedFiles.filter((f) => f.startsWith(prefix)).map((f) => f.slice(prefix.length));
|
|
11159
|
+
const hasChanges = changedFiles.length > 0;
|
|
11160
|
+
return {
|
|
11161
|
+
...diff,
|
|
11162
|
+
changedFiles,
|
|
11163
|
+
hasChanges,
|
|
11164
|
+
summary: hasChanges ? `${changedFiles.length} files changed` : "no changes"
|
|
11165
|
+
};
|
|
11166
|
+
}
|
|
10971
11167
|
|
|
10972
11168
|
// src/writers/refresh.ts
|
|
10973
11169
|
init_pre_commit_block();
|
|
10974
11170
|
import fs37 from "fs";
|
|
10975
11171
|
import path30 from "path";
|
|
10976
|
-
function writeRefreshDocs(docs) {
|
|
11172
|
+
function writeRefreshDocs(docs, dir = ".") {
|
|
10977
11173
|
const written = [];
|
|
11174
|
+
const p = (relPath) => (dir === "." ? relPath : path30.join(dir, relPath)).replace(/\\/g, "/");
|
|
11175
|
+
const ensureParent = (filePath) => {
|
|
11176
|
+
const parent = path30.dirname(filePath);
|
|
11177
|
+
if (parent !== "." && !fs37.existsSync(parent)) fs37.mkdirSync(parent, { recursive: true });
|
|
11178
|
+
};
|
|
10978
11179
|
if (docs.agentsMd) {
|
|
10979
|
-
|
|
10980
|
-
|
|
11180
|
+
const filePath = p("AGENTS.md");
|
|
11181
|
+
ensureParent(filePath);
|
|
11182
|
+
fs37.writeFileSync(filePath, appendManagedBlocks(docs.agentsMd, "codex"));
|
|
11183
|
+
written.push(filePath);
|
|
10981
11184
|
}
|
|
10982
11185
|
if (docs.claudeMd) {
|
|
10983
|
-
|
|
10984
|
-
|
|
11186
|
+
const filePath = p("CLAUDE.md");
|
|
11187
|
+
ensureParent(filePath);
|
|
11188
|
+
fs37.writeFileSync(filePath, appendManagedBlocks(docs.claudeMd));
|
|
11189
|
+
written.push(filePath);
|
|
10985
11190
|
}
|
|
10986
11191
|
if (docs.readmeMd) {
|
|
10987
|
-
|
|
10988
|
-
|
|
11192
|
+
const filePath = p("README.md");
|
|
11193
|
+
ensureParent(filePath);
|
|
11194
|
+
fs37.writeFileSync(filePath, docs.readmeMd);
|
|
11195
|
+
written.push(filePath);
|
|
10989
11196
|
}
|
|
10990
11197
|
if (docs.cursorrules) {
|
|
10991
|
-
|
|
10992
|
-
|
|
11198
|
+
const filePath = p(".cursorrules");
|
|
11199
|
+
ensureParent(filePath);
|
|
11200
|
+
fs37.writeFileSync(filePath, docs.cursorrules);
|
|
11201
|
+
written.push(filePath);
|
|
10993
11202
|
}
|
|
10994
11203
|
if (docs.cursorRules) {
|
|
10995
|
-
const rulesDir = path30.join(".cursor", "rules");
|
|
11204
|
+
const rulesDir = p(path30.join(".cursor", "rules"));
|
|
10996
11205
|
if (!fs37.existsSync(rulesDir)) fs37.mkdirSync(rulesDir, { recursive: true });
|
|
10997
11206
|
for (const rule of docs.cursorRules) {
|
|
10998
11207
|
fs37.writeFileSync(path30.join(rulesDir, rule.filename), rule.content);
|
|
10999
|
-
written.push(
|
|
11208
|
+
written.push(p(path30.join(".cursor", "rules", rule.filename)));
|
|
11000
11209
|
}
|
|
11001
11210
|
}
|
|
11002
11211
|
if (docs.copilotInstructions) {
|
|
11003
|
-
|
|
11004
|
-
|
|
11005
|
-
|
|
11006
|
-
|
|
11007
|
-
);
|
|
11008
|
-
written.push(".github/copilot-instructions.md");
|
|
11212
|
+
const filePath = p(path30.join(".github", "copilot-instructions.md"));
|
|
11213
|
+
ensureParent(filePath);
|
|
11214
|
+
fs37.writeFileSync(filePath, appendManagedBlocks(docs.copilotInstructions, "copilot"));
|
|
11215
|
+
written.push(filePath);
|
|
11009
11216
|
}
|
|
11010
11217
|
if (docs.copilotInstructionFiles) {
|
|
11011
|
-
const instructionsDir = path30.join(".github", "instructions");
|
|
11012
|
-
fs37.mkdirSync(instructionsDir, { recursive: true });
|
|
11218
|
+
const instructionsDir = p(path30.join(".github", "instructions"));
|
|
11219
|
+
if (!fs37.existsSync(instructionsDir)) fs37.mkdirSync(instructionsDir, { recursive: true });
|
|
11013
11220
|
for (const file of docs.copilotInstructionFiles) {
|
|
11014
11221
|
fs37.writeFileSync(path30.join(instructionsDir, file.filename), file.content);
|
|
11015
|
-
written.push(
|
|
11222
|
+
written.push(p(path30.join(".github", "instructions", file.filename)));
|
|
11016
11223
|
}
|
|
11017
11224
|
}
|
|
11018
11225
|
return written;
|
|
@@ -11021,8 +11228,15 @@ function writeRefreshDocs(docs) {
|
|
|
11021
11228
|
// src/ai/refresh.ts
|
|
11022
11229
|
init_config();
|
|
11023
11230
|
init_pre_commit_block();
|
|
11024
|
-
async function refreshDocs(diff, existingDocs, projectContext, learnedSection, sources2) {
|
|
11025
|
-
const prompt = buildRefreshPrompt(
|
|
11231
|
+
async function refreshDocs(diff, existingDocs, projectContext, learnedSection, sources2, scope) {
|
|
11232
|
+
const prompt = buildRefreshPrompt(
|
|
11233
|
+
diff,
|
|
11234
|
+
existingDocs,
|
|
11235
|
+
projectContext,
|
|
11236
|
+
learnedSection,
|
|
11237
|
+
sources2,
|
|
11238
|
+
scope
|
|
11239
|
+
);
|
|
11026
11240
|
const fastModel = getFastModel();
|
|
11027
11241
|
const raw = await llmCall({
|
|
11028
11242
|
system: REFRESH_SYSTEM_PROMPT,
|
|
@@ -11032,8 +11246,14 @@ async function refreshDocs(diff, existingDocs, projectContext, learnedSection, s
|
|
|
11032
11246
|
});
|
|
11033
11247
|
return parseJsonResponse(raw);
|
|
11034
11248
|
}
|
|
11035
|
-
function buildRefreshPrompt(diff, existingDocs, projectContext, learnedSection, sources2) {
|
|
11249
|
+
function buildRefreshPrompt(diff, existingDocs, projectContext, learnedSection, sources2, scope) {
|
|
11036
11250
|
const parts = [];
|
|
11251
|
+
if (scope) {
|
|
11252
|
+
parts.push(`You are updating docs for the \`${scope}\` subdirectory of a monorepo.`);
|
|
11253
|
+
parts.push("Only include changes relevant to files under this directory.");
|
|
11254
|
+
parts.push("The changed files list has been filtered to this directory already.");
|
|
11255
|
+
parts.push("Ignore diff content for files outside this directory.\n");
|
|
11256
|
+
}
|
|
11037
11257
|
parts.push("Update documentation based on the following code changes.\n");
|
|
11038
11258
|
if (projectContext.packageName) parts.push(`Project: ${projectContext.packageName}`);
|
|
11039
11259
|
if (projectContext.languages?.length)
|
|
@@ -11315,10 +11535,77 @@ function migrateInlineLearnings() {
|
|
|
11315
11535
|
init_config();
|
|
11316
11536
|
init_resolve_caliber();
|
|
11317
11537
|
init_builtin_skills();
|
|
11538
|
+
|
|
11539
|
+
// src/lib/config-discovery.ts
|
|
11540
|
+
import fs39 from "fs";
|
|
11541
|
+
import path32 from "path";
|
|
11542
|
+
var CONFIG_FILE_MARKERS = [
|
|
11543
|
+
"CLAUDE.md",
|
|
11544
|
+
"AGENTS.md",
|
|
11545
|
+
".cursorrules",
|
|
11546
|
+
".github/copilot-instructions.md"
|
|
11547
|
+
];
|
|
11548
|
+
var CONFIG_DIR_MARKERS = [".cursor/rules", ".github/instructions", ".opencode/skills"];
|
|
11549
|
+
var IGNORE_DIRS3 = /* @__PURE__ */ new Set([
|
|
11550
|
+
"node_modules",
|
|
11551
|
+
".git",
|
|
11552
|
+
".next",
|
|
11553
|
+
"dist",
|
|
11554
|
+
"build",
|
|
11555
|
+
".cache",
|
|
11556
|
+
".turbo",
|
|
11557
|
+
"coverage",
|
|
11558
|
+
".caliber",
|
|
11559
|
+
"__pycache__",
|
|
11560
|
+
".venv",
|
|
11561
|
+
"vendor",
|
|
11562
|
+
"target"
|
|
11563
|
+
]);
|
|
11564
|
+
var MAX_DEPTH = 4;
|
|
11565
|
+
function hasConfigFiles(dir) {
|
|
11566
|
+
for (const marker of CONFIG_FILE_MARKERS) {
|
|
11567
|
+
if (fs39.existsSync(path32.join(dir, marker))) return true;
|
|
11568
|
+
}
|
|
11569
|
+
for (const marker of CONFIG_DIR_MARKERS) {
|
|
11570
|
+
const markerPath = path32.join(dir, marker);
|
|
11571
|
+
if (fs39.existsSync(markerPath) && fs39.statSync(markerPath).isDirectory()) return true;
|
|
11572
|
+
}
|
|
11573
|
+
return false;
|
|
11574
|
+
}
|
|
11575
|
+
function discoverConfigDirs(rootDir) {
|
|
11576
|
+
const dirs = [];
|
|
11577
|
+
if (hasConfigFiles(rootDir)) {
|
|
11578
|
+
dirs.push(".");
|
|
11579
|
+
}
|
|
11580
|
+
walkForConfigs(rootDir, rootDir, 0, dirs);
|
|
11581
|
+
dirs.sort();
|
|
11582
|
+
return dirs;
|
|
11583
|
+
}
|
|
11584
|
+
function walkForConfigs(baseDir, currentDir, depth, result) {
|
|
11585
|
+
if (depth >= MAX_DEPTH) return;
|
|
11586
|
+
let entries;
|
|
11587
|
+
try {
|
|
11588
|
+
entries = fs39.readdirSync(currentDir, { withFileTypes: true });
|
|
11589
|
+
} catch {
|
|
11590
|
+
return;
|
|
11591
|
+
}
|
|
11592
|
+
for (const entry of entries) {
|
|
11593
|
+
if (!entry.isDirectory()) continue;
|
|
11594
|
+
if (entry.name.startsWith(".") || IGNORE_DIRS3.has(entry.name)) continue;
|
|
11595
|
+
const fullPath = path32.join(currentDir, entry.name);
|
|
11596
|
+
const relPath = path32.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
11597
|
+
if (hasConfigFiles(fullPath)) {
|
|
11598
|
+
result.push(relPath);
|
|
11599
|
+
}
|
|
11600
|
+
walkForConfigs(baseDir, fullPath, depth + 1, result);
|
|
11601
|
+
}
|
|
11602
|
+
}
|
|
11603
|
+
|
|
11604
|
+
// src/commands/refresh.ts
|
|
11318
11605
|
function writeRefreshError(error) {
|
|
11319
11606
|
try {
|
|
11320
|
-
if (!
|
|
11321
|
-
|
|
11607
|
+
if (!fs41.existsSync(CALIBER_DIR)) fs41.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
11608
|
+
fs41.writeFileSync(
|
|
11322
11609
|
REFRESH_LAST_ERROR_FILE,
|
|
11323
11610
|
JSON.stringify(
|
|
11324
11611
|
{
|
|
@@ -11337,15 +11624,15 @@ function writeRefreshError(error) {
|
|
|
11337
11624
|
}
|
|
11338
11625
|
function readRefreshError() {
|
|
11339
11626
|
try {
|
|
11340
|
-
if (!
|
|
11341
|
-
return JSON.parse(
|
|
11627
|
+
if (!fs41.existsSync(REFRESH_LAST_ERROR_FILE)) return null;
|
|
11628
|
+
return JSON.parse(fs41.readFileSync(REFRESH_LAST_ERROR_FILE, "utf-8"));
|
|
11342
11629
|
} catch {
|
|
11343
11630
|
return null;
|
|
11344
11631
|
}
|
|
11345
11632
|
}
|
|
11346
11633
|
function clearRefreshError() {
|
|
11347
11634
|
try {
|
|
11348
|
-
if (
|
|
11635
|
+
if (fs41.existsSync(REFRESH_LAST_ERROR_FILE)) fs41.unlinkSync(REFRESH_LAST_ERROR_FILE);
|
|
11349
11636
|
} catch {
|
|
11350
11637
|
}
|
|
11351
11638
|
}
|
|
@@ -11354,7 +11641,8 @@ function detectSyncedAgents(writtenFiles) {
|
|
|
11354
11641
|
const joined = writtenFiles.join(" ");
|
|
11355
11642
|
if (joined.includes("CLAUDE.md") || joined.includes(".claude/")) agents.push("Claude Code");
|
|
11356
11643
|
if (joined.includes(".cursor/") || joined.includes(".cursorrules")) agents.push("Cursor");
|
|
11357
|
-
if (joined.includes("copilot-instructions") || joined.includes(".github/instructions/"))
|
|
11644
|
+
if (joined.includes("copilot-instructions") || joined.includes(".github/instructions/"))
|
|
11645
|
+
agents.push("Copilot");
|
|
11358
11646
|
if (joined.includes("AGENTS.md") || joined.includes(".agents/")) agents.push("Codex");
|
|
11359
11647
|
return agents;
|
|
11360
11648
|
}
|
|
@@ -11364,11 +11652,11 @@ function log2(quiet, ...args) {
|
|
|
11364
11652
|
function discoverGitRepos(parentDir) {
|
|
11365
11653
|
const repos = [];
|
|
11366
11654
|
try {
|
|
11367
|
-
const entries =
|
|
11655
|
+
const entries = fs41.readdirSync(parentDir, { withFileTypes: true });
|
|
11368
11656
|
for (const entry of entries) {
|
|
11369
11657
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
11370
|
-
const childPath =
|
|
11371
|
-
if (
|
|
11658
|
+
const childPath = path34.join(parentDir, entry.name);
|
|
11659
|
+
if (fs41.existsSync(path34.join(childPath, ".git"))) {
|
|
11372
11660
|
repos.push(childPath);
|
|
11373
11661
|
}
|
|
11374
11662
|
}
|
|
@@ -11376,51 +11664,33 @@ function discoverGitRepos(parentDir) {
|
|
|
11376
11664
|
}
|
|
11377
11665
|
return repos.sort();
|
|
11378
11666
|
}
|
|
11379
|
-
function collectFilesToWrite(updatedDocs) {
|
|
11667
|
+
function collectFilesToWrite(updatedDocs, dir = ".") {
|
|
11380
11668
|
const files = [];
|
|
11381
|
-
|
|
11382
|
-
if (updatedDocs.
|
|
11383
|
-
if (updatedDocs.
|
|
11384
|
-
if (updatedDocs.
|
|
11669
|
+
const p = (relPath) => (dir === "." ? relPath : path34.join(dir, relPath)).replace(/\\/g, "/");
|
|
11670
|
+
if (updatedDocs.agentsMd) files.push(p("AGENTS.md"));
|
|
11671
|
+
if (updatedDocs.claudeMd) files.push(p("CLAUDE.md"));
|
|
11672
|
+
if (updatedDocs.readmeMd) files.push(p("README.md"));
|
|
11673
|
+
if (updatedDocs.cursorrules) files.push(p(".cursorrules"));
|
|
11385
11674
|
if (Array.isArray(updatedDocs.cursorRules)) {
|
|
11386
11675
|
for (const r of updatedDocs.cursorRules)
|
|
11387
|
-
files.push(`.cursor/rules/${r.filename}`);
|
|
11676
|
+
files.push(p(`.cursor/rules/${r.filename}`));
|
|
11388
11677
|
}
|
|
11389
|
-
if (updatedDocs.copilotInstructions) files.push(".github/copilot-instructions.md");
|
|
11678
|
+
if (updatedDocs.copilotInstructions) files.push(p(".github/copilot-instructions.md"));
|
|
11390
11679
|
if (Array.isArray(updatedDocs.copilotInstructionFiles)) {
|
|
11391
11680
|
for (const f of updatedDocs.copilotInstructionFiles)
|
|
11392
|
-
files.push(`.github/instructions/${f.filename}`);
|
|
11681
|
+
files.push(p(`.github/instructions/${f.filename}`));
|
|
11393
11682
|
}
|
|
11394
11683
|
return files;
|
|
11395
11684
|
}
|
|
11396
11685
|
var REFRESH_COOLDOWN_MS = 3e4;
|
|
11397
|
-
async function
|
|
11686
|
+
async function refreshDir(repoDir, dir, diff, options) {
|
|
11398
11687
|
const quiet = !!options.quiet;
|
|
11399
11688
|
const prefix = options.label ? `${chalk19.bold(options.label)} ` : "";
|
|
11400
|
-
const
|
|
11401
|
-
const
|
|
11402
|
-
const currentSha = getCurrentHeadSha();
|
|
11403
|
-
if (state?.lastRefreshTimestamp && lastSha && currentSha === lastSha) {
|
|
11404
|
-
const elapsed = Date.now() - new Date(state.lastRefreshTimestamp).getTime();
|
|
11405
|
-
if (elapsed < REFRESH_COOLDOWN_MS && elapsed > 0) {
|
|
11406
|
-
log2(
|
|
11407
|
-
quiet,
|
|
11408
|
-
chalk19.dim(`${prefix}Skipped \u2014 last refresh was ${Math.round(elapsed / 1e3)}s ago.`)
|
|
11409
|
-
);
|
|
11410
|
-
return;
|
|
11411
|
-
}
|
|
11412
|
-
}
|
|
11413
|
-
const diff = collectDiff(lastSha);
|
|
11414
|
-
if (!diff.hasChanges) {
|
|
11415
|
-
if (currentSha) {
|
|
11416
|
-
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
11417
|
-
}
|
|
11418
|
-
log2(quiet, chalk19.dim(`${prefix}No changes since last refresh.`));
|
|
11419
|
-
return;
|
|
11420
|
-
}
|
|
11689
|
+
const absDir = dir === "." ? repoDir : path34.resolve(repoDir, dir);
|
|
11690
|
+
const scope = dir === "." ? void 0 : dir;
|
|
11421
11691
|
const spinner = quiet ? null : ora6(`${prefix}Analyzing changes...`).start();
|
|
11422
11692
|
const learnedSection = readLearnedSection();
|
|
11423
|
-
const fingerprint = await collectFingerprint(
|
|
11693
|
+
const fingerprint = await collectFingerprint(absDir);
|
|
11424
11694
|
const existingDocs = fingerprint.existingConfigs;
|
|
11425
11695
|
const projectContext = {
|
|
11426
11696
|
languages: fingerprint.languages,
|
|
@@ -11428,8 +11698,8 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11428
11698
|
packageName: fingerprint.packageName,
|
|
11429
11699
|
fileTree: fingerprint.fileTree
|
|
11430
11700
|
};
|
|
11431
|
-
const workspaces = getDetectedWorkspaces(
|
|
11432
|
-
const sources2 = resolveAllSources(
|
|
11701
|
+
const workspaces = getDetectedWorkspaces(absDir);
|
|
11702
|
+
const sources2 = resolveAllSources(absDir, [], workspaces);
|
|
11433
11703
|
const diffPayload = {
|
|
11434
11704
|
committed: diff.committedDiff,
|
|
11435
11705
|
staged: diff.stagedDiff,
|
|
@@ -11445,7 +11715,8 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11445
11715
|
existingDocs,
|
|
11446
11716
|
projectContext,
|
|
11447
11717
|
learnedSection,
|
|
11448
|
-
sourcesPayload
|
|
11718
|
+
sourcesPayload,
|
|
11719
|
+
scope
|
|
11449
11720
|
);
|
|
11450
11721
|
} catch (firstErr) {
|
|
11451
11722
|
const isTransient = firstErr instanceof Error && TRANSIENT_ERRORS.some((e) => firstErr.message.toLowerCase().includes(e.toLowerCase()));
|
|
@@ -11456,7 +11727,8 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11456
11727
|
existingDocs,
|
|
11457
11728
|
projectContext,
|
|
11458
11729
|
learnedSection,
|
|
11459
|
-
sourcesPayload
|
|
11730
|
+
sourcesPayload,
|
|
11731
|
+
scope
|
|
11460
11732
|
);
|
|
11461
11733
|
} catch {
|
|
11462
11734
|
spinner?.fail(`${prefix}Refresh failed after retry`);
|
|
@@ -11465,10 +11737,7 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11465
11737
|
}
|
|
11466
11738
|
if (!response.docsUpdated || response.docsUpdated.length === 0) {
|
|
11467
11739
|
spinner?.succeed(`${prefix}No doc updates needed`);
|
|
11468
|
-
|
|
11469
|
-
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
11470
|
-
}
|
|
11471
|
-
return;
|
|
11740
|
+
return { written: [] };
|
|
11472
11741
|
}
|
|
11473
11742
|
if (options.dryRun) {
|
|
11474
11743
|
spinner?.info(`${prefix}Dry run \u2014 would update:`);
|
|
@@ -11479,49 +11748,53 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11479
11748
|
console.log(chalk19.dim(`
|
|
11480
11749
|
${response.changesSummary}`));
|
|
11481
11750
|
}
|
|
11482
|
-
return;
|
|
11751
|
+
return { written: [] };
|
|
11483
11752
|
}
|
|
11484
|
-
const
|
|
11485
|
-
const preScore = computeLocalScore(repoDir, targetAgent);
|
|
11486
|
-
const allFilesToWrite = collectFilesToWrite(response.updatedDocs);
|
|
11753
|
+
const allFilesToWrite = collectFilesToWrite(response.updatedDocs, dir);
|
|
11487
11754
|
const preRefreshContents = /* @__PURE__ */ new Map();
|
|
11488
11755
|
for (const filePath of allFilesToWrite) {
|
|
11489
|
-
const fullPath =
|
|
11756
|
+
const fullPath = path34.resolve(repoDir, filePath);
|
|
11490
11757
|
try {
|
|
11491
|
-
preRefreshContents.set(filePath,
|
|
11758
|
+
preRefreshContents.set(filePath, fs41.readFileSync(fullPath, "utf-8"));
|
|
11492
11759
|
} catch {
|
|
11493
11760
|
preRefreshContents.set(filePath, null);
|
|
11494
11761
|
}
|
|
11495
11762
|
}
|
|
11496
|
-
const
|
|
11763
|
+
const state = readState();
|
|
11764
|
+
const targetAgent = state?.targetAgent ?? detectTargetAgent(repoDir);
|
|
11765
|
+
const runQualityGate = dir === ".";
|
|
11766
|
+
const preScore = runQualityGate ? computeLocalScore(absDir, targetAgent) : null;
|
|
11767
|
+
const written = writeRefreshDocs(response.updatedDocs, dir);
|
|
11497
11768
|
const trigger = quiet ? "hook" : "manual";
|
|
11498
11769
|
trackRefreshCompleted(written.length, Date.now(), trigger);
|
|
11499
|
-
|
|
11500
|
-
|
|
11501
|
-
|
|
11502
|
-
const
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11770
|
+
if (runQualityGate && preScore) {
|
|
11771
|
+
const postScore = computeLocalScore(absDir, targetAgent);
|
|
11772
|
+
if (postScore.score < preScore.score) {
|
|
11773
|
+
for (const [filePath, content] of preRefreshContents) {
|
|
11774
|
+
const fullPath = path34.resolve(repoDir, filePath);
|
|
11775
|
+
if (content === null) {
|
|
11776
|
+
try {
|
|
11777
|
+
fs41.unlinkSync(fullPath);
|
|
11778
|
+
} catch {
|
|
11779
|
+
}
|
|
11780
|
+
} else {
|
|
11781
|
+
fs41.writeFileSync(fullPath, content);
|
|
11507
11782
|
}
|
|
11508
|
-
} else {
|
|
11509
|
-
fs40.writeFileSync(fullPath, content);
|
|
11510
11783
|
}
|
|
11784
|
+
spinner?.warn(
|
|
11785
|
+
`${prefix}Refresh reverted \u2014 score would drop from ${preScore.score} to ${postScore.score}`
|
|
11786
|
+
);
|
|
11787
|
+
log2(quiet, chalk19.dim(` Config quality gate prevented a regression. No files were changed.`));
|
|
11788
|
+
return { written: [] };
|
|
11511
11789
|
}
|
|
11512
|
-
|
|
11513
|
-
`${prefix}Refresh reverted \u2014 score would drop from ${preScore.score} to ${postScore.score}`
|
|
11514
|
-
);
|
|
11515
|
-
log2(quiet, chalk19.dim(` Config quality gate prevented a regression. No files were changed.`));
|
|
11516
|
-
if (currentSha) {
|
|
11517
|
-
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
11518
|
-
}
|
|
11519
|
-
return;
|
|
11790
|
+
recordScore(postScore, "refresh");
|
|
11520
11791
|
}
|
|
11521
|
-
recordScore(postScore, "refresh");
|
|
11522
11792
|
spinner?.succeed(`${prefix}Updated ${written.length} doc${written.length === 1 ? "" : "s"}`);
|
|
11523
11793
|
const fileChangesMap = new Map(
|
|
11524
|
-
(response.fileChanges || []).map((fc) => [
|
|
11794
|
+
(response.fileChanges || []).map((fc) => [
|
|
11795
|
+
fc.file,
|
|
11796
|
+
fc.description
|
|
11797
|
+
])
|
|
11525
11798
|
);
|
|
11526
11799
|
for (const file of written) {
|
|
11527
11800
|
const desc = fileChangesMap.get(file);
|
|
@@ -11537,6 +11810,59 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11537
11810
|
log2(quiet, chalk19.dim(`
|
|
11538
11811
|
${response.changesSummary}`));
|
|
11539
11812
|
}
|
|
11813
|
+
return { written };
|
|
11814
|
+
}
|
|
11815
|
+
async function refreshSingleRepo(repoDir, options) {
|
|
11816
|
+
const quiet = !!options.quiet;
|
|
11817
|
+
const prefix = options.label ? `${chalk19.bold(options.label)} ` : "";
|
|
11818
|
+
const state = readState();
|
|
11819
|
+
const lastSha = state?.lastRefreshSha ?? null;
|
|
11820
|
+
const currentSha = getCurrentHeadSha();
|
|
11821
|
+
if (state?.lastRefreshTimestamp && lastSha && currentSha === lastSha) {
|
|
11822
|
+
const elapsed = Date.now() - new Date(state.lastRefreshTimestamp).getTime();
|
|
11823
|
+
if (elapsed < REFRESH_COOLDOWN_MS && elapsed > 0) {
|
|
11824
|
+
log2(
|
|
11825
|
+
quiet,
|
|
11826
|
+
chalk19.dim(`${prefix}Skipped \u2014 last refresh was ${Math.round(elapsed / 1e3)}s ago.`)
|
|
11827
|
+
);
|
|
11828
|
+
return;
|
|
11829
|
+
}
|
|
11830
|
+
}
|
|
11831
|
+
const diff = collectDiff(lastSha);
|
|
11832
|
+
if (!diff.hasChanges) {
|
|
11833
|
+
if (currentSha) {
|
|
11834
|
+
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
11835
|
+
}
|
|
11836
|
+
log2(quiet, chalk19.dim(`${prefix}No changes since last refresh.`));
|
|
11837
|
+
return;
|
|
11838
|
+
}
|
|
11839
|
+
const configDirs = discoverConfigDirs(repoDir);
|
|
11840
|
+
if (configDirs.length <= 1) {
|
|
11841
|
+
await refreshDir(repoDir, ".", diff, options);
|
|
11842
|
+
} else {
|
|
11843
|
+
log2(quiet, chalk19.dim(`${prefix}Found configs in ${configDirs.length} directories
|
|
11844
|
+
`));
|
|
11845
|
+
let hadFailure = false;
|
|
11846
|
+
for (const dir of configDirs) {
|
|
11847
|
+
const scopedDiff = scopeDiffToDir(diff, dir, configDirs);
|
|
11848
|
+
if (!scopedDiff.hasChanges) continue;
|
|
11849
|
+
const dirLabel = dir === "." ? "root" : dir;
|
|
11850
|
+
try {
|
|
11851
|
+
await refreshDir(repoDir, dir, scopedDiff, { ...options, label: dirLabel });
|
|
11852
|
+
} catch (err) {
|
|
11853
|
+
hadFailure = true;
|
|
11854
|
+
log2(
|
|
11855
|
+
quiet,
|
|
11856
|
+
chalk19.yellow(
|
|
11857
|
+
` ${dirLabel}: refresh failed \u2014 ${err instanceof Error ? err.message : "unknown error"}`
|
|
11858
|
+
)
|
|
11859
|
+
);
|
|
11860
|
+
}
|
|
11861
|
+
}
|
|
11862
|
+
if (hadFailure) {
|
|
11863
|
+
return;
|
|
11864
|
+
}
|
|
11865
|
+
}
|
|
11540
11866
|
const builtinWritten = ensureBuiltinSkills();
|
|
11541
11867
|
for (const file of builtinWritten) {
|
|
11542
11868
|
log2(quiet, ` ${chalk19.green("\u2713")} ${file} ${chalk19.dim("(built-in)")}`);
|
|
@@ -11593,7 +11919,7 @@ async function refreshCommand(options) {
|
|
|
11593
11919
|
`));
|
|
11594
11920
|
const originalDir = process.cwd();
|
|
11595
11921
|
for (const repo of repos) {
|
|
11596
|
-
const repoName =
|
|
11922
|
+
const repoName = path34.basename(repo);
|
|
11597
11923
|
try {
|
|
11598
11924
|
process.chdir(repo);
|
|
11599
11925
|
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
@@ -11621,7 +11947,7 @@ async function refreshCommand(options) {
|
|
|
11621
11947
|
|
|
11622
11948
|
// src/commands/hooks.ts
|
|
11623
11949
|
import chalk20 from "chalk";
|
|
11624
|
-
import
|
|
11950
|
+
import fs42 from "fs";
|
|
11625
11951
|
var HOOKS = [
|
|
11626
11952
|
{
|
|
11627
11953
|
id: "session-end",
|
|
@@ -11665,11 +11991,11 @@ async function hooksCommand(options) {
|
|
|
11665
11991
|
console.log(chalk20.green(" \u2713") + ` ${hook.label} enabled`);
|
|
11666
11992
|
}
|
|
11667
11993
|
}
|
|
11668
|
-
if (
|
|
11994
|
+
if (fs42.existsSync(".claude")) {
|
|
11669
11995
|
const r = installLearningHooks();
|
|
11670
11996
|
if (r.installed) console.log(chalk20.green(" \u2713") + " Claude Code learning hooks enabled");
|
|
11671
11997
|
}
|
|
11672
|
-
if (
|
|
11998
|
+
if (fs42.existsSync(".cursor")) {
|
|
11673
11999
|
const r = installCursorLearningHooks();
|
|
11674
12000
|
if (r.installed) console.log(chalk20.green(" \u2713") + " Cursor learning hooks enabled");
|
|
11675
12001
|
}
|
|
@@ -11837,8 +12163,8 @@ async function configCommand() {
|
|
|
11837
12163
|
}
|
|
11838
12164
|
|
|
11839
12165
|
// src/commands/learn.ts
|
|
11840
|
-
import
|
|
11841
|
-
import
|
|
12166
|
+
import fs46 from "fs";
|
|
12167
|
+
import path38 from "path";
|
|
11842
12168
|
import chalk23 from "chalk";
|
|
11843
12169
|
|
|
11844
12170
|
// src/learner/stdin.ts
|
|
@@ -11869,8 +12195,8 @@ function readStdin() {
|
|
|
11869
12195
|
}
|
|
11870
12196
|
|
|
11871
12197
|
// src/learner/storage.ts
|
|
11872
|
-
import
|
|
11873
|
-
import
|
|
12198
|
+
import fs43 from "fs";
|
|
12199
|
+
import path35 from "path";
|
|
11874
12200
|
var MAX_RESPONSE_LENGTH = 2e3;
|
|
11875
12201
|
var DEFAULT_STATE = {
|
|
11876
12202
|
sessionId: null,
|
|
@@ -11879,15 +12205,15 @@ var DEFAULT_STATE = {
|
|
|
11879
12205
|
lastAnalysisEventCount: 0
|
|
11880
12206
|
};
|
|
11881
12207
|
function ensureLearningDir() {
|
|
11882
|
-
if (!
|
|
11883
|
-
|
|
12208
|
+
if (!fs43.existsSync(getLearningDir())) {
|
|
12209
|
+
fs43.mkdirSync(getLearningDir(), { recursive: true });
|
|
11884
12210
|
}
|
|
11885
12211
|
}
|
|
11886
12212
|
function sessionFilePath() {
|
|
11887
|
-
return
|
|
12213
|
+
return path35.join(getLearningDir(), LEARNING_SESSION_FILE);
|
|
11888
12214
|
}
|
|
11889
12215
|
function stateFilePath() {
|
|
11890
|
-
return
|
|
12216
|
+
return path35.join(getLearningDir(), LEARNING_STATE_FILE);
|
|
11891
12217
|
}
|
|
11892
12218
|
function truncateResponse(response) {
|
|
11893
12219
|
const str = JSON.stringify(response);
|
|
@@ -11897,10 +12223,10 @@ function truncateResponse(response) {
|
|
|
11897
12223
|
function trimSessionFileIfNeeded(filePath) {
|
|
11898
12224
|
const state = readState2();
|
|
11899
12225
|
if (state.eventCount + 1 > LEARNING_MAX_EVENTS) {
|
|
11900
|
-
const lines =
|
|
12226
|
+
const lines = fs43.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
11901
12227
|
if (lines.length > LEARNING_MAX_EVENTS) {
|
|
11902
12228
|
const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
|
|
11903
|
-
|
|
12229
|
+
fs43.writeFileSync(filePath, kept.join("\n") + "\n");
|
|
11904
12230
|
}
|
|
11905
12231
|
}
|
|
11906
12232
|
}
|
|
@@ -11908,19 +12234,19 @@ function appendEvent(event) {
|
|
|
11908
12234
|
ensureLearningDir();
|
|
11909
12235
|
const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
|
|
11910
12236
|
const filePath = sessionFilePath();
|
|
11911
|
-
|
|
12237
|
+
fs43.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
|
|
11912
12238
|
trimSessionFileIfNeeded(filePath);
|
|
11913
12239
|
}
|
|
11914
12240
|
function appendPromptEvent(event) {
|
|
11915
12241
|
ensureLearningDir();
|
|
11916
12242
|
const filePath = sessionFilePath();
|
|
11917
|
-
|
|
12243
|
+
fs43.appendFileSync(filePath, JSON.stringify(event) + "\n");
|
|
11918
12244
|
trimSessionFileIfNeeded(filePath);
|
|
11919
12245
|
}
|
|
11920
12246
|
function readAllEvents() {
|
|
11921
12247
|
const filePath = sessionFilePath();
|
|
11922
|
-
if (!
|
|
11923
|
-
const lines =
|
|
12248
|
+
if (!fs43.existsSync(filePath)) return [];
|
|
12249
|
+
const lines = fs43.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
11924
12250
|
const events = [];
|
|
11925
12251
|
for (const line of lines) {
|
|
11926
12252
|
try {
|
|
@@ -11932,26 +12258,26 @@ function readAllEvents() {
|
|
|
11932
12258
|
}
|
|
11933
12259
|
function getEventCount() {
|
|
11934
12260
|
const filePath = sessionFilePath();
|
|
11935
|
-
if (!
|
|
11936
|
-
const content =
|
|
12261
|
+
if (!fs43.existsSync(filePath)) return 0;
|
|
12262
|
+
const content = fs43.readFileSync(filePath, "utf-8");
|
|
11937
12263
|
return content.split("\n").filter(Boolean).length;
|
|
11938
12264
|
}
|
|
11939
12265
|
function clearSession() {
|
|
11940
12266
|
const filePath = sessionFilePath();
|
|
11941
|
-
if (
|
|
12267
|
+
if (fs43.existsSync(filePath)) fs43.unlinkSync(filePath);
|
|
11942
12268
|
}
|
|
11943
12269
|
function readState2() {
|
|
11944
12270
|
const filePath = stateFilePath();
|
|
11945
|
-
if (!
|
|
12271
|
+
if (!fs43.existsSync(filePath)) return { ...DEFAULT_STATE };
|
|
11946
12272
|
try {
|
|
11947
|
-
return JSON.parse(
|
|
12273
|
+
return JSON.parse(fs43.readFileSync(filePath, "utf-8"));
|
|
11948
12274
|
} catch {
|
|
11949
12275
|
return { ...DEFAULT_STATE };
|
|
11950
12276
|
}
|
|
11951
12277
|
}
|
|
11952
12278
|
function writeState2(state) {
|
|
11953
12279
|
ensureLearningDir();
|
|
11954
|
-
|
|
12280
|
+
fs43.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
|
|
11955
12281
|
}
|
|
11956
12282
|
function resetState() {
|
|
11957
12283
|
writeState2({ ...DEFAULT_STATE });
|
|
@@ -11959,16 +12285,16 @@ function resetState() {
|
|
|
11959
12285
|
var LOCK_FILE = "finalize.lock";
|
|
11960
12286
|
var LOCK_STALE_MS = 5 * 60 * 1e3;
|
|
11961
12287
|
function lockFilePath() {
|
|
11962
|
-
return
|
|
12288
|
+
return path35.join(getLearningDir(), LOCK_FILE);
|
|
11963
12289
|
}
|
|
11964
12290
|
function acquireFinalizeLock() {
|
|
11965
12291
|
ensureLearningDir();
|
|
11966
12292
|
const lockPath = lockFilePath();
|
|
11967
|
-
if (
|
|
12293
|
+
if (fs43.existsSync(lockPath)) {
|
|
11968
12294
|
try {
|
|
11969
|
-
const stat =
|
|
12295
|
+
const stat = fs43.statSync(lockPath);
|
|
11970
12296
|
if (Date.now() - stat.mtimeMs < LOCK_STALE_MS) {
|
|
11971
|
-
const pid = parseInt(
|
|
12297
|
+
const pid = parseInt(fs43.readFileSync(lockPath, "utf-8").trim(), 10);
|
|
11972
12298
|
if (!isNaN(pid) && isProcessAlive(pid)) {
|
|
11973
12299
|
return false;
|
|
11974
12300
|
}
|
|
@@ -11976,12 +12302,12 @@ function acquireFinalizeLock() {
|
|
|
11976
12302
|
} catch {
|
|
11977
12303
|
}
|
|
11978
12304
|
try {
|
|
11979
|
-
|
|
12305
|
+
fs43.unlinkSync(lockPath);
|
|
11980
12306
|
} catch {
|
|
11981
12307
|
}
|
|
11982
12308
|
}
|
|
11983
12309
|
try {
|
|
11984
|
-
|
|
12310
|
+
fs43.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
11985
12311
|
return true;
|
|
11986
12312
|
} catch {
|
|
11987
12313
|
return false;
|
|
@@ -11998,30 +12324,30 @@ function isProcessAlive(pid) {
|
|
|
11998
12324
|
function releaseFinalizeLock() {
|
|
11999
12325
|
const lockPath = lockFilePath();
|
|
12000
12326
|
try {
|
|
12001
|
-
if (
|
|
12327
|
+
if (fs43.existsSync(lockPath)) fs43.unlinkSync(lockPath);
|
|
12002
12328
|
} catch {
|
|
12003
12329
|
}
|
|
12004
12330
|
}
|
|
12005
12331
|
|
|
12006
12332
|
// src/lib/notifications.ts
|
|
12007
|
-
import
|
|
12008
|
-
import
|
|
12333
|
+
import fs44 from "fs";
|
|
12334
|
+
import path36 from "path";
|
|
12009
12335
|
import chalk22 from "chalk";
|
|
12010
12336
|
function notificationFilePath() {
|
|
12011
|
-
return
|
|
12337
|
+
return path36.join(getLearningDir(), "last-finalize-summary.json");
|
|
12012
12338
|
}
|
|
12013
12339
|
function writeFinalizeSummary(summary) {
|
|
12014
12340
|
try {
|
|
12015
12341
|
ensureLearningDir();
|
|
12016
|
-
|
|
12342
|
+
fs44.writeFileSync(notificationFilePath(), JSON.stringify(summary, null, 2));
|
|
12017
12343
|
} catch {
|
|
12018
12344
|
}
|
|
12019
12345
|
}
|
|
12020
12346
|
function checkPendingNotifications() {
|
|
12021
12347
|
try {
|
|
12022
|
-
if (!
|
|
12023
|
-
const raw =
|
|
12024
|
-
|
|
12348
|
+
if (!fs44.existsSync(notificationFilePath())) return;
|
|
12349
|
+
const raw = fs44.readFileSync(notificationFilePath(), "utf-8");
|
|
12350
|
+
fs44.unlinkSync(notificationFilePath());
|
|
12025
12351
|
const summary = JSON.parse(raw);
|
|
12026
12352
|
if (!summary.newItemCount || summary.newItemCount === 0) return;
|
|
12027
12353
|
const wasteLabel = summary.wasteTokens > 0 ? ` (~${summary.wasteTokens.toLocaleString()} wasted tokens captured)` : "";
|
|
@@ -12037,7 +12363,7 @@ function checkPendingNotifications() {
|
|
|
12037
12363
|
console.log("");
|
|
12038
12364
|
} catch {
|
|
12039
12365
|
try {
|
|
12040
|
-
|
|
12366
|
+
fs44.unlinkSync(notificationFilePath());
|
|
12041
12367
|
} catch {
|
|
12042
12368
|
}
|
|
12043
12369
|
}
|
|
@@ -12189,8 +12515,8 @@ function calculateSessionWaste(events) {
|
|
|
12189
12515
|
init_config();
|
|
12190
12516
|
|
|
12191
12517
|
// src/learner/roi.ts
|
|
12192
|
-
import
|
|
12193
|
-
import
|
|
12518
|
+
import fs45 from "fs";
|
|
12519
|
+
import path37 from "path";
|
|
12194
12520
|
var DEFAULT_TOTALS = {
|
|
12195
12521
|
totalWasteTokens: 0,
|
|
12196
12522
|
totalWasteSeconds: 0,
|
|
@@ -12204,19 +12530,19 @@ var DEFAULT_TOTALS = {
|
|
|
12204
12530
|
lastSessionTimestamp: ""
|
|
12205
12531
|
};
|
|
12206
12532
|
function roiFilePath() {
|
|
12207
|
-
return
|
|
12533
|
+
return path37.join(getLearningDir(), LEARNING_ROI_FILE);
|
|
12208
12534
|
}
|
|
12209
12535
|
function readROIStats() {
|
|
12210
12536
|
const filePath = roiFilePath();
|
|
12211
|
-
if (!
|
|
12537
|
+
if (!fs45.existsSync(filePath)) {
|
|
12212
12538
|
return { learnings: [], sessions: [], totals: { ...DEFAULT_TOTALS } };
|
|
12213
12539
|
}
|
|
12214
12540
|
try {
|
|
12215
|
-
return JSON.parse(
|
|
12541
|
+
return JSON.parse(fs45.readFileSync(filePath, "utf-8"));
|
|
12216
12542
|
} catch {
|
|
12217
12543
|
try {
|
|
12218
12544
|
const corruptPath = filePath + ".corrupt";
|
|
12219
|
-
|
|
12545
|
+
fs45.renameSync(filePath, corruptPath);
|
|
12220
12546
|
console.error(`caliber: roi-stats.json was corrupt \u2014 renamed to ${corruptPath}`);
|
|
12221
12547
|
} catch {
|
|
12222
12548
|
}
|
|
@@ -12225,7 +12551,7 @@ function readROIStats() {
|
|
|
12225
12551
|
}
|
|
12226
12552
|
function writeROIStats(stats) {
|
|
12227
12553
|
ensureLearningDir();
|
|
12228
|
-
|
|
12554
|
+
fs45.writeFileSync(roiFilePath(), JSON.stringify(stats, null, 2));
|
|
12229
12555
|
}
|
|
12230
12556
|
function recalculateTotals(stats) {
|
|
12231
12557
|
const totals = stats.totals;
|
|
@@ -12434,9 +12760,9 @@ var AUTO_SETTLE_MS = 200;
|
|
|
12434
12760
|
var INCREMENTAL_INTERVAL = 50;
|
|
12435
12761
|
function writeFinalizeError(message) {
|
|
12436
12762
|
try {
|
|
12437
|
-
const errorPath =
|
|
12438
|
-
if (!
|
|
12439
|
-
|
|
12763
|
+
const errorPath = path38.join(getLearningDir(), LEARNING_LAST_ERROR_FILE);
|
|
12764
|
+
if (!fs46.existsSync(getLearningDir())) fs46.mkdirSync(getLearningDir(), { recursive: true });
|
|
12765
|
+
fs46.writeFileSync(errorPath, JSON.stringify({
|
|
12440
12766
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12441
12767
|
error: message,
|
|
12442
12768
|
pid: process.pid
|
|
@@ -12446,9 +12772,9 @@ function writeFinalizeError(message) {
|
|
|
12446
12772
|
}
|
|
12447
12773
|
function readFinalizeError() {
|
|
12448
12774
|
try {
|
|
12449
|
-
const errorPath =
|
|
12450
|
-
if (!
|
|
12451
|
-
return JSON.parse(
|
|
12775
|
+
const errorPath = path38.join(getLearningDir(), LEARNING_LAST_ERROR_FILE);
|
|
12776
|
+
if (!fs46.existsSync(errorPath)) return null;
|
|
12777
|
+
return JSON.parse(fs46.readFileSync(errorPath, "utf-8"));
|
|
12452
12778
|
} catch {
|
|
12453
12779
|
return null;
|
|
12454
12780
|
}
|
|
@@ -12495,14 +12821,14 @@ async function learnObserveCommand(options) {
|
|
|
12495
12821
|
const { resolveCaliber: resolveCaliber2 } = await Promise.resolve().then(() => (init_resolve_caliber(), resolve_caliber_exports));
|
|
12496
12822
|
const bin = resolveCaliber2();
|
|
12497
12823
|
const { spawn: spawn4 } = await import("child_process");
|
|
12498
|
-
const logPath =
|
|
12499
|
-
if (!
|
|
12500
|
-
const logFd =
|
|
12824
|
+
const logPath = path38.join(getLearningDir(), LEARNING_FINALIZE_LOG);
|
|
12825
|
+
if (!fs46.existsSync(getLearningDir())) fs46.mkdirSync(getLearningDir(), { recursive: true });
|
|
12826
|
+
const logFd = fs46.openSync(logPath, "a");
|
|
12501
12827
|
spawn4(bin, ["learn", "finalize", "--auto", "--incremental"], {
|
|
12502
12828
|
detached: true,
|
|
12503
12829
|
stdio: ["ignore", logFd, logFd]
|
|
12504
12830
|
}).unref();
|
|
12505
|
-
|
|
12831
|
+
fs46.closeSync(logFd);
|
|
12506
12832
|
} catch {
|
|
12507
12833
|
}
|
|
12508
12834
|
}
|
|
@@ -12709,7 +13035,7 @@ async function learnFinalizeCommand(options) {
|
|
|
12709
13035
|
}
|
|
12710
13036
|
async function learnInstallCommand() {
|
|
12711
13037
|
let anyInstalled = false;
|
|
12712
|
-
if (
|
|
13038
|
+
if (fs46.existsSync(".claude")) {
|
|
12713
13039
|
const r = installLearningHooks();
|
|
12714
13040
|
if (r.installed) {
|
|
12715
13041
|
console.log(chalk23.green("\u2713") + " Claude Code learning hooks installed");
|
|
@@ -12718,7 +13044,7 @@ async function learnInstallCommand() {
|
|
|
12718
13044
|
console.log(chalk23.dim(" Claude Code hooks already installed"));
|
|
12719
13045
|
}
|
|
12720
13046
|
}
|
|
12721
|
-
if (
|
|
13047
|
+
if (fs46.existsSync(".cursor")) {
|
|
12722
13048
|
const r = installCursorLearningHooks();
|
|
12723
13049
|
if (r.installed) {
|
|
12724
13050
|
console.log(chalk23.green("\u2713") + " Cursor learning hooks installed");
|
|
@@ -12727,7 +13053,7 @@ async function learnInstallCommand() {
|
|
|
12727
13053
|
console.log(chalk23.dim(" Cursor hooks already installed"));
|
|
12728
13054
|
}
|
|
12729
13055
|
}
|
|
12730
|
-
if (!
|
|
13056
|
+
if (!fs46.existsSync(".claude") && !fs46.existsSync(".cursor")) {
|
|
12731
13057
|
console.log(chalk23.yellow("No .claude/ or .cursor/ directory found."));
|
|
12732
13058
|
console.log(chalk23.dim(` Run \`${resolveCaliber()} init\` first, or create the directory manually.`));
|
|
12733
13059
|
return;
|
|
@@ -12785,8 +13111,8 @@ async function learnStatusCommand() {
|
|
|
12785
13111
|
if (lastError) {
|
|
12786
13112
|
console.log(`Last error: ${chalk23.red(lastError.error)}`);
|
|
12787
13113
|
console.log(chalk23.dim(` at ${lastError.timestamp}`));
|
|
12788
|
-
const logPath =
|
|
12789
|
-
if (
|
|
13114
|
+
const logPath = path38.join(getLearningDir(), LEARNING_FINALIZE_LOG);
|
|
13115
|
+
if (fs46.existsSync(logPath)) {
|
|
12790
13116
|
console.log(chalk23.dim(` Full log: ${logPath}`));
|
|
12791
13117
|
}
|
|
12792
13118
|
}
|
|
@@ -12866,11 +13192,11 @@ async function learnDeleteCommand(indexStr) {
|
|
|
12866
13192
|
}
|
|
12867
13193
|
const item = items[targetIdx];
|
|
12868
13194
|
const filePath = item.source === "personal" ? PERSONAL_LEARNINGS_FILE : "CALIBER_LEARNINGS.md";
|
|
12869
|
-
if (!
|
|
13195
|
+
if (!fs46.existsSync(filePath)) {
|
|
12870
13196
|
console.log(chalk23.red("Learnings file not found."));
|
|
12871
13197
|
return;
|
|
12872
13198
|
}
|
|
12873
|
-
const content =
|
|
13199
|
+
const content = fs46.readFileSync(filePath, "utf-8");
|
|
12874
13200
|
const lines = content.split("\n");
|
|
12875
13201
|
const bulletsOfSource = items.filter((i) => i.source === item.source);
|
|
12876
13202
|
const posInFile = bulletsOfSource.indexOf(item);
|
|
@@ -12891,9 +13217,9 @@ async function learnDeleteCommand(indexStr) {
|
|
|
12891
13217
|
}
|
|
12892
13218
|
const bulletToRemove = lines[lineToRemove];
|
|
12893
13219
|
const newLines = lines.filter((_, i) => i !== lineToRemove);
|
|
12894
|
-
|
|
13220
|
+
fs46.writeFileSync(filePath, newLines.join("\n"));
|
|
12895
13221
|
if (item.source === "personal") {
|
|
12896
|
-
|
|
13222
|
+
fs46.chmodSync(filePath, 384);
|
|
12897
13223
|
}
|
|
12898
13224
|
const roiStats = readROIStats();
|
|
12899
13225
|
const cleanText = bulletToRemove.replace(/^- /, "").replace(/^\*\*\[[^\]]+\]\*\*\s*/, "").trim();
|
|
@@ -13066,8 +13392,8 @@ async function insightsCommand(options) {
|
|
|
13066
13392
|
}
|
|
13067
13393
|
|
|
13068
13394
|
// src/commands/sources.ts
|
|
13069
|
-
import
|
|
13070
|
-
import
|
|
13395
|
+
import fs47 from "fs";
|
|
13396
|
+
import path39 from "path";
|
|
13071
13397
|
import chalk25 from "chalk";
|
|
13072
13398
|
init_resolve_caliber();
|
|
13073
13399
|
async function sourcesListCommand() {
|
|
@@ -13084,9 +13410,9 @@ async function sourcesListCommand() {
|
|
|
13084
13410
|
if (configSources.length > 0) {
|
|
13085
13411
|
for (const source of configSources) {
|
|
13086
13412
|
const sourcePath = source.path || source.url || "";
|
|
13087
|
-
const exists = source.path ?
|
|
13413
|
+
const exists = source.path ? fs47.existsSync(path39.resolve(dir, source.path)) : false;
|
|
13088
13414
|
const status = exists ? chalk25.green("reachable") : chalk25.red("not found");
|
|
13089
|
-
const hasSummary = source.path &&
|
|
13415
|
+
const hasSummary = source.path && fs47.existsSync(path39.join(path39.resolve(dir, source.path), ".caliber", "summary.json"));
|
|
13090
13416
|
console.log(` ${chalk25.bold(source.role || source.type)} ${chalk25.dim(sourcePath)}`);
|
|
13091
13417
|
console.log(` Type: ${source.type} Status: ${status}${hasSummary ? " " + chalk25.cyan("has summary.json") : ""}`);
|
|
13092
13418
|
if (source.description) console.log(` ${chalk25.dim(source.description)}`);
|
|
@@ -13096,7 +13422,7 @@ async function sourcesListCommand() {
|
|
|
13096
13422
|
if (workspaces.length > 0) {
|
|
13097
13423
|
console.log(chalk25.dim(" Auto-detected workspaces:"));
|
|
13098
13424
|
for (const ws of workspaces) {
|
|
13099
|
-
const exists =
|
|
13425
|
+
const exists = fs47.existsSync(path39.resolve(dir, ws));
|
|
13100
13426
|
console.log(` ${exists ? chalk25.green("\u25CF") : chalk25.red("\u25CF")} ${ws}`);
|
|
13101
13427
|
}
|
|
13102
13428
|
console.log("");
|
|
@@ -13104,8 +13430,8 @@ async function sourcesListCommand() {
|
|
|
13104
13430
|
}
|
|
13105
13431
|
async function sourcesAddCommand(sourcePath) {
|
|
13106
13432
|
const dir = process.cwd();
|
|
13107
|
-
const absPath =
|
|
13108
|
-
if (!
|
|
13433
|
+
const absPath = path39.resolve(dir, sourcePath);
|
|
13434
|
+
if (!fs47.existsSync(absPath)) {
|
|
13109
13435
|
console.log(chalk25.red(`
|
|
13110
13436
|
Path not found: ${sourcePath}
|
|
13111
13437
|
`));
|
|
@@ -13120,7 +13446,7 @@ async function sourcesAddCommand(sourcePath) {
|
|
|
13120
13446
|
}
|
|
13121
13447
|
const existing = loadSourcesConfig(dir);
|
|
13122
13448
|
const alreadyConfigured = existing.some(
|
|
13123
|
-
(s) => s.path &&
|
|
13449
|
+
(s) => s.path && path39.resolve(dir, s.path) === absPath
|
|
13124
13450
|
);
|
|
13125
13451
|
if (alreadyConfigured) {
|
|
13126
13452
|
console.log(chalk25.yellow(`
|
|
@@ -13168,8 +13494,8 @@ async function sourcesRemoveCommand(name) {
|
|
|
13168
13494
|
}
|
|
13169
13495
|
|
|
13170
13496
|
// src/commands/publish.ts
|
|
13171
|
-
import
|
|
13172
|
-
import
|
|
13497
|
+
import fs48 from "fs";
|
|
13498
|
+
import path40 from "path";
|
|
13173
13499
|
import chalk26 from "chalk";
|
|
13174
13500
|
import ora7 from "ora";
|
|
13175
13501
|
init_config();
|
|
@@ -13184,10 +13510,10 @@ async function publishCommand() {
|
|
|
13184
13510
|
const spinner = ora7("Generating project summary...").start();
|
|
13185
13511
|
try {
|
|
13186
13512
|
const fingerprint = await collectFingerprint(dir);
|
|
13187
|
-
const claudeMd = readFileOrNull(
|
|
13513
|
+
const claudeMd = readFileOrNull(path40.join(dir, "CLAUDE.md"));
|
|
13188
13514
|
const topLevelDirs = fingerprint.fileTree.filter((f) => f.endsWith("/") && !f.includes("/")).map((f) => f.replace(/\/$/, ""));
|
|
13189
13515
|
const summary = {
|
|
13190
|
-
name: fingerprint.packageName ||
|
|
13516
|
+
name: fingerprint.packageName || path40.basename(dir),
|
|
13191
13517
|
version: "1.0.0",
|
|
13192
13518
|
description: fingerprint.description || "",
|
|
13193
13519
|
languages: fingerprint.languages,
|
|
@@ -13199,7 +13525,7 @@ async function publishCommand() {
|
|
|
13199
13525
|
summary.conventions = claudeMd.slice(0, 2e3);
|
|
13200
13526
|
}
|
|
13201
13527
|
try {
|
|
13202
|
-
const pkgContent = readFileOrNull(
|
|
13528
|
+
const pkgContent = readFileOrNull(path40.join(dir, "package.json"));
|
|
13203
13529
|
if (pkgContent) {
|
|
13204
13530
|
const pkg3 = JSON.parse(pkgContent);
|
|
13205
13531
|
if (pkg3.scripts) {
|
|
@@ -13212,14 +13538,14 @@ async function publishCommand() {
|
|
|
13212
13538
|
}
|
|
13213
13539
|
} catch {
|
|
13214
13540
|
}
|
|
13215
|
-
const outputDir =
|
|
13216
|
-
if (!
|
|
13217
|
-
|
|
13541
|
+
const outputDir = path40.join(dir, ".caliber");
|
|
13542
|
+
if (!fs48.existsSync(outputDir)) {
|
|
13543
|
+
fs48.mkdirSync(outputDir, { recursive: true });
|
|
13218
13544
|
}
|
|
13219
|
-
const outputPath =
|
|
13220
|
-
|
|
13545
|
+
const outputPath = path40.join(outputDir, "summary.json");
|
|
13546
|
+
fs48.writeFileSync(outputPath, JSON.stringify(summary, null, 2) + "\n", "utf-8");
|
|
13221
13547
|
spinner.succeed("Project summary published");
|
|
13222
|
-
console.log(` ${chalk26.green("\u2713")} ${
|
|
13548
|
+
console.log(` ${chalk26.green("\u2713")} ${path40.relative(dir, outputPath)}`);
|
|
13223
13549
|
console.log(chalk26.dim("\n Other projects can now reference this repo as a source."));
|
|
13224
13550
|
console.log(chalk26.dim(" When they run `caliber init`, they'll read this summary automatically.\n"));
|
|
13225
13551
|
} catch (err) {
|
|
@@ -13232,7 +13558,7 @@ async function publishCommand() {
|
|
|
13232
13558
|
|
|
13233
13559
|
// src/commands/bootstrap.ts
|
|
13234
13560
|
init_builtin_skills();
|
|
13235
|
-
import
|
|
13561
|
+
import fs49 from "fs";
|
|
13236
13562
|
import chalk27 from "chalk";
|
|
13237
13563
|
var PLATFORM_SKILL_DIRS = {
|
|
13238
13564
|
claude: ".claude/skills",
|
|
@@ -13253,8 +13579,8 @@ async function bootstrapCommand() {
|
|
|
13253
13579
|
for (const skill of BUILTIN_SKILLS) {
|
|
13254
13580
|
const skillDir = `${skillsDir}/${skill.name}`;
|
|
13255
13581
|
const skillPath = `${skillDir}/SKILL.md`;
|
|
13256
|
-
|
|
13257
|
-
|
|
13582
|
+
fs49.mkdirSync(skillDir, { recursive: true });
|
|
13583
|
+
fs49.writeFileSync(skillPath, buildSkillContent(skill));
|
|
13258
13584
|
written.push(skillPath);
|
|
13259
13585
|
}
|
|
13260
13586
|
}
|
|
@@ -13271,8 +13597,8 @@ async function bootstrapCommand() {
|
|
|
13271
13597
|
}
|
|
13272
13598
|
|
|
13273
13599
|
// src/commands/uninstall.ts
|
|
13274
|
-
import
|
|
13275
|
-
import
|
|
13600
|
+
import fs50 from "fs";
|
|
13601
|
+
import path41 from "path";
|
|
13276
13602
|
import chalk28 from "chalk";
|
|
13277
13603
|
import confirm3 from "@inquirer/confirm";
|
|
13278
13604
|
init_pre_commit_block();
|
|
@@ -13280,18 +13606,18 @@ init_builtin_skills();
|
|
|
13280
13606
|
init_config();
|
|
13281
13607
|
var MANAGED_DOC_FILES = [
|
|
13282
13608
|
"CLAUDE.md",
|
|
13283
|
-
|
|
13609
|
+
path41.join(".github", "copilot-instructions.md"),
|
|
13284
13610
|
"AGENTS.md"
|
|
13285
13611
|
];
|
|
13286
13612
|
var SKILL_DIRS = PLATFORM_CONFIGS.map((c) => c.skillsDir);
|
|
13287
|
-
var CURSOR_RULES_DIR =
|
|
13613
|
+
var CURSOR_RULES_DIR = path41.join(".cursor", "rules");
|
|
13288
13614
|
function removeCaliberCursorRules() {
|
|
13289
13615
|
const removed = [];
|
|
13290
|
-
if (!
|
|
13291
|
-
for (const file of
|
|
13616
|
+
if (!fs50.existsSync(CURSOR_RULES_DIR)) return removed;
|
|
13617
|
+
for (const file of fs50.readdirSync(CURSOR_RULES_DIR)) {
|
|
13292
13618
|
if (file.startsWith("caliber-") && file.endsWith(".mdc")) {
|
|
13293
|
-
const fullPath =
|
|
13294
|
-
|
|
13619
|
+
const fullPath = path41.join(CURSOR_RULES_DIR, file);
|
|
13620
|
+
fs50.unlinkSync(fullPath);
|
|
13295
13621
|
removed.push(fullPath);
|
|
13296
13622
|
}
|
|
13297
13623
|
}
|
|
@@ -13300,11 +13626,11 @@ function removeCaliberCursorRules() {
|
|
|
13300
13626
|
function removeBuiltinSkills() {
|
|
13301
13627
|
const removed = [];
|
|
13302
13628
|
for (const skillsDir of SKILL_DIRS) {
|
|
13303
|
-
if (!
|
|
13629
|
+
if (!fs50.existsSync(skillsDir)) continue;
|
|
13304
13630
|
for (const name of BUILTIN_SKILL_NAMES) {
|
|
13305
|
-
const skillDir =
|
|
13306
|
-
if (
|
|
13307
|
-
|
|
13631
|
+
const skillDir = path41.join(skillsDir, name);
|
|
13632
|
+
if (fs50.existsSync(skillDir)) {
|
|
13633
|
+
fs50.rmSync(skillDir, { recursive: true });
|
|
13308
13634
|
removed.push(skillDir);
|
|
13309
13635
|
}
|
|
13310
13636
|
}
|
|
@@ -13314,15 +13640,15 @@ function removeBuiltinSkills() {
|
|
|
13314
13640
|
function stripManagedBlocksFromFiles() {
|
|
13315
13641
|
const modified = [];
|
|
13316
13642
|
for (const filePath of MANAGED_DOC_FILES) {
|
|
13317
|
-
if (!
|
|
13318
|
-
const original =
|
|
13643
|
+
if (!fs50.existsSync(filePath)) continue;
|
|
13644
|
+
const original = fs50.readFileSync(filePath, "utf-8");
|
|
13319
13645
|
const stripped = stripManagedBlocks(original);
|
|
13320
13646
|
if (stripped !== original) {
|
|
13321
13647
|
const trimmed = stripped.trim();
|
|
13322
13648
|
if (!trimmed || /^#\s*\S*$/.test(trimmed)) {
|
|
13323
|
-
|
|
13649
|
+
fs50.unlinkSync(filePath);
|
|
13324
13650
|
} else {
|
|
13325
|
-
|
|
13651
|
+
fs50.writeFileSync(filePath, stripped);
|
|
13326
13652
|
}
|
|
13327
13653
|
modified.push(filePath);
|
|
13328
13654
|
}
|
|
@@ -13330,8 +13656,8 @@ function stripManagedBlocksFromFiles() {
|
|
|
13330
13656
|
return modified;
|
|
13331
13657
|
}
|
|
13332
13658
|
function removeDirectory(dir) {
|
|
13333
|
-
if (!
|
|
13334
|
-
|
|
13659
|
+
if (!fs50.existsSync(dir)) return false;
|
|
13660
|
+
fs50.rmSync(dir, { recursive: true });
|
|
13335
13661
|
return true;
|
|
13336
13662
|
}
|
|
13337
13663
|
async function uninstallCommand(options) {
|
|
@@ -13388,8 +13714,8 @@ async function uninstallCommand(options) {
|
|
|
13388
13714
|
console.log(` ${chalk28.red("\u2717")} ${skill}/`);
|
|
13389
13715
|
}
|
|
13390
13716
|
if (removedSkills.length > 0) actions.push("builtin skills");
|
|
13391
|
-
if (
|
|
13392
|
-
|
|
13717
|
+
if (fs50.existsSync("CALIBER_LEARNINGS.md")) {
|
|
13718
|
+
fs50.unlinkSync("CALIBER_LEARNINGS.md");
|
|
13393
13719
|
console.log(` ${chalk28.red("\u2717")} CALIBER_LEARNINGS.md`);
|
|
13394
13720
|
actions.push("learnings file");
|
|
13395
13721
|
}
|
|
@@ -13403,18 +13729,18 @@ async function uninstallCommand(options) {
|
|
|
13403
13729
|
}
|
|
13404
13730
|
trackUninstallExecuted();
|
|
13405
13731
|
const configPath = getConfigFilePath();
|
|
13406
|
-
if (
|
|
13732
|
+
if (fs50.existsSync(configPath)) {
|
|
13407
13733
|
console.log("");
|
|
13408
13734
|
const removeConfig = options.force || await confirm3({
|
|
13409
13735
|
message: `Remove global config (~/.caliber/config.json)? This affects all projects.`
|
|
13410
13736
|
});
|
|
13411
13737
|
if (removeConfig) {
|
|
13412
|
-
|
|
13738
|
+
fs50.unlinkSync(configPath);
|
|
13413
13739
|
console.log(` ${chalk28.red("\u2717")} ${configPath}`);
|
|
13414
|
-
const configDir =
|
|
13740
|
+
const configDir = path41.dirname(configPath);
|
|
13415
13741
|
try {
|
|
13416
|
-
const remaining =
|
|
13417
|
-
if (remaining.length === 0)
|
|
13742
|
+
const remaining = fs50.readdirSync(configDir);
|
|
13743
|
+
if (remaining.length === 0) fs50.rmdirSync(configDir);
|
|
13418
13744
|
} catch {
|
|
13419
13745
|
}
|
|
13420
13746
|
}
|
|
@@ -13425,10 +13751,8 @@ async function uninstallCommand(options) {
|
|
|
13425
13751
|
}
|
|
13426
13752
|
|
|
13427
13753
|
// src/cli.ts
|
|
13428
|
-
var __dirname =
|
|
13429
|
-
var pkg = JSON.parse(
|
|
13430
|
-
fs50.readFileSync(path41.resolve(__dirname, "..", "package.json"), "utf-8")
|
|
13431
|
-
);
|
|
13754
|
+
var __dirname = path42.dirname(fileURLToPath(import.meta.url));
|
|
13755
|
+
var pkg = JSON.parse(fs51.readFileSync(path42.resolve(__dirname, "..", "package.json"), "utf-8"));
|
|
13432
13756
|
var program = new Command();
|
|
13433
13757
|
var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
|
|
13434
13758
|
program.name(process.env.CALIBER_LOCAL ? "caloc" : "caliber").description("AI context infrastructure for coding agents").version(displayVersion).option("--no-traces", "Disable anonymous telemetry for this run");
|
|
@@ -13483,22 +13807,38 @@ function parseAgentOption(value) {
|
|
|
13483
13807
|
if (value === "both") return ["claude", "cursor"];
|
|
13484
13808
|
if (value === "all") return ["claude", "cursor", "codex", "opencode", "github-copilot"];
|
|
13485
13809
|
const valid = ["claude", "cursor", "codex", "opencode", "github-copilot"];
|
|
13486
|
-
const agents = [
|
|
13810
|
+
const agents = [
|
|
13811
|
+
...new Set(
|
|
13812
|
+
value.split(",").map((s) => s.trim().toLowerCase()).filter((a) => valid.includes(a))
|
|
13813
|
+
)
|
|
13814
|
+
];
|
|
13487
13815
|
if (agents.length === 0) {
|
|
13488
|
-
console.error(
|
|
13816
|
+
console.error(
|
|
13817
|
+
`Invalid agent "${value}". Choose from: claude, cursor, codex, opencode, github-copilot (comma-separated for multiple)`
|
|
13818
|
+
);
|
|
13489
13819
|
process.exit(1);
|
|
13490
13820
|
}
|
|
13491
13821
|
return agents;
|
|
13492
13822
|
}
|
|
13493
|
-
program.command("init").description("Initialize your project for AI-assisted development").option(
|
|
13494
|
-
|
|
13823
|
+
program.command("init").description("Initialize your project for AI-assisted development").option(
|
|
13824
|
+
"--agent <type>",
|
|
13825
|
+
"Target agents (comma-separated): claude, cursor, codex, opencode, github-copilot",
|
|
13826
|
+
parseAgentOption
|
|
13827
|
+
).option("--source <paths...>", "Related source paths to include as context").option("--dry-run", "Preview changes without writing files").option("--force", "Overwrite existing config without prompting").option("--debug-report", void 0, false).option("--show-tokens", "Show token usage summary at the end").option("--auto-approve", "Run without interactive prompts (auto-accept all)").option("--verbose", "Show detailed logs of each step").option("--thorough", "Deep analysis \u2014 more refinement passes for maximum quality").action(tracked("init", initCommand));
|
|
13828
|
+
program.command("bootstrap").description(
|
|
13829
|
+
"Install agent skills (/setup-caliber, /find-skills, /save-learning) without running init"
|
|
13830
|
+
).action(tracked("bootstrap", bootstrapCommand));
|
|
13495
13831
|
program.command("undo").description("Revert all config changes made by Caliber").action(tracked("undo", undoCommand));
|
|
13496
13832
|
program.command("uninstall").description("Remove all Caliber resources from this project").option("--force", "Skip confirmation prompt").action(tracked("uninstall", (options) => uninstallCommand(options)));
|
|
13497
13833
|
program.command("status").description("Show current Caliber config status").option("--json", "Output as JSON").action(tracked("status", statusCommand));
|
|
13498
13834
|
program.command("regenerate").alias("regen").alias("re").description("Re-analyze project and regenerate config").option("--dry-run", "Preview changes without writing files").action(tracked("regenerate", regenerateCommand));
|
|
13499
13835
|
program.command("config").description("Configure LLM provider, API key, and model").action(tracked("config", configCommand));
|
|
13500
13836
|
program.command("skills").description("Discover and install community skills for your project").option("--query <terms>", 'Search for skills by topic (e.g. "react frontend")').option("--install <slugs>", "Install specific skills by slug (comma-separated)").action(tracked("skills", recommendCommand));
|
|
13501
|
-
program.command("score").description("Score your AI context configuration (deterministic, no network)").option("--json", "Output as JSON").option("--quiet", "One-line output for scripts/hooks").option(
|
|
13837
|
+
program.command("score").description("Score your AI context configuration (deterministic, no network)").option("--json", "Output as JSON").option("--quiet", "One-line output for scripts/hooks").option(
|
|
13838
|
+
"--agent <type>",
|
|
13839
|
+
"Target agents (comma-separated): claude, cursor, codex, opencode, github-copilot",
|
|
13840
|
+
parseAgentOption
|
|
13841
|
+
).option("--compare <ref>", "Compare score against a git ref (branch, tag, or SHA)").action(tracked("score", scoreCommand));
|
|
13502
13842
|
program.command("refresh").description("Update docs based on recent code changes").option("--quiet", "Suppress output (for use in hooks)").option("--dry-run", "Preview changes without writing files").action(tracked("refresh", refreshCommand));
|
|
13503
13843
|
program.command("hooks").description("Manage auto-refresh hooks (toggle interactively)").option("--install", "Enable all hooks non-interactively").option("--remove", "Disable all hooks non-interactively").action(tracked("hooks", hooksCommand));
|
|
13504
13844
|
program.command("insights").description("Show agent performance insights and learning impact").option("--json", "Output as JSON").action(tracked("insights", insightsCommand));
|
|
@@ -13509,7 +13849,12 @@ sources.command("remove").description("Remove a configured source").argument("<n
|
|
|
13509
13849
|
program.command("publish").description("Generate a machine-readable summary for other repos to consume").action(tracked("publish", publishCommand));
|
|
13510
13850
|
var learn = program.command("learn").description("Manage session learning \u2014 extract patterns from your AI coding sessions");
|
|
13511
13851
|
learn.command("observe").description("Record a tool event from stdin (called by hooks)").option("--failure", "Mark event as a tool failure").option("--prompt", "Record a user prompt event").action(tracked("learn:observe", learnObserveCommand));
|
|
13512
|
-
learn.command("finalize").description("Analyze session events and update CALIBER_LEARNINGS.md (called on SessionEnd)").option("--force", "Skip the running-process check (for manual invocation)").option("--auto", "Silent mode for hooks (lower threshold, no interactive output)").option("--incremental", "Extract learnings mid-session without clearing events").action(
|
|
13852
|
+
learn.command("finalize").description("Analyze session events and update CALIBER_LEARNINGS.md (called on SessionEnd)").option("--force", "Skip the running-process check (for manual invocation)").option("--auto", "Silent mode for hooks (lower threshold, no interactive output)").option("--incremental", "Extract learnings mid-session without clearing events").action(
|
|
13853
|
+
tracked(
|
|
13854
|
+
"learn:finalize",
|
|
13855
|
+
(opts) => learnFinalizeCommand(opts)
|
|
13856
|
+
)
|
|
13857
|
+
);
|
|
13513
13858
|
learn.command("install").description("Install learning hooks into .claude/settings.json").action(tracked("learn:install", learnInstallCommand));
|
|
13514
13859
|
learn.command("remove").description("Remove learning hooks from .claude/settings.json").action(tracked("learn:remove", learnRemoveCommand));
|
|
13515
13860
|
learn.command("status").description("Show learning system status").action(tracked("learn:status", learnStatusCommand));
|
|
@@ -13518,15 +13863,15 @@ learn.command("delete <index>").description("Delete a learning by its index numb
|
|
|
13518
13863
|
learn.command("add <content>").description("Add a learning directly (used by agent skills)").option("--personal", "Save as a personal learning instead of project-level").action(tracked("learn:add", learnAddCommand));
|
|
13519
13864
|
|
|
13520
13865
|
// src/utils/version-check.ts
|
|
13521
|
-
import
|
|
13522
|
-
import
|
|
13866
|
+
import fs52 from "fs";
|
|
13867
|
+
import path43 from "path";
|
|
13523
13868
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
13524
13869
|
import { execSync as execSync16, execFileSync as execFileSync3 } from "child_process";
|
|
13525
13870
|
import chalk29 from "chalk";
|
|
13526
13871
|
import ora8 from "ora";
|
|
13527
13872
|
import confirm4 from "@inquirer/confirm";
|
|
13528
|
-
var __dirname_vc =
|
|
13529
|
-
var pkg2 = JSON.parse(
|
|
13873
|
+
var __dirname_vc = path43.dirname(fileURLToPath2(import.meta.url));
|
|
13874
|
+
var pkg2 = JSON.parse(fs52.readFileSync(path43.resolve(__dirname_vc, "..", "package.json"), "utf-8"));
|
|
13530
13875
|
function getChannel(version) {
|
|
13531
13876
|
const match = version.match(/-(dev|next)\./);
|
|
13532
13877
|
return match ? match[1] : "latest";
|
|
@@ -13553,8 +13898,8 @@ function getInstalledVersion() {
|
|
|
13553
13898
|
encoding: "utf-8",
|
|
13554
13899
|
stdio: ["pipe", "pipe", "pipe"]
|
|
13555
13900
|
}).trim();
|
|
13556
|
-
const pkgPath =
|
|
13557
|
-
return JSON.parse(
|
|
13901
|
+
const pkgPath = path43.join(globalRoot, "@rely-ai", "caliber", "package.json");
|
|
13902
|
+
return JSON.parse(fs52.readFileSync(pkgPath, "utf-8")).version;
|
|
13558
13903
|
} catch {
|
|
13559
13904
|
return null;
|
|
13560
13905
|
}
|