@rely-ai/caliber 1.30.0 → 1.30.2
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 +321 -259
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -158,30 +158,16 @@ var init_config = __esm({
|
|
|
158
158
|
}
|
|
159
159
|
});
|
|
160
160
|
|
|
161
|
-
// src/llm/types.ts
|
|
162
|
-
var types_exports = {};
|
|
163
|
-
__export(types_exports, {
|
|
164
|
-
isSeatBased: () => isSeatBased
|
|
165
|
-
});
|
|
166
|
-
function isSeatBased(provider) {
|
|
167
|
-
return SEAT_BASED_PROVIDERS.has(provider);
|
|
168
|
-
}
|
|
169
|
-
var SEAT_BASED_PROVIDERS;
|
|
170
|
-
var init_types = __esm({
|
|
171
|
-
"src/llm/types.ts"() {
|
|
172
|
-
"use strict";
|
|
173
|
-
SEAT_BASED_PROVIDERS = /* @__PURE__ */ new Set(["cursor", "claude-cli"]);
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
|
|
177
161
|
// src/lib/resolve-caliber.ts
|
|
178
162
|
var resolve_caliber_exports = {};
|
|
179
163
|
__export(resolve_caliber_exports, {
|
|
180
164
|
isCaliberCommand: () => isCaliberCommand,
|
|
165
|
+
isNpxResolution: () => isNpxResolution,
|
|
166
|
+
resetResolvedCaliber: () => resetResolvedCaliber,
|
|
181
167
|
resolveCaliber: () => resolveCaliber
|
|
182
168
|
});
|
|
183
|
-
import
|
|
184
|
-
import { execSync as
|
|
169
|
+
import fs6 from "fs";
|
|
170
|
+
import { execSync as execSync4 } from "child_process";
|
|
185
171
|
function resolveCaliber() {
|
|
186
172
|
if (_resolved) return _resolved;
|
|
187
173
|
const isNpx = process.argv[1]?.includes("_npx") || process.env.npm_execpath?.includes("npx");
|
|
@@ -191,7 +177,7 @@ function resolveCaliber() {
|
|
|
191
177
|
}
|
|
192
178
|
try {
|
|
193
179
|
const whichCmd = process.platform === "win32" ? "where caliber" : "which caliber";
|
|
194
|
-
|
|
180
|
+
execSync4(whichCmd, {
|
|
195
181
|
encoding: "utf-8",
|
|
196
182
|
stdio: ["pipe", "pipe", "pipe"]
|
|
197
183
|
});
|
|
@@ -200,13 +186,19 @@ function resolveCaliber() {
|
|
|
200
186
|
} catch {
|
|
201
187
|
}
|
|
202
188
|
const binPath = process.argv[1];
|
|
203
|
-
if (binPath &&
|
|
189
|
+
if (binPath && /caliber/.test(binPath) && fs6.existsSync(binPath)) {
|
|
204
190
|
_resolved = binPath;
|
|
205
191
|
return _resolved;
|
|
206
192
|
}
|
|
207
193
|
_resolved = "caliber";
|
|
208
194
|
return _resolved;
|
|
209
195
|
}
|
|
196
|
+
function isNpxResolution() {
|
|
197
|
+
return resolveCaliber().startsWith("npx ");
|
|
198
|
+
}
|
|
199
|
+
function resetResolvedCaliber() {
|
|
200
|
+
_resolved = null;
|
|
201
|
+
}
|
|
210
202
|
function isCaliberCommand(command, subcommandTail) {
|
|
211
203
|
if (command === `caliber ${subcommandTail}`) return true;
|
|
212
204
|
if (command.endsWith(`/caliber ${subcommandTail}`)) return true;
|
|
@@ -222,6 +214,22 @@ var init_resolve_caliber = __esm({
|
|
|
222
214
|
}
|
|
223
215
|
});
|
|
224
216
|
|
|
217
|
+
// src/llm/types.ts
|
|
218
|
+
var types_exports = {};
|
|
219
|
+
__export(types_exports, {
|
|
220
|
+
isSeatBased: () => isSeatBased
|
|
221
|
+
});
|
|
222
|
+
function isSeatBased(provider) {
|
|
223
|
+
return SEAT_BASED_PROVIDERS.has(provider);
|
|
224
|
+
}
|
|
225
|
+
var SEAT_BASED_PROVIDERS;
|
|
226
|
+
var init_types = __esm({
|
|
227
|
+
"src/llm/types.ts"() {
|
|
228
|
+
"use strict";
|
|
229
|
+
SEAT_BASED_PROVIDERS = /* @__PURE__ */ new Set(["cursor", "claude-cli"]);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
225
233
|
// src/utils/editor.ts
|
|
226
234
|
import { execSync as execSync13, spawn as spawn3 } from "child_process";
|
|
227
235
|
import fs26 from "fs";
|
|
@@ -415,7 +423,7 @@ async function interactiveDiffExplorer(files) {
|
|
|
415
423
|
stdout.write(output + "\n");
|
|
416
424
|
lineCount = output.split("\n").length;
|
|
417
425
|
}
|
|
418
|
-
return new Promise((
|
|
426
|
+
return new Promise((resolve3) => {
|
|
419
427
|
console.log("");
|
|
420
428
|
draw(true);
|
|
421
429
|
stdin.setRawMode(true);
|
|
@@ -450,7 +458,7 @@ async function interactiveDiffExplorer(files) {
|
|
|
450
458
|
case "":
|
|
451
459
|
cleanup();
|
|
452
460
|
console.log("");
|
|
453
|
-
|
|
461
|
+
resolve3();
|
|
454
462
|
break;
|
|
455
463
|
}
|
|
456
464
|
} else {
|
|
@@ -473,7 +481,7 @@ async function interactiveDiffExplorer(files) {
|
|
|
473
481
|
case "":
|
|
474
482
|
cleanup();
|
|
475
483
|
console.log("");
|
|
476
|
-
|
|
484
|
+
resolve3();
|
|
477
485
|
break;
|
|
478
486
|
}
|
|
479
487
|
}
|
|
@@ -547,7 +555,7 @@ import chalk14 from "chalk";
|
|
|
547
555
|
import fs32 from "fs";
|
|
548
556
|
|
|
549
557
|
// src/fingerprint/index.ts
|
|
550
|
-
import
|
|
558
|
+
import fs8 from "fs";
|
|
551
559
|
import path7 from "path";
|
|
552
560
|
|
|
553
561
|
// src/fingerprint/git.ts
|
|
@@ -1590,19 +1598,20 @@ var OpenAICompatProvider = class {
|
|
|
1590
1598
|
};
|
|
1591
1599
|
|
|
1592
1600
|
// src/llm/cursor-acp.ts
|
|
1593
|
-
import { spawn, execSync as
|
|
1601
|
+
import { spawn, execSync as execSync5 } from "child_process";
|
|
1594
1602
|
import os3 from "os";
|
|
1595
1603
|
|
|
1596
1604
|
// src/llm/seat-based-errors.ts
|
|
1605
|
+
init_resolve_caliber();
|
|
1597
1606
|
var ERROR_PATTERNS = [
|
|
1598
1607
|
{ pattern: /not logged in|not authenticated|login required|unauthorized/i, message: "Authentication required. Run the login command for your provider to re-authenticate." },
|
|
1599
1608
|
{ pattern: /rate limit|too many requests|429/i, message: "Rate limit exceeded. Retrying..." },
|
|
1600
|
-
{ pattern: /model.*not found|invalid model|model.*unavailable/i, message:
|
|
1609
|
+
{ pattern: /model.*not found|invalid model|model.*unavailable/i, message: () => `The requested model is not available. Run \`${resolveCaliber()} config\` to select a different model.` }
|
|
1601
1610
|
];
|
|
1602
1611
|
function parseSeatBasedError(stderr, exitCode) {
|
|
1603
1612
|
if (!stderr && exitCode === 0) return null;
|
|
1604
1613
|
for (const { pattern, message } of ERROR_PATTERNS) {
|
|
1605
|
-
if (pattern.test(stderr)) return message;
|
|
1614
|
+
if (pattern.test(stderr)) return typeof message === "function" ? message() : message;
|
|
1606
1615
|
}
|
|
1607
1616
|
return null;
|
|
1608
1617
|
}
|
|
@@ -1722,7 +1731,7 @@ var CursorAcpProvider = class {
|
|
|
1722
1731
|
return stderr ? `${base}: ${stderr.slice(0, 200)}` : base;
|
|
1723
1732
|
}
|
|
1724
1733
|
runPrint(model, prompt) {
|
|
1725
|
-
return new Promise((
|
|
1734
|
+
return new Promise((resolve3, reject) => {
|
|
1726
1735
|
const { child, stderrChunks } = this.spawnAgent(model, false);
|
|
1727
1736
|
let settled = false;
|
|
1728
1737
|
const chunks = [];
|
|
@@ -1750,7 +1759,7 @@ var CursorAcpProvider = class {
|
|
|
1750
1759
|
if (code !== 0 && !output) {
|
|
1751
1760
|
reject(new Error(this.buildErrorMessage(code, stderrChunks)));
|
|
1752
1761
|
} else {
|
|
1753
|
-
|
|
1762
|
+
resolve3(output);
|
|
1754
1763
|
}
|
|
1755
1764
|
});
|
|
1756
1765
|
child.stdin.write(prompt);
|
|
@@ -1758,7 +1767,7 @@ var CursorAcpProvider = class {
|
|
|
1758
1767
|
});
|
|
1759
1768
|
}
|
|
1760
1769
|
runPrintStream(model, prompt, callbacks) {
|
|
1761
|
-
return new Promise((
|
|
1770
|
+
return new Promise((resolve3, reject) => {
|
|
1762
1771
|
const { child, stderrChunks } = this.spawnAgent(model, true);
|
|
1763
1772
|
let buffer = "";
|
|
1764
1773
|
let endCalled = false;
|
|
@@ -1840,7 +1849,7 @@ var CursorAcpProvider = class {
|
|
|
1840
1849
|
reject(err);
|
|
1841
1850
|
}
|
|
1842
1851
|
} else {
|
|
1843
|
-
|
|
1852
|
+
resolve3();
|
|
1844
1853
|
}
|
|
1845
1854
|
});
|
|
1846
1855
|
child.stdin.write(prompt);
|
|
@@ -1866,7 +1875,7 @@ var CursorAcpProvider = class {
|
|
|
1866
1875
|
function isCursorAgentAvailable() {
|
|
1867
1876
|
try {
|
|
1868
1877
|
const cmd = IS_WINDOWS ? `where ${AGENT_BIN}` : `which ${AGENT_BIN}`;
|
|
1869
|
-
|
|
1878
|
+
execSync5(cmd, { stdio: "ignore" });
|
|
1870
1879
|
return true;
|
|
1871
1880
|
} catch {
|
|
1872
1881
|
return false;
|
|
@@ -1874,7 +1883,7 @@ function isCursorAgentAvailable() {
|
|
|
1874
1883
|
}
|
|
1875
1884
|
function isCursorLoggedIn() {
|
|
1876
1885
|
try {
|
|
1877
|
-
const result =
|
|
1886
|
+
const result = execSync5(`${AGENT_BIN} status`, { stdio: ["ignore", "pipe", "ignore"], timeout: 5e3 });
|
|
1878
1887
|
return !result.toString().includes("not logged in");
|
|
1879
1888
|
} catch {
|
|
1880
1889
|
return false;
|
|
@@ -1882,7 +1891,7 @@ function isCursorLoggedIn() {
|
|
|
1882
1891
|
}
|
|
1883
1892
|
|
|
1884
1893
|
// src/llm/claude-cli.ts
|
|
1885
|
-
import { spawn as spawn2, execSync as
|
|
1894
|
+
import { spawn as spawn2, execSync as execSync6 } from "child_process";
|
|
1886
1895
|
var CLAUDE_CLI_BIN = "claude";
|
|
1887
1896
|
var DEFAULT_TIMEOUT_MS2 = 10 * 60 * 1e3;
|
|
1888
1897
|
var IS_WINDOWS2 = process.platform === "win32";
|
|
@@ -1977,7 +1986,7 @@ var ClaudeCliProvider = class {
|
|
|
1977
1986
|
return combined;
|
|
1978
1987
|
}
|
|
1979
1988
|
runClaudePrint(combinedPrompt, model) {
|
|
1980
|
-
return new Promise((
|
|
1989
|
+
return new Promise((resolve3, reject) => {
|
|
1981
1990
|
const args = ["-p"];
|
|
1982
1991
|
if (model) args.push("--model", model);
|
|
1983
1992
|
const child = spawnClaude(args);
|
|
@@ -1994,7 +2003,7 @@ var ClaudeCliProvider = class {
|
|
|
1994
2003
|
clearTimeout(timer);
|
|
1995
2004
|
const stdout = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1996
2005
|
if (code === 0) {
|
|
1997
|
-
|
|
2006
|
+
resolve3(stdout);
|
|
1998
2007
|
} else {
|
|
1999
2008
|
const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
|
|
2000
2009
|
const friendly = parseSeatBasedError(stderr, code);
|
|
@@ -2017,7 +2026,7 @@ var ClaudeCliProvider = class {
|
|
|
2017
2026
|
function isClaudeCliAvailable() {
|
|
2018
2027
|
try {
|
|
2019
2028
|
const cmd = process.platform === "win32" ? `where ${CLAUDE_CLI_BIN}` : `which ${CLAUDE_CLI_BIN}`;
|
|
2020
|
-
|
|
2029
|
+
execSync6(cmd, { stdio: "ignore" });
|
|
2021
2030
|
return true;
|
|
2022
2031
|
} catch {
|
|
2023
2032
|
return false;
|
|
@@ -2075,6 +2084,7 @@ function estimateTokens(text) {
|
|
|
2075
2084
|
|
|
2076
2085
|
// src/llm/model-recovery.ts
|
|
2077
2086
|
init_config();
|
|
2087
|
+
init_resolve_caliber();
|
|
2078
2088
|
import chalk from "chalk";
|
|
2079
2089
|
import select from "@inquirer/select";
|
|
2080
2090
|
var KNOWN_MODELS = {
|
|
@@ -2128,7 +2138,7 @@ function filterRelevantModels(models, provider) {
|
|
|
2128
2138
|
async function handleModelNotAvailable(failedModel, provider, config) {
|
|
2129
2139
|
if (!process.stdin.isTTY) {
|
|
2130
2140
|
console.error(
|
|
2131
|
-
chalk.red(`Model "${failedModel}" is not available. Run
|
|
2141
|
+
chalk.red(`Model "${failedModel}" is not available. Run \`${resolveCaliber()} config\` to select a different model.`)
|
|
2132
2142
|
);
|
|
2133
2143
|
return null;
|
|
2134
2144
|
}
|
|
@@ -2147,7 +2157,7 @@ async function handleModelNotAvailable(failedModel, provider, config) {
|
|
|
2147
2157
|
}
|
|
2148
2158
|
models = models.filter((m) => m !== failedModel);
|
|
2149
2159
|
if (models.length === 0) {
|
|
2150
|
-
console.log(chalk.red(
|
|
2160
|
+
console.log(chalk.red(` No alternative models found. Run \`${resolveCaliber()} config\` to configure manually.`));
|
|
2151
2161
|
return null;
|
|
2152
2162
|
}
|
|
2153
2163
|
console.log("");
|
|
@@ -2174,6 +2184,7 @@ async function handleModelNotAvailable(failedModel, provider, config) {
|
|
|
2174
2184
|
}
|
|
2175
2185
|
|
|
2176
2186
|
// src/llm/index.ts
|
|
2187
|
+
init_resolve_caliber();
|
|
2177
2188
|
init_types();
|
|
2178
2189
|
init_config();
|
|
2179
2190
|
var cachedProvider = null;
|
|
@@ -2211,7 +2222,7 @@ function getProvider() {
|
|
|
2211
2222
|
const config = loadConfig();
|
|
2212
2223
|
if (!config) {
|
|
2213
2224
|
throw new Error(
|
|
2214
|
-
|
|
2225
|
+
`No LLM provider configured. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or VERTEX_PROJECT_ID; or run \`${resolveCaliber()} config\` and choose Cursor or Claude Code; or set CALIBER_USE_CURSOR_SEAT=1 / CALIBER_USE_CLAUDE_CLI=1.`
|
|
2215
2226
|
);
|
|
2216
2227
|
}
|
|
2217
2228
|
cachedConfig = config;
|
|
@@ -2774,10 +2785,10 @@ async function detectProjectStack(fileTree, suffixCounts) {
|
|
|
2774
2785
|
init_config();
|
|
2775
2786
|
|
|
2776
2787
|
// src/fingerprint/cache.ts
|
|
2777
|
-
import
|
|
2788
|
+
import fs7 from "fs";
|
|
2778
2789
|
import path6 from "path";
|
|
2779
2790
|
import crypto from "crypto";
|
|
2780
|
-
import { execSync as
|
|
2791
|
+
import { execSync as execSync7 } from "child_process";
|
|
2781
2792
|
var CACHE_VERSION = 1;
|
|
2782
2793
|
var CACHE_DIR = ".caliber/cache";
|
|
2783
2794
|
var CACHE_FILE = "fingerprint.json";
|
|
@@ -2786,7 +2797,7 @@ function getCachePath(dir) {
|
|
|
2786
2797
|
}
|
|
2787
2798
|
function getGitHead(dir) {
|
|
2788
2799
|
try {
|
|
2789
|
-
return
|
|
2800
|
+
return execSync7("git rev-parse HEAD", {
|
|
2790
2801
|
cwd: dir,
|
|
2791
2802
|
encoding: "utf-8",
|
|
2792
2803
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -2798,7 +2809,7 @@ function getGitHead(dir) {
|
|
|
2798
2809
|
}
|
|
2799
2810
|
function getDirtySignature(dir) {
|
|
2800
2811
|
try {
|
|
2801
|
-
const output =
|
|
2812
|
+
const output = execSync7("git diff --name-only HEAD", {
|
|
2802
2813
|
cwd: dir,
|
|
2803
2814
|
encoding: "utf-8",
|
|
2804
2815
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -2819,8 +2830,8 @@ function computeTreeSignature(fileTree, dir) {
|
|
|
2819
2830
|
function loadFingerprintCache(dir, fileTree) {
|
|
2820
2831
|
const cachePath = getCachePath(dir);
|
|
2821
2832
|
try {
|
|
2822
|
-
if (!
|
|
2823
|
-
const raw =
|
|
2833
|
+
if (!fs7.existsSync(cachePath)) return null;
|
|
2834
|
+
const raw = fs7.readFileSync(cachePath, "utf-8");
|
|
2824
2835
|
const cache = JSON.parse(raw);
|
|
2825
2836
|
if (cache.version !== CACHE_VERSION) return null;
|
|
2826
2837
|
const currentHead = getGitHead(dir);
|
|
@@ -2842,8 +2853,8 @@ function saveFingerprintCache(dir, fileTree, codeAnalysis, languages, frameworks
|
|
|
2842
2853
|
const cachePath = getCachePath(dir);
|
|
2843
2854
|
try {
|
|
2844
2855
|
const cacheDir = path6.dirname(cachePath);
|
|
2845
|
-
if (!
|
|
2846
|
-
|
|
2856
|
+
if (!fs7.existsSync(cacheDir)) {
|
|
2857
|
+
fs7.mkdirSync(cacheDir, { recursive: true });
|
|
2847
2858
|
}
|
|
2848
2859
|
const cache = {
|
|
2849
2860
|
version: CACHE_VERSION,
|
|
@@ -2855,15 +2866,15 @@ function saveFingerprintCache(dir, fileTree, codeAnalysis, languages, frameworks
|
|
|
2855
2866
|
tools,
|
|
2856
2867
|
workspaces
|
|
2857
2868
|
};
|
|
2858
|
-
|
|
2869
|
+
fs7.writeFileSync(cachePath, JSON.stringify(cache), "utf-8");
|
|
2859
2870
|
} catch {
|
|
2860
2871
|
}
|
|
2861
2872
|
}
|
|
2862
2873
|
function getDetectedWorkspaces(dir) {
|
|
2863
2874
|
const cachePath = getCachePath(dir);
|
|
2864
2875
|
try {
|
|
2865
|
-
if (!
|
|
2866
|
-
const raw =
|
|
2876
|
+
if (!fs7.existsSync(cachePath)) return [];
|
|
2877
|
+
const raw = fs7.readFileSync(cachePath, "utf-8");
|
|
2867
2878
|
const cache = JSON.parse(raw);
|
|
2868
2879
|
return cache.workspaces ?? [];
|
|
2869
2880
|
} catch {
|
|
@@ -2916,8 +2927,8 @@ async function collectFingerprint(dir) {
|
|
|
2916
2927
|
function readPackageName(dir) {
|
|
2917
2928
|
try {
|
|
2918
2929
|
const pkgPath = path7.join(dir, "package.json");
|
|
2919
|
-
if (!
|
|
2920
|
-
const pkg3 = JSON.parse(
|
|
2930
|
+
if (!fs8.existsSync(pkgPath)) return void 0;
|
|
2931
|
+
const pkg3 = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
|
|
2921
2932
|
return pkg3.name;
|
|
2922
2933
|
} catch {
|
|
2923
2934
|
return void 0;
|
|
@@ -2947,22 +2958,22 @@ async function enrichWithLLM(fingerprint) {
|
|
|
2947
2958
|
}
|
|
2948
2959
|
|
|
2949
2960
|
// src/scanner/index.ts
|
|
2950
|
-
import
|
|
2961
|
+
import fs9 from "fs";
|
|
2951
2962
|
import path8 from "path";
|
|
2952
2963
|
import crypto2 from "crypto";
|
|
2953
2964
|
import os4 from "os";
|
|
2954
2965
|
function detectPlatforms() {
|
|
2955
2966
|
const home = os4.homedir();
|
|
2956
2967
|
return {
|
|
2957
|
-
claude:
|
|
2958
|
-
cursor:
|
|
2959
|
-
codex:
|
|
2968
|
+
claude: fs9.existsSync(path8.join(home, ".claude")),
|
|
2969
|
+
cursor: fs9.existsSync(getCursorConfigDir()),
|
|
2970
|
+
codex: fs9.existsSync(path8.join(home, ".codex"))
|
|
2960
2971
|
};
|
|
2961
2972
|
}
|
|
2962
2973
|
function scanLocalState(dir) {
|
|
2963
2974
|
const items = [];
|
|
2964
2975
|
const claudeMdPath = path8.join(dir, "CLAUDE.md");
|
|
2965
|
-
if (
|
|
2976
|
+
if (fs9.existsSync(claudeMdPath)) {
|
|
2966
2977
|
items.push({
|
|
2967
2978
|
type: "rule",
|
|
2968
2979
|
platform: "claude",
|
|
@@ -2972,8 +2983,8 @@ function scanLocalState(dir) {
|
|
|
2972
2983
|
});
|
|
2973
2984
|
}
|
|
2974
2985
|
const skillsDir = path8.join(dir, ".claude", "skills");
|
|
2975
|
-
if (
|
|
2976
|
-
for (const file of
|
|
2986
|
+
if (fs9.existsSync(skillsDir)) {
|
|
2987
|
+
for (const file of fs9.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
|
|
2977
2988
|
const filePath = path8.join(skillsDir, file);
|
|
2978
2989
|
items.push({
|
|
2979
2990
|
type: "skill",
|
|
@@ -2985,9 +2996,9 @@ function scanLocalState(dir) {
|
|
|
2985
2996
|
}
|
|
2986
2997
|
}
|
|
2987
2998
|
const mcpJsonPath = path8.join(dir, ".mcp.json");
|
|
2988
|
-
if (
|
|
2999
|
+
if (fs9.existsSync(mcpJsonPath)) {
|
|
2989
3000
|
try {
|
|
2990
|
-
const mcpJson = JSON.parse(
|
|
3001
|
+
const mcpJson = JSON.parse(fs9.readFileSync(mcpJsonPath, "utf-8"));
|
|
2991
3002
|
if (mcpJson.mcpServers) {
|
|
2992
3003
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
2993
3004
|
items.push({
|
|
@@ -3004,7 +3015,7 @@ function scanLocalState(dir) {
|
|
|
3004
3015
|
}
|
|
3005
3016
|
}
|
|
3006
3017
|
const agentsMdPath = path8.join(dir, "AGENTS.md");
|
|
3007
|
-
if (
|
|
3018
|
+
if (fs9.existsSync(agentsMdPath)) {
|
|
3008
3019
|
items.push({
|
|
3009
3020
|
type: "rule",
|
|
3010
3021
|
platform: "codex",
|
|
@@ -3014,11 +3025,11 @@ function scanLocalState(dir) {
|
|
|
3014
3025
|
});
|
|
3015
3026
|
}
|
|
3016
3027
|
const codexSkillsDir = path8.join(dir, ".agents", "skills");
|
|
3017
|
-
if (
|
|
3028
|
+
if (fs9.existsSync(codexSkillsDir)) {
|
|
3018
3029
|
try {
|
|
3019
|
-
for (const name of
|
|
3030
|
+
for (const name of fs9.readdirSync(codexSkillsDir)) {
|
|
3020
3031
|
const skillFile = path8.join(codexSkillsDir, name, "SKILL.md");
|
|
3021
|
-
if (
|
|
3032
|
+
if (fs9.existsSync(skillFile)) {
|
|
3022
3033
|
items.push({
|
|
3023
3034
|
type: "skill",
|
|
3024
3035
|
platform: "codex",
|
|
@@ -3033,7 +3044,7 @@ function scanLocalState(dir) {
|
|
|
3033
3044
|
}
|
|
3034
3045
|
}
|
|
3035
3046
|
const cursorrulesPath = path8.join(dir, ".cursorrules");
|
|
3036
|
-
if (
|
|
3047
|
+
if (fs9.existsSync(cursorrulesPath)) {
|
|
3037
3048
|
items.push({
|
|
3038
3049
|
type: "rule",
|
|
3039
3050
|
platform: "cursor",
|
|
@@ -3043,8 +3054,8 @@ function scanLocalState(dir) {
|
|
|
3043
3054
|
});
|
|
3044
3055
|
}
|
|
3045
3056
|
const cursorRulesDir = path8.join(dir, ".cursor", "rules");
|
|
3046
|
-
if (
|
|
3047
|
-
for (const file of
|
|
3057
|
+
if (fs9.existsSync(cursorRulesDir)) {
|
|
3058
|
+
for (const file of fs9.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
|
|
3048
3059
|
const filePath = path8.join(cursorRulesDir, file);
|
|
3049
3060
|
items.push({
|
|
3050
3061
|
type: "rule",
|
|
@@ -3056,11 +3067,11 @@ function scanLocalState(dir) {
|
|
|
3056
3067
|
}
|
|
3057
3068
|
}
|
|
3058
3069
|
const cursorSkillsDir = path8.join(dir, ".cursor", "skills");
|
|
3059
|
-
if (
|
|
3070
|
+
if (fs9.existsSync(cursorSkillsDir)) {
|
|
3060
3071
|
try {
|
|
3061
|
-
for (const name of
|
|
3072
|
+
for (const name of fs9.readdirSync(cursorSkillsDir)) {
|
|
3062
3073
|
const skillFile = path8.join(cursorSkillsDir, name, "SKILL.md");
|
|
3063
|
-
if (
|
|
3074
|
+
if (fs9.existsSync(skillFile)) {
|
|
3064
3075
|
items.push({
|
|
3065
3076
|
type: "skill",
|
|
3066
3077
|
platform: "cursor",
|
|
@@ -3075,9 +3086,9 @@ function scanLocalState(dir) {
|
|
|
3075
3086
|
}
|
|
3076
3087
|
}
|
|
3077
3088
|
const cursorMcpPath = path8.join(dir, ".cursor", "mcp.json");
|
|
3078
|
-
if (
|
|
3089
|
+
if (fs9.existsSync(cursorMcpPath)) {
|
|
3079
3090
|
try {
|
|
3080
|
-
const mcpJson = JSON.parse(
|
|
3091
|
+
const mcpJson = JSON.parse(fs9.readFileSync(cursorMcpPath, "utf-8"));
|
|
3081
3092
|
if (mcpJson.mcpServers) {
|
|
3082
3093
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
3083
3094
|
items.push({
|
|
@@ -3096,7 +3107,7 @@ function scanLocalState(dir) {
|
|
|
3096
3107
|
return items;
|
|
3097
3108
|
}
|
|
3098
3109
|
function hashFile(filePath) {
|
|
3099
|
-
const text =
|
|
3110
|
+
const text = fs9.readFileSync(filePath, "utf-8");
|
|
3100
3111
|
return crypto2.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
|
|
3101
3112
|
}
|
|
3102
3113
|
function hashJson(obj) {
|
|
@@ -3118,7 +3129,7 @@ function getCursorConfigDir() {
|
|
|
3118
3129
|
}
|
|
3119
3130
|
|
|
3120
3131
|
// src/fingerprint/sources.ts
|
|
3121
|
-
import
|
|
3132
|
+
import fs10 from "fs";
|
|
3122
3133
|
import path9 from "path";
|
|
3123
3134
|
|
|
3124
3135
|
// src/scoring/utils.ts
|
|
@@ -3457,15 +3468,15 @@ function loadSourcesConfig(dir) {
|
|
|
3457
3468
|
}
|
|
3458
3469
|
function writeSourcesConfig(dir, sources2) {
|
|
3459
3470
|
const configDir = path9.join(dir, ".caliber");
|
|
3460
|
-
if (!
|
|
3461
|
-
|
|
3471
|
+
if (!fs10.existsSync(configDir)) {
|
|
3472
|
+
fs10.mkdirSync(configDir, { recursive: true });
|
|
3462
3473
|
}
|
|
3463
3474
|
const configPath = path9.join(configDir, "sources.json");
|
|
3464
|
-
|
|
3475
|
+
fs10.writeFileSync(configPath, JSON.stringify({ sources: sources2 }, null, 2) + "\n", "utf-8");
|
|
3465
3476
|
}
|
|
3466
3477
|
function detectSourceType(absPath) {
|
|
3467
3478
|
try {
|
|
3468
|
-
return
|
|
3479
|
+
return fs10.statSync(absPath).isDirectory() ? "repo" : "file";
|
|
3469
3480
|
} catch {
|
|
3470
3481
|
return "file";
|
|
3471
3482
|
}
|
|
@@ -3509,7 +3520,7 @@ function resolveAllSources(dir, cliSources, workspaces) {
|
|
|
3509
3520
|
for (const [absPath, resolved] of seen) {
|
|
3510
3521
|
let stat;
|
|
3511
3522
|
try {
|
|
3512
|
-
stat =
|
|
3523
|
+
stat = fs10.statSync(absPath);
|
|
3513
3524
|
} catch {
|
|
3514
3525
|
console.warn(`Source ${resolved.config.path || absPath} not found, skipping`);
|
|
3515
3526
|
continue;
|
|
@@ -3564,7 +3575,7 @@ function collectRepoSummary(resolved, projectDir) {
|
|
|
3564
3575
|
let topLevelDirs;
|
|
3565
3576
|
let keyFiles;
|
|
3566
3577
|
try {
|
|
3567
|
-
const entries =
|
|
3578
|
+
const entries = fs10.readdirSync(absPath, { withFileTypes: true });
|
|
3568
3579
|
topLevelDirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name).slice(0, 20);
|
|
3569
3580
|
keyFiles = entries.filter((e) => e.isFile() && !e.name.startsWith(".")).map((e) => e.name).slice(0, 15);
|
|
3570
3581
|
} catch {
|
|
@@ -3891,7 +3902,7 @@ async function streamGeneration(config) {
|
|
|
3891
3902
|
config.baseMaxTokens + attempt * config.tokenIncrement,
|
|
3892
3903
|
config.maxTokensCap
|
|
3893
3904
|
);
|
|
3894
|
-
return new Promise((
|
|
3905
|
+
return new Promise((resolve3) => {
|
|
3895
3906
|
let preJsonBuffer = "";
|
|
3896
3907
|
let jsonContent = "";
|
|
3897
3908
|
let inJson = false;
|
|
@@ -3946,7 +3957,7 @@ async function streamGeneration(config) {
|
|
|
3946
3957
|
}
|
|
3947
3958
|
if (!setup && stopReason === "max_tokens" && attempt < MAX_RETRIES2) {
|
|
3948
3959
|
if (config.callbacks) config.callbacks.onStatus("Output was truncated, retrying with higher token limit...");
|
|
3949
|
-
setTimeout(() => attemptGeneration().then(
|
|
3960
|
+
setTimeout(() => attemptGeneration().then(resolve3), 1e3);
|
|
3950
3961
|
return;
|
|
3951
3962
|
}
|
|
3952
3963
|
let explanation;
|
|
@@ -3956,24 +3967,24 @@ async function streamGeneration(config) {
|
|
|
3956
3967
|
}
|
|
3957
3968
|
if (setup) {
|
|
3958
3969
|
if (config.callbacks) config.callbacks.onComplete(setup, explanation);
|
|
3959
|
-
|
|
3970
|
+
resolve3({ setup, explanation, stopReason: stopReason ?? void 0 });
|
|
3960
3971
|
} else {
|
|
3961
|
-
|
|
3972
|
+
resolve3({ setup: null, explanation, raw: preJsonBuffer, stopReason: stopReason ?? void 0 });
|
|
3962
3973
|
}
|
|
3963
3974
|
},
|
|
3964
3975
|
onError: (error) => {
|
|
3965
3976
|
if (isTransientError2(error) && attempt < MAX_RETRIES2) {
|
|
3966
3977
|
if (config.callbacks) config.callbacks.onStatus("Connection interrupted, retrying...");
|
|
3967
|
-
setTimeout(() => attemptGeneration().then(
|
|
3978
|
+
setTimeout(() => attemptGeneration().then(resolve3), 2e3);
|
|
3968
3979
|
return;
|
|
3969
3980
|
}
|
|
3970
3981
|
if (config.callbacks) config.callbacks.onError(error.message);
|
|
3971
|
-
|
|
3982
|
+
resolve3({ setup: null, raw: error.message, stopReason: "error" });
|
|
3972
3983
|
}
|
|
3973
3984
|
}
|
|
3974
3985
|
).catch((error) => {
|
|
3975
3986
|
if (config.callbacks) config.callbacks.onError(error.message);
|
|
3976
|
-
|
|
3987
|
+
resolve3({ setup: null, raw: error.message, stopReason: "error" });
|
|
3977
3988
|
});
|
|
3978
3989
|
});
|
|
3979
3990
|
};
|
|
@@ -4231,44 +4242,51 @@ ${f.content}
|
|
|
4231
4242
|
}
|
|
4232
4243
|
|
|
4233
4244
|
// src/writers/index.ts
|
|
4234
|
-
import
|
|
4245
|
+
import fs18 from "fs";
|
|
4235
4246
|
|
|
4236
4247
|
// src/writers/claude/index.ts
|
|
4237
|
-
import
|
|
4248
|
+
import fs11 from "fs";
|
|
4238
4249
|
import path10 from "path";
|
|
4239
4250
|
|
|
4240
4251
|
// src/writers/pre-commit-block.ts
|
|
4252
|
+
init_resolve_caliber();
|
|
4241
4253
|
var BLOCK_START = "<!-- caliber:managed:pre-commit -->";
|
|
4242
4254
|
var BLOCK_END = "<!-- /caliber:managed:pre-commit -->";
|
|
4243
4255
|
var MANAGED_DOC_PATHS = "CLAUDE.md .claude/ .cursor/ .github/copilot-instructions.md AGENTS.md CALIBER_LEARNINGS.md";
|
|
4244
|
-
|
|
4256
|
+
function getPreCommitBlock() {
|
|
4257
|
+
const bin = resolveCaliber();
|
|
4258
|
+
return `${BLOCK_START}
|
|
4245
4259
|
## Before Committing
|
|
4246
4260
|
|
|
4247
|
-
Run
|
|
4261
|
+
Run \`${bin} refresh\` before creating git commits to keep docs in sync with code changes.
|
|
4248
4262
|
After it completes, stage any modified doc files before committing:
|
|
4249
4263
|
|
|
4250
4264
|
\`\`\`bash
|
|
4251
|
-
|
|
4265
|
+
${bin} refresh && git add ${MANAGED_DOC_PATHS} 2>/dev/null
|
|
4252
4266
|
\`\`\`
|
|
4253
4267
|
${BLOCK_END}`;
|
|
4268
|
+
}
|
|
4254
4269
|
var CURSOR_RULE_FILENAME = "caliber-pre-commit.mdc";
|
|
4255
|
-
|
|
4270
|
+
function getCursorRuleContent() {
|
|
4271
|
+
const bin = resolveCaliber();
|
|
4272
|
+
return `---
|
|
4256
4273
|
description: Run caliber refresh before git commits to keep docs in sync
|
|
4257
4274
|
alwaysApply: true
|
|
4258
4275
|
---
|
|
4259
|
-
Before creating git commits, run
|
|
4276
|
+
Before creating git commits, run \`${bin} refresh\` to update docs based on code changes,
|
|
4260
4277
|
then stage modified doc files (${MANAGED_DOC_PATHS}).
|
|
4261
4278
|
`;
|
|
4279
|
+
}
|
|
4262
4280
|
function hasPreCommitBlock(content) {
|
|
4263
4281
|
return content.includes(BLOCK_START);
|
|
4264
4282
|
}
|
|
4265
4283
|
function appendPreCommitBlock(content) {
|
|
4266
4284
|
if (hasPreCommitBlock(content)) return content;
|
|
4267
4285
|
const trimmed = content.trimEnd();
|
|
4268
|
-
return trimmed + "\n\n" +
|
|
4286
|
+
return trimmed + "\n\n" + getPreCommitBlock() + "\n";
|
|
4269
4287
|
}
|
|
4270
4288
|
function getCursorPreCommitRule() {
|
|
4271
|
-
return { filename: CURSOR_RULE_FILENAME, content:
|
|
4289
|
+
return { filename: CURSOR_RULE_FILENAME, content: getCursorRuleContent() };
|
|
4272
4290
|
}
|
|
4273
4291
|
var LEARNINGS_BLOCK_START = "<!-- caliber:managed:learnings -->";
|
|
4274
4292
|
var LEARNINGS_BLOCK_END = "<!-- /caliber:managed:learnings -->";
|
|
@@ -4301,12 +4319,12 @@ function getCursorLearningsRule() {
|
|
|
4301
4319
|
// src/writers/claude/index.ts
|
|
4302
4320
|
function writeClaudeConfig(config) {
|
|
4303
4321
|
const written = [];
|
|
4304
|
-
|
|
4322
|
+
fs11.writeFileSync("CLAUDE.md", appendLearningsBlock(appendPreCommitBlock(config.claudeMd)));
|
|
4305
4323
|
written.push("CLAUDE.md");
|
|
4306
4324
|
if (config.skills?.length) {
|
|
4307
4325
|
for (const skill of config.skills) {
|
|
4308
4326
|
const skillDir = path10.join(".claude", "skills", skill.name);
|
|
4309
|
-
if (!
|
|
4327
|
+
if (!fs11.existsSync(skillDir)) fs11.mkdirSync(skillDir, { recursive: true });
|
|
4310
4328
|
const skillPath = path10.join(skillDir, "SKILL.md");
|
|
4311
4329
|
const frontmatter = [
|
|
4312
4330
|
"---",
|
|
@@ -4315,49 +4333,49 @@ function writeClaudeConfig(config) {
|
|
|
4315
4333
|
"---",
|
|
4316
4334
|
""
|
|
4317
4335
|
].join("\n");
|
|
4318
|
-
|
|
4336
|
+
fs11.writeFileSync(skillPath, frontmatter + skill.content);
|
|
4319
4337
|
written.push(skillPath);
|
|
4320
4338
|
}
|
|
4321
4339
|
}
|
|
4322
4340
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
4323
4341
|
let existingServers = {};
|
|
4324
4342
|
try {
|
|
4325
|
-
if (
|
|
4326
|
-
const existing = JSON.parse(
|
|
4343
|
+
if (fs11.existsSync(".mcp.json")) {
|
|
4344
|
+
const existing = JSON.parse(fs11.readFileSync(".mcp.json", "utf-8"));
|
|
4327
4345
|
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
4328
4346
|
}
|
|
4329
4347
|
} catch {
|
|
4330
4348
|
}
|
|
4331
4349
|
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
4332
|
-
|
|
4350
|
+
fs11.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
4333
4351
|
written.push(".mcp.json");
|
|
4334
4352
|
}
|
|
4335
4353
|
return written;
|
|
4336
4354
|
}
|
|
4337
4355
|
|
|
4338
4356
|
// src/writers/cursor/index.ts
|
|
4339
|
-
import
|
|
4357
|
+
import fs12 from "fs";
|
|
4340
4358
|
import path11 from "path";
|
|
4341
4359
|
function writeCursorConfig(config) {
|
|
4342
4360
|
const written = [];
|
|
4343
4361
|
if (config.cursorrules) {
|
|
4344
|
-
|
|
4362
|
+
fs12.writeFileSync(".cursorrules", config.cursorrules);
|
|
4345
4363
|
written.push(".cursorrules");
|
|
4346
4364
|
}
|
|
4347
4365
|
const preCommitRule = getCursorPreCommitRule();
|
|
4348
4366
|
const learningsRule = getCursorLearningsRule();
|
|
4349
4367
|
const allRules = [...config.rules || [], preCommitRule, learningsRule];
|
|
4350
4368
|
const rulesDir = path11.join(".cursor", "rules");
|
|
4351
|
-
if (!
|
|
4369
|
+
if (!fs12.existsSync(rulesDir)) fs12.mkdirSync(rulesDir, { recursive: true });
|
|
4352
4370
|
for (const rule of allRules) {
|
|
4353
4371
|
const rulePath = path11.join(rulesDir, rule.filename);
|
|
4354
|
-
|
|
4372
|
+
fs12.writeFileSync(rulePath, rule.content);
|
|
4355
4373
|
written.push(rulePath);
|
|
4356
4374
|
}
|
|
4357
4375
|
if (config.skills?.length) {
|
|
4358
4376
|
for (const skill of config.skills) {
|
|
4359
4377
|
const skillDir = path11.join(".cursor", "skills", skill.name);
|
|
4360
|
-
if (!
|
|
4378
|
+
if (!fs12.existsSync(skillDir)) fs12.mkdirSync(skillDir, { recursive: true });
|
|
4361
4379
|
const skillPath = path11.join(skillDir, "SKILL.md");
|
|
4362
4380
|
const frontmatter = [
|
|
4363
4381
|
"---",
|
|
@@ -4366,40 +4384,40 @@ function writeCursorConfig(config) {
|
|
|
4366
4384
|
"---",
|
|
4367
4385
|
""
|
|
4368
4386
|
].join("\n");
|
|
4369
|
-
|
|
4387
|
+
fs12.writeFileSync(skillPath, frontmatter + skill.content);
|
|
4370
4388
|
written.push(skillPath);
|
|
4371
4389
|
}
|
|
4372
4390
|
}
|
|
4373
4391
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
4374
4392
|
const cursorDir = ".cursor";
|
|
4375
|
-
if (!
|
|
4393
|
+
if (!fs12.existsSync(cursorDir)) fs12.mkdirSync(cursorDir, { recursive: true });
|
|
4376
4394
|
const mcpPath = path11.join(cursorDir, "mcp.json");
|
|
4377
4395
|
let existingServers = {};
|
|
4378
4396
|
try {
|
|
4379
|
-
if (
|
|
4380
|
-
const existing = JSON.parse(
|
|
4397
|
+
if (fs12.existsSync(mcpPath)) {
|
|
4398
|
+
const existing = JSON.parse(fs12.readFileSync(mcpPath, "utf-8"));
|
|
4381
4399
|
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
4382
4400
|
}
|
|
4383
4401
|
} catch {
|
|
4384
4402
|
}
|
|
4385
4403
|
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
4386
|
-
|
|
4404
|
+
fs12.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
4387
4405
|
written.push(mcpPath);
|
|
4388
4406
|
}
|
|
4389
4407
|
return written;
|
|
4390
4408
|
}
|
|
4391
4409
|
|
|
4392
4410
|
// src/writers/codex/index.ts
|
|
4393
|
-
import
|
|
4411
|
+
import fs13 from "fs";
|
|
4394
4412
|
import path12 from "path";
|
|
4395
4413
|
function writeCodexConfig(config) {
|
|
4396
4414
|
const written = [];
|
|
4397
|
-
|
|
4415
|
+
fs13.writeFileSync("AGENTS.md", appendLearningsBlock(appendPreCommitBlock(config.agentsMd)));
|
|
4398
4416
|
written.push("AGENTS.md");
|
|
4399
4417
|
if (config.skills?.length) {
|
|
4400
4418
|
for (const skill of config.skills) {
|
|
4401
4419
|
const skillDir = path12.join(".agents", "skills", skill.name);
|
|
4402
|
-
if (!
|
|
4420
|
+
if (!fs13.existsSync(skillDir)) fs13.mkdirSync(skillDir, { recursive: true });
|
|
4403
4421
|
const skillPath = path12.join(skillDir, "SKILL.md");
|
|
4404
4422
|
const frontmatter = [
|
|
4405
4423
|
"---",
|
|
@@ -4408,7 +4426,7 @@ function writeCodexConfig(config) {
|
|
|
4408
4426
|
"---",
|
|
4409
4427
|
""
|
|
4410
4428
|
].join("\n");
|
|
4411
|
-
|
|
4429
|
+
fs13.writeFileSync(skillPath, frontmatter + skill.content);
|
|
4412
4430
|
written.push(skillPath);
|
|
4413
4431
|
}
|
|
4414
4432
|
}
|
|
@@ -4416,20 +4434,20 @@ function writeCodexConfig(config) {
|
|
|
4416
4434
|
}
|
|
4417
4435
|
|
|
4418
4436
|
// src/writers/github-copilot/index.ts
|
|
4419
|
-
import
|
|
4437
|
+
import fs14 from "fs";
|
|
4420
4438
|
import path13 from "path";
|
|
4421
4439
|
function writeGithubCopilotConfig(config) {
|
|
4422
4440
|
const written = [];
|
|
4423
4441
|
if (config.instructions) {
|
|
4424
|
-
|
|
4425
|
-
|
|
4442
|
+
fs14.mkdirSync(".github", { recursive: true });
|
|
4443
|
+
fs14.writeFileSync(path13.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(config.instructions)));
|
|
4426
4444
|
written.push(".github/copilot-instructions.md");
|
|
4427
4445
|
}
|
|
4428
4446
|
if (config.instructionFiles?.length) {
|
|
4429
4447
|
const instructionsDir = path13.join(".github", "instructions");
|
|
4430
|
-
|
|
4448
|
+
fs14.mkdirSync(instructionsDir, { recursive: true });
|
|
4431
4449
|
for (const file of config.instructionFiles) {
|
|
4432
|
-
|
|
4450
|
+
fs14.writeFileSync(path13.join(instructionsDir, file.filename), file.content);
|
|
4433
4451
|
written.push(`.github/instructions/${file.filename}`);
|
|
4434
4452
|
}
|
|
4435
4453
|
}
|
|
@@ -4437,35 +4455,36 @@ function writeGithubCopilotConfig(config) {
|
|
|
4437
4455
|
}
|
|
4438
4456
|
|
|
4439
4457
|
// src/writers/backup.ts
|
|
4440
|
-
import
|
|
4458
|
+
import fs15 from "fs";
|
|
4441
4459
|
import path14 from "path";
|
|
4442
4460
|
function createBackup(files) {
|
|
4443
4461
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4444
4462
|
const backupDir = path14.join(BACKUPS_DIR, timestamp);
|
|
4445
4463
|
for (const file of files) {
|
|
4446
|
-
if (!
|
|
4464
|
+
if (!fs15.existsSync(file)) continue;
|
|
4447
4465
|
const dest = path14.join(backupDir, file);
|
|
4448
4466
|
const destDir = path14.dirname(dest);
|
|
4449
|
-
if (!
|
|
4450
|
-
|
|
4467
|
+
if (!fs15.existsSync(destDir)) {
|
|
4468
|
+
fs15.mkdirSync(destDir, { recursive: true });
|
|
4451
4469
|
}
|
|
4452
|
-
|
|
4470
|
+
fs15.copyFileSync(file, dest);
|
|
4453
4471
|
}
|
|
4454
4472
|
return backupDir;
|
|
4455
4473
|
}
|
|
4456
4474
|
function restoreBackup(backupDir, file) {
|
|
4457
4475
|
const backupFile = path14.join(backupDir, file);
|
|
4458
|
-
if (!
|
|
4476
|
+
if (!fs15.existsSync(backupFile)) return false;
|
|
4459
4477
|
const destDir = path14.dirname(file);
|
|
4460
|
-
if (!
|
|
4461
|
-
|
|
4478
|
+
if (!fs15.existsSync(destDir)) {
|
|
4479
|
+
fs15.mkdirSync(destDir, { recursive: true });
|
|
4462
4480
|
}
|
|
4463
|
-
|
|
4481
|
+
fs15.copyFileSync(backupFile, file);
|
|
4464
4482
|
return true;
|
|
4465
4483
|
}
|
|
4466
4484
|
|
|
4467
4485
|
// src/lib/builtin-skills.ts
|
|
4468
|
-
|
|
4486
|
+
init_resolve_caliber();
|
|
4487
|
+
import fs16 from "fs";
|
|
4469
4488
|
import path15 from "path";
|
|
4470
4489
|
function buildSkillContent(skill) {
|
|
4471
4490
|
const frontmatter = `---
|
|
@@ -4476,10 +4495,9 @@ description: ${skill.description}
|
|
|
4476
4495
|
`;
|
|
4477
4496
|
return frontmatter + skill.content;
|
|
4478
4497
|
}
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
content: `# Find Skills
|
|
4498
|
+
function getFindSkillsContent() {
|
|
4499
|
+
const bin = resolveCaliber();
|
|
4500
|
+
return `# Find Skills
|
|
4483
4501
|
|
|
4484
4502
|
Search the public skill registry for community-contributed skills
|
|
4485
4503
|
relevant to the user's current task and install them into this project.
|
|
@@ -4492,13 +4510,13 @@ relevant to the user's current task and install them into this project.
|
|
|
4492
4510
|
for [identified technologies]?"
|
|
4493
4511
|
3. If the user agrees, run:
|
|
4494
4512
|
\`\`\`bash
|
|
4495
|
-
|
|
4513
|
+
${bin} skills --query "<relevant terms>"
|
|
4496
4514
|
\`\`\`
|
|
4497
4515
|
This outputs the top 5 matching skills with scores and descriptions.
|
|
4498
4516
|
4. Present the results to the user and ask which ones to install
|
|
4499
4517
|
5. Install the selected skills:
|
|
4500
4518
|
\`\`\`bash
|
|
4501
|
-
|
|
4519
|
+
${bin} skills --install <slug1>,<slug2>
|
|
4502
4520
|
\`\`\`
|
|
4503
4521
|
6. Read the installed SKILL.md files to load them into your current
|
|
4504
4522
|
context so you can use them immediately in this session
|
|
@@ -4509,18 +4527,18 @@ relevant to the user's current task and install them into this project.
|
|
|
4509
4527
|
User: "let's build a web app using React"
|
|
4510
4528
|
-> "I notice you want to work with React. Would you like me to search
|
|
4511
4529
|
for community skills that could help with React development?"
|
|
4512
|
-
-> If yes: run
|
|
4530
|
+
-> If yes: run \`${bin} skills --query "react frontend"\`
|
|
4513
4531
|
-> Show the user the results, ask which to install
|
|
4514
|
-
-> Run
|
|
4532
|
+
-> Run \`${bin} skills --install <selected-slugs>\`
|
|
4515
4533
|
-> Read the installed files and continue
|
|
4516
4534
|
|
|
4517
4535
|
User: "help me set up Docker for this project"
|
|
4518
4536
|
-> "Would you like me to search for Docker-related skills?"
|
|
4519
|
-
-> If yes: run
|
|
4537
|
+
-> If yes: run \`${bin} skills --query "docker deployment"\`
|
|
4520
4538
|
|
|
4521
4539
|
User: "I need to write tests for this Python ML pipeline"
|
|
4522
4540
|
-> "Would you like me to find skills for Python ML testing?"
|
|
4523
|
-
-> If yes: run
|
|
4541
|
+
-> If yes: run \`${bin} skills --query "python machine-learning testing"\`
|
|
4524
4542
|
|
|
4525
4543
|
## When NOT to trigger
|
|
4526
4544
|
|
|
@@ -4528,12 +4546,11 @@ User: "I need to write tests for this Python ML pipeline"
|
|
|
4528
4546
|
- You already suggested skills for this technology in this session
|
|
4529
4547
|
- The user is in the middle of urgent debugging or time-sensitive work
|
|
4530
4548
|
- The technology is too generic (e.g. just "code" or "programming")
|
|
4531
|
-
|
|
4532
|
-
}
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
content: `# Save Learning
|
|
4549
|
+
`;
|
|
4550
|
+
}
|
|
4551
|
+
function getSaveLearningContent() {
|
|
4552
|
+
const bin = resolveCaliber();
|
|
4553
|
+
return `# Save Learning
|
|
4537
4554
|
|
|
4538
4555
|
Save a user's instruction or preference as a persistent learning that
|
|
4539
4556
|
will be applied in all future sessions on this project.
|
|
@@ -4554,11 +4571,11 @@ will be applied in all future sessions on this project.
|
|
|
4554
4571
|
3. Show the refined learning to the user and ask for confirmation
|
|
4555
4572
|
4. If confirmed, run:
|
|
4556
4573
|
\`\`\`bash
|
|
4557
|
-
|
|
4574
|
+
${bin} learn add "<refined learning>"
|
|
4558
4575
|
\`\`\`
|
|
4559
4576
|
For personal preferences (not project-level), add \`--personal\`:
|
|
4560
4577
|
\`\`\`bash
|
|
4561
|
-
|
|
4578
|
+
${bin} learn add --personal "<refined learning>"
|
|
4562
4579
|
\`\`\`
|
|
4563
4580
|
5. Stage the learnings file for the next commit:
|
|
4564
4581
|
\`\`\`bash
|
|
@@ -4572,7 +4589,7 @@ User: "when developing features, push to next branch not master, remember it"
|
|
|
4572
4589
|
-> "I'll save this as a project learning:
|
|
4573
4590
|
**[convention]** Push feature commits to the \\\`next\\\` branch, not \\\`master\\\`
|
|
4574
4591
|
Save for future sessions?"
|
|
4575
|
-
-> If yes: run
|
|
4592
|
+
-> If yes: run \`${bin} learn add "**[convention]** Push feature commits to the next branch, not master"\`
|
|
4576
4593
|
-> Run \`git add CALIBER_LEARNINGS.md\`
|
|
4577
4594
|
|
|
4578
4595
|
User: "always use bun instead of npm"
|
|
@@ -4588,7 +4605,21 @@ User: "never use any in TypeScript, use unknown instead"
|
|
|
4588
4605
|
- The user is giving a one-time instruction for the current task only
|
|
4589
4606
|
- The instruction is too vague to be actionable
|
|
4590
4607
|
- The user explicitly says "just for now" or "only this time"
|
|
4591
|
-
|
|
4608
|
+
`;
|
|
4609
|
+
}
|
|
4610
|
+
var FIND_SKILLS_SKILL = {
|
|
4611
|
+
name: "find-skills",
|
|
4612
|
+
description: "Discovers and installs community skills from the public registry. Use when the user mentions a technology, framework, or task that could benefit from specialized skills not yet installed, asks 'how do I do X', 'find a skill for X', or starts work in a new technology area. Proactively suggest when the user's task involves tools or frameworks without existing skills.",
|
|
4613
|
+
get content() {
|
|
4614
|
+
return getFindSkillsContent();
|
|
4615
|
+
}
|
|
4616
|
+
};
|
|
4617
|
+
var SAVE_LEARNING_SKILL = {
|
|
4618
|
+
name: "save-learning",
|
|
4619
|
+
description: "Saves user instructions as persistent learnings for future sessions. Use when the user says 'remember this', 'always do X', 'from now on', 'never do Y', or gives any instruction they want persisted across sessions. Proactively suggest when the user states a preference, convention, or rule they clearly want followed in the future.",
|
|
4620
|
+
get content() {
|
|
4621
|
+
return getSaveLearningContent();
|
|
4622
|
+
}
|
|
4592
4623
|
};
|
|
4593
4624
|
var BUILTIN_SKILLS = [FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL];
|
|
4594
4625
|
var PLATFORM_CONFIGS = [
|
|
@@ -4599,12 +4630,12 @@ var PLATFORM_CONFIGS = [
|
|
|
4599
4630
|
function ensureBuiltinSkills() {
|
|
4600
4631
|
const written = [];
|
|
4601
4632
|
for (const { platformDir, skillsDir } of PLATFORM_CONFIGS) {
|
|
4602
|
-
if (!
|
|
4633
|
+
if (!fs16.existsSync(platformDir)) continue;
|
|
4603
4634
|
for (const skill of BUILTIN_SKILLS) {
|
|
4604
4635
|
const skillPath = path15.join(skillsDir, skill.name, "SKILL.md");
|
|
4605
|
-
if (
|
|
4606
|
-
|
|
4607
|
-
|
|
4636
|
+
if (fs16.existsSync(skillPath)) continue;
|
|
4637
|
+
fs16.mkdirSync(path15.dirname(skillPath), { recursive: true });
|
|
4638
|
+
fs16.writeFileSync(skillPath, buildSkillContent(skill));
|
|
4608
4639
|
written.push(skillPath);
|
|
4609
4640
|
}
|
|
4610
4641
|
}
|
|
@@ -4612,33 +4643,33 @@ function ensureBuiltinSkills() {
|
|
|
4612
4643
|
}
|
|
4613
4644
|
|
|
4614
4645
|
// src/writers/manifest.ts
|
|
4615
|
-
import
|
|
4646
|
+
import fs17 from "fs";
|
|
4616
4647
|
import crypto3 from "crypto";
|
|
4617
4648
|
function readManifest() {
|
|
4618
4649
|
try {
|
|
4619
|
-
if (!
|
|
4620
|
-
return JSON.parse(
|
|
4650
|
+
if (!fs17.existsSync(MANIFEST_FILE)) return null;
|
|
4651
|
+
return JSON.parse(fs17.readFileSync(MANIFEST_FILE, "utf-8"));
|
|
4621
4652
|
} catch {
|
|
4622
4653
|
return null;
|
|
4623
4654
|
}
|
|
4624
4655
|
}
|
|
4625
4656
|
function writeManifest(manifest) {
|
|
4626
|
-
if (!
|
|
4627
|
-
|
|
4657
|
+
if (!fs17.existsSync(CALIBER_DIR)) {
|
|
4658
|
+
fs17.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
4628
4659
|
}
|
|
4629
|
-
|
|
4660
|
+
fs17.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
|
|
4630
4661
|
}
|
|
4631
4662
|
function fileChecksum(filePath) {
|
|
4632
|
-
const content =
|
|
4663
|
+
const content = fs17.readFileSync(filePath);
|
|
4633
4664
|
return crypto3.createHash("sha256").update(content).digest("hex");
|
|
4634
4665
|
}
|
|
4635
4666
|
|
|
4636
4667
|
// src/writers/index.ts
|
|
4637
4668
|
function writeSetup(setup) {
|
|
4638
4669
|
const filesToWrite = getFilesToWrite(setup);
|
|
4639
|
-
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) =>
|
|
4670
|
+
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs18.existsSync(f));
|
|
4640
4671
|
const existingFiles = [
|
|
4641
|
-
...filesToWrite.filter((f) =>
|
|
4672
|
+
...filesToWrite.filter((f) => fs18.existsSync(f)),
|
|
4642
4673
|
...filesToDelete
|
|
4643
4674
|
];
|
|
4644
4675
|
const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
|
|
@@ -4657,7 +4688,7 @@ function writeSetup(setup) {
|
|
|
4657
4688
|
}
|
|
4658
4689
|
const deleted = [];
|
|
4659
4690
|
for (const filePath of filesToDelete) {
|
|
4660
|
-
|
|
4691
|
+
fs18.unlinkSync(filePath);
|
|
4661
4692
|
deleted.push(filePath);
|
|
4662
4693
|
}
|
|
4663
4694
|
written.push(...ensureBuiltinSkills());
|
|
@@ -4688,8 +4719,8 @@ function undoSetup() {
|
|
|
4688
4719
|
const removed = [];
|
|
4689
4720
|
for (const entry of manifest.entries) {
|
|
4690
4721
|
if (entry.action === "created") {
|
|
4691
|
-
if (
|
|
4692
|
-
|
|
4722
|
+
if (fs18.existsSync(entry.path)) {
|
|
4723
|
+
fs18.unlinkSync(entry.path);
|
|
4693
4724
|
removed.push(entry.path);
|
|
4694
4725
|
}
|
|
4695
4726
|
} else if ((entry.action === "modified" || entry.action === "deleted") && manifest.backupDir) {
|
|
@@ -4698,8 +4729,8 @@ function undoSetup() {
|
|
|
4698
4729
|
}
|
|
4699
4730
|
}
|
|
4700
4731
|
}
|
|
4701
|
-
if (
|
|
4702
|
-
|
|
4732
|
+
if (fs18.existsSync(MANIFEST_FILE)) {
|
|
4733
|
+
fs18.unlinkSync(MANIFEST_FILE);
|
|
4703
4734
|
}
|
|
4704
4735
|
return { restored, removed };
|
|
4705
4736
|
}
|
|
@@ -4740,18 +4771,18 @@ function getFilesToWrite(setup) {
|
|
|
4740
4771
|
}
|
|
4741
4772
|
function ensureGitignore() {
|
|
4742
4773
|
const gitignorePath = ".gitignore";
|
|
4743
|
-
if (
|
|
4744
|
-
const content =
|
|
4774
|
+
if (fs18.existsSync(gitignorePath)) {
|
|
4775
|
+
const content = fs18.readFileSync(gitignorePath, "utf-8");
|
|
4745
4776
|
if (!content.includes(".caliber/")) {
|
|
4746
|
-
|
|
4777
|
+
fs18.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
|
|
4747
4778
|
}
|
|
4748
4779
|
} else {
|
|
4749
|
-
|
|
4780
|
+
fs18.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
|
|
4750
4781
|
}
|
|
4751
4782
|
}
|
|
4752
4783
|
|
|
4753
4784
|
// src/writers/staging.ts
|
|
4754
|
-
import
|
|
4785
|
+
import fs19 from "fs";
|
|
4755
4786
|
import path16 from "path";
|
|
4756
4787
|
var STAGED_DIR = path16.join(CALIBER_DIR, "staged");
|
|
4757
4788
|
var PROPOSED_DIR = path16.join(STAGED_DIR, "proposed");
|
|
@@ -4766,19 +4797,19 @@ function stageFiles(files, projectDir) {
|
|
|
4766
4797
|
const stagedFiles = [];
|
|
4767
4798
|
for (const file of files) {
|
|
4768
4799
|
const originalPath = path16.join(projectDir, file.path);
|
|
4769
|
-
if (
|
|
4770
|
-
const existing =
|
|
4800
|
+
if (fs19.existsSync(originalPath)) {
|
|
4801
|
+
const existing = fs19.readFileSync(originalPath, "utf-8");
|
|
4771
4802
|
if (normalizeContent(existing) === normalizeContent(file.content)) {
|
|
4772
4803
|
continue;
|
|
4773
4804
|
}
|
|
4774
4805
|
}
|
|
4775
4806
|
const proposedPath = path16.join(PROPOSED_DIR, file.path);
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
if (
|
|
4807
|
+
fs19.mkdirSync(path16.dirname(proposedPath), { recursive: true });
|
|
4808
|
+
fs19.writeFileSync(proposedPath, file.content);
|
|
4809
|
+
if (fs19.existsSync(originalPath)) {
|
|
4779
4810
|
const currentPath = path16.join(CURRENT_DIR, file.path);
|
|
4780
|
-
|
|
4781
|
-
|
|
4811
|
+
fs19.mkdirSync(path16.dirname(currentPath), { recursive: true });
|
|
4812
|
+
fs19.copyFileSync(originalPath, currentPath);
|
|
4782
4813
|
modifiedFiles++;
|
|
4783
4814
|
stagedFiles.push({ relativePath: file.path, proposedPath, currentPath, originalPath, isNew: false });
|
|
4784
4815
|
} else {
|
|
@@ -4789,13 +4820,13 @@ function stageFiles(files, projectDir) {
|
|
|
4789
4820
|
return { newFiles, modifiedFiles, stagedFiles };
|
|
4790
4821
|
}
|
|
4791
4822
|
function cleanupStaging() {
|
|
4792
|
-
if (
|
|
4793
|
-
|
|
4823
|
+
if (fs19.existsSync(STAGED_DIR)) {
|
|
4824
|
+
fs19.rmSync(STAGED_DIR, { recursive: true, force: true });
|
|
4794
4825
|
}
|
|
4795
4826
|
}
|
|
4796
4827
|
|
|
4797
4828
|
// src/commands/setup-files.ts
|
|
4798
|
-
import
|
|
4829
|
+
import fs20 from "fs";
|
|
4799
4830
|
function collectSetupFiles(setup, targetAgent) {
|
|
4800
4831
|
const files = [];
|
|
4801
4832
|
const claude = setup.claude;
|
|
@@ -4854,7 +4885,7 @@ function collectSetupFiles(setup, targetAgent) {
|
|
|
4854
4885
|
}
|
|
4855
4886
|
}
|
|
4856
4887
|
const codexTargeted = targetAgent ? targetAgent.includes("codex") : false;
|
|
4857
|
-
if (codexTargeted && !
|
|
4888
|
+
if (codexTargeted && !fs20.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
|
|
4858
4889
|
const agentRefs = [];
|
|
4859
4890
|
if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
|
|
4860
4891
|
if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
|
|
@@ -5021,6 +5052,9 @@ function removeLearningHooks() {
|
|
|
5021
5052
|
return { removed: true, notFound: false };
|
|
5022
5053
|
}
|
|
5023
5054
|
|
|
5055
|
+
// src/commands/init.ts
|
|
5056
|
+
init_resolve_caliber();
|
|
5057
|
+
|
|
5024
5058
|
// src/lib/state.ts
|
|
5025
5059
|
import fs22 from "fs";
|
|
5026
5060
|
import path18 from "path";
|
|
@@ -5066,10 +5100,10 @@ import chalk2 from "chalk";
|
|
|
5066
5100
|
import readline from "readline";
|
|
5067
5101
|
function promptInput(question) {
|
|
5068
5102
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
5069
|
-
return new Promise((
|
|
5103
|
+
return new Promise((resolve3) => {
|
|
5070
5104
|
rl.question(chalk2.cyan(`${question} `), (answer) => {
|
|
5071
5105
|
rl.close();
|
|
5072
|
-
|
|
5106
|
+
resolve3(answer.trim());
|
|
5073
5107
|
});
|
|
5074
5108
|
});
|
|
5075
5109
|
}
|
|
@@ -5667,6 +5701,7 @@ function checkGrounding(dir) {
|
|
|
5667
5701
|
import { existsSync as existsSync4, statSync as statSync2 } from "fs";
|
|
5668
5702
|
import { execSync as execSync9 } from "child_process";
|
|
5669
5703
|
import { join as join5 } from "path";
|
|
5704
|
+
init_resolve_caliber();
|
|
5670
5705
|
function validateReferences(dir) {
|
|
5671
5706
|
const configContent = collectPrimaryConfigContent(dir);
|
|
5672
5707
|
if (!configContent) return { valid: [], invalid: [], total: 0 };
|
|
@@ -5790,7 +5825,7 @@ function checkAccuracy(dir) {
|
|
|
5790
5825
|
earnedPoints: driftPoints,
|
|
5791
5826
|
passed: drift.commitsSinceConfigUpdate <= 15 || !drift.isGitRepo,
|
|
5792
5827
|
detail: !drift.isGitRepo ? "Not a git repository \u2014 skipping drift check" : !drift.lastConfigCommit ? "Config files not tracked in git" : drift.commitsSinceConfigUpdate === 0 ? "Config is up to date with latest commits" : `${drift.commitsSinceConfigUpdate} commit${drift.commitsSinceConfigUpdate === 1 ? "" : "s"} since last config update`,
|
|
5793
|
-
suggestion: drift.commitsSinceConfigUpdate > 15 ? `Code has had ${drift.commitsSinceConfigUpdate} commits since last config update \u2014 run
|
|
5828
|
+
suggestion: drift.commitsSinceConfigUpdate > 15 ? `Code has had ${drift.commitsSinceConfigUpdate} commits since last config update \u2014 run \`${resolveCaliber()} refresh\` to sync` : void 0,
|
|
5794
5829
|
fix: drift.commitsSinceConfigUpdate > 15 ? {
|
|
5795
5830
|
action: "refresh_config",
|
|
5796
5831
|
data: { commitsSince: drift.commitsSinceConfigUpdate, lastConfigCommit: drift.lastConfigCommit },
|
|
@@ -5801,6 +5836,7 @@ function checkAccuracy(dir) {
|
|
|
5801
5836
|
}
|
|
5802
5837
|
|
|
5803
5838
|
// src/scoring/checks/freshness.ts
|
|
5839
|
+
init_resolve_caliber();
|
|
5804
5840
|
import { existsSync as existsSync5, statSync as statSync3 } from "fs";
|
|
5805
5841
|
import { execSync as execSync10 } from "child_process";
|
|
5806
5842
|
import { join as join6 } from "path";
|
|
@@ -5864,7 +5900,7 @@ function checkFreshness(dir) {
|
|
|
5864
5900
|
earnedPoints: freshnessPoints,
|
|
5865
5901
|
passed: freshnessPoints >= 3,
|
|
5866
5902
|
detail: freshnessDetail,
|
|
5867
|
-
suggestion: commitsSince !== null && freshnessPoints < 3 ? `Config is ${commitsSince} commits behind \u2014 run
|
|
5903
|
+
suggestion: commitsSince !== null && freshnessPoints < 3 ? `Config is ${commitsSince} commits behind \u2014 run \`${resolveCaliber()} refresh\` to update it` : void 0,
|
|
5868
5904
|
fix: commitsSince !== null && freshnessPoints < 3 ? {
|
|
5869
5905
|
action: "refresh_config",
|
|
5870
5906
|
data: { commitsSince },
|
|
@@ -5952,6 +5988,7 @@ function checkFreshness(dir) {
|
|
|
5952
5988
|
import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
|
|
5953
5989
|
import { execSync as execSync11 } from "child_process";
|
|
5954
5990
|
import { join as join7 } from "path";
|
|
5991
|
+
init_resolve_caliber();
|
|
5955
5992
|
function hasPreCommitHook(dir) {
|
|
5956
5993
|
try {
|
|
5957
5994
|
const gitDir = execSync11("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
@@ -5997,11 +6034,11 @@ function checkBonus(dir) {
|
|
|
5997
6034
|
earnedPoints: hasHooks ? POINTS_HOOKS : 0,
|
|
5998
6035
|
passed: hasHooks,
|
|
5999
6036
|
detail: hasHooks ? hookSources.join(", ") : "No hooks configured",
|
|
6000
|
-
suggestion: hasHooks ? void 0 :
|
|
6037
|
+
suggestion: hasHooks ? void 0 : `Run \`${resolveCaliber()} init\` to add pre-commit instructions`,
|
|
6001
6038
|
fix: hasHooks ? void 0 : {
|
|
6002
6039
|
action: "install_hooks",
|
|
6003
6040
|
data: {},
|
|
6004
|
-
instruction:
|
|
6041
|
+
instruction: `Run ${resolveCaliber()} init to add pre-commit refresh instructions to config files.`
|
|
6005
6042
|
}
|
|
6006
6043
|
});
|
|
6007
6044
|
const agentsMdExists = existsSync6(join7(dir, "AGENTS.md"));
|
|
@@ -6066,13 +6103,14 @@ function checkBonus(dir) {
|
|
|
6066
6103
|
earnedPoints: hasLearned ? POINTS_LEARNED_CONTENT : 0,
|
|
6067
6104
|
passed: hasLearned,
|
|
6068
6105
|
detail: hasLearned ? "Session learnings found in CALIBER_LEARNINGS.md" : "No learned content",
|
|
6069
|
-
suggestion: hasLearned ? void 0 :
|
|
6106
|
+
suggestion: hasLearned ? void 0 : `Install learning hooks: \`${resolveCaliber()} learn install\``
|
|
6070
6107
|
});
|
|
6071
6108
|
return checks;
|
|
6072
6109
|
}
|
|
6073
6110
|
|
|
6074
6111
|
// src/scoring/checks/sources.ts
|
|
6075
6112
|
import { join as join8 } from "path";
|
|
6113
|
+
init_resolve_caliber();
|
|
6076
6114
|
function checkSources(dir) {
|
|
6077
6115
|
const checks = [];
|
|
6078
6116
|
const configSources = loadSourcesConfig(dir);
|
|
@@ -6085,7 +6123,7 @@ function checkSources(dir) {
|
|
|
6085
6123
|
earnedPoints: hasSources ? POINTS_SOURCES_CONFIGURED : 0,
|
|
6086
6124
|
passed: hasSources,
|
|
6087
6125
|
detail: hasSources ? `${configSources.length} source${configSources.length === 1 ? "" : "s"} configured` : "No external sources configured",
|
|
6088
|
-
suggestion: hasSources ? void 0 :
|
|
6126
|
+
suggestion: hasSources ? void 0 : `Run \`${resolveCaliber()} sources add <path>\` to add related repos or docs`
|
|
6089
6127
|
});
|
|
6090
6128
|
if (hasSources) {
|
|
6091
6129
|
const claudeMd = readFileOrNull(join8(dir, "CLAUDE.md"));
|
|
@@ -6103,7 +6141,7 @@ function checkSources(dir) {
|
|
|
6103
6141
|
earnedPoints: referenced ? POINTS_SOURCES_REFERENCED : 0,
|
|
6104
6142
|
passed: referenced,
|
|
6105
6143
|
detail: referenced ? "At least one source is referenced in CLAUDE.md" : "No configured sources are mentioned in CLAUDE.md",
|
|
6106
|
-
suggestion: referenced ? void 0 :
|
|
6144
|
+
suggestion: referenced ? void 0 : `Regenerate with \`${resolveCaliber()} init\` to include source context in your config`
|
|
6107
6145
|
});
|
|
6108
6146
|
}
|
|
6109
6147
|
return checks;
|
|
@@ -6194,6 +6232,7 @@ function computeLocalScore(dir, targetAgent) {
|
|
|
6194
6232
|
}
|
|
6195
6233
|
|
|
6196
6234
|
// src/scoring/display.ts
|
|
6235
|
+
init_resolve_caliber();
|
|
6197
6236
|
import chalk4 from "chalk";
|
|
6198
6237
|
var AGENT_DISPLAY_NAMES = {
|
|
6199
6238
|
claude: "Claude Code",
|
|
@@ -6329,7 +6368,7 @@ function displayScoreSummary(result) {
|
|
|
6329
6368
|
const remaining = failing.length - shown.length;
|
|
6330
6369
|
const moreText = remaining > 0 ? ` (+${remaining} more)` : "";
|
|
6331
6370
|
console.log(chalk4.dim(`
|
|
6332
|
-
Run ${chalk4.hex("#83D1EB")(
|
|
6371
|
+
Run ${chalk4.hex("#83D1EB")(`${resolveCaliber()} score`)} for details.${moreText}`));
|
|
6333
6372
|
}
|
|
6334
6373
|
console.log("");
|
|
6335
6374
|
}
|
|
@@ -6371,7 +6410,7 @@ import chalk6 from "chalk";
|
|
|
6371
6410
|
import ora from "ora";
|
|
6372
6411
|
import select3 from "@inquirer/select";
|
|
6373
6412
|
import { mkdirSync, readFileSync as readFileSync4, readdirSync as readdirSync4, existsSync as existsSync8, writeFileSync } from "fs";
|
|
6374
|
-
import { join as join10, dirname as dirname2 } from "path";
|
|
6413
|
+
import { join as join10, dirname as dirname2, resolve as resolve2 } from "path";
|
|
6375
6414
|
init_config();
|
|
6376
6415
|
|
|
6377
6416
|
// src/telemetry/index.ts
|
|
@@ -6408,11 +6447,12 @@ function getMachineId() {
|
|
|
6408
6447
|
writeConfig({ ...config, machineId });
|
|
6409
6448
|
return machineId;
|
|
6410
6449
|
}
|
|
6450
|
+
var EMAIL_HASH_KEY = "caliber-telemetry-v1";
|
|
6411
6451
|
function getGitEmailHash() {
|
|
6412
6452
|
try {
|
|
6413
6453
|
const email = execSync12("git config user.email", { encoding: "utf-8" }).trim();
|
|
6414
6454
|
if (!email) return void 0;
|
|
6415
|
-
return crypto4.
|
|
6455
|
+
return crypto4.createHmac("sha256", EMAIL_HASH_KEY).update(email).digest("hex");
|
|
6416
6456
|
} catch {
|
|
6417
6457
|
return void 0;
|
|
6418
6458
|
}
|
|
@@ -6573,6 +6613,7 @@ function trackInsightsViewed(totalSessions, learningCount) {
|
|
|
6573
6613
|
}
|
|
6574
6614
|
|
|
6575
6615
|
// src/commands/recommend.ts
|
|
6616
|
+
init_resolve_caliber();
|
|
6576
6617
|
function detectLocalPlatforms() {
|
|
6577
6618
|
const items = scanLocalState(process.cwd());
|
|
6578
6619
|
const platforms = /* @__PURE__ */ new Set();
|
|
@@ -6581,14 +6622,19 @@ function detectLocalPlatforms() {
|
|
|
6581
6622
|
}
|
|
6582
6623
|
return platforms.size > 0 ? Array.from(platforms) : ["claude"];
|
|
6583
6624
|
}
|
|
6625
|
+
function sanitizeSlug(slug) {
|
|
6626
|
+
return slug.replace(/[^a-zA-Z0-9_\-]/g, "-").replace(/^-+|-+$/g, "");
|
|
6627
|
+
}
|
|
6584
6628
|
function getSkillPath(platform, slug) {
|
|
6585
|
-
|
|
6586
|
-
|
|
6629
|
+
const safe = sanitizeSlug(slug);
|
|
6630
|
+
if (!safe) throw new Error(`Invalid skill slug: "${slug}"`);
|
|
6631
|
+
const baseDir = platform === "cursor" ? join10(".cursor", "skills") : platform === "codex" ? join10(".agents", "skills") : join10(".claude", "skills");
|
|
6632
|
+
const cwd = process.cwd();
|
|
6633
|
+
const fullPath = resolve2(cwd, baseDir, safe, "SKILL.md");
|
|
6634
|
+
if (!fullPath.startsWith(resolve2(cwd, baseDir) + "/")) {
|
|
6635
|
+
throw new Error(`Skill path escapes base directory: "${slug}"`);
|
|
6587
6636
|
}
|
|
6588
|
-
|
|
6589
|
-
return join10(".agents", "skills", slug, "SKILL.md");
|
|
6590
|
-
}
|
|
6591
|
-
return join10(".claude", "skills", slug, "SKILL.md");
|
|
6637
|
+
return join10(baseDir, safe, "SKILL.md");
|
|
6592
6638
|
}
|
|
6593
6639
|
function getSkillDir(platform) {
|
|
6594
6640
|
if (platform === "cursor") return join10(process.cwd(), ".cursor", "skills");
|
|
@@ -6944,7 +6990,7 @@ async function querySkills(query) {
|
|
|
6944
6990
|
console.log(` ${r.reason || r.name}`);
|
|
6945
6991
|
}
|
|
6946
6992
|
console.log("");
|
|
6947
|
-
console.log(chalk6.dim(` Install with:
|
|
6993
|
+
console.log(chalk6.dim(` Install with: ${resolveCaliber()} skills --install ${available.map((r) => r.slug).join(",")}`));
|
|
6948
6994
|
console.log("");
|
|
6949
6995
|
}
|
|
6950
6996
|
async function installBySlug(slugStr) {
|
|
@@ -7121,7 +7167,7 @@ async function interactiveSelect(recs) {
|
|
|
7121
7167
|
stdout.write(output + "\n");
|
|
7122
7168
|
lineCount = output.split("\n").length;
|
|
7123
7169
|
}
|
|
7124
|
-
return new Promise((
|
|
7170
|
+
return new Promise((resolve3) => {
|
|
7125
7171
|
console.log("");
|
|
7126
7172
|
draw(true);
|
|
7127
7173
|
stdin.setRawMode(true);
|
|
@@ -7159,9 +7205,9 @@ async function interactiveSelect(recs) {
|
|
|
7159
7205
|
cleanup();
|
|
7160
7206
|
if (selected.size === 0) {
|
|
7161
7207
|
console.log(chalk6.dim("\n No skills selected.\n"));
|
|
7162
|
-
|
|
7208
|
+
resolve3(null);
|
|
7163
7209
|
} else {
|
|
7164
|
-
|
|
7210
|
+
resolve3(Array.from(selected).sort().map((i) => recs[i]));
|
|
7165
7211
|
}
|
|
7166
7212
|
break;
|
|
7167
7213
|
case "q":
|
|
@@ -7169,7 +7215,7 @@ async function interactiveSelect(recs) {
|
|
|
7169
7215
|
case "":
|
|
7170
7216
|
cleanup();
|
|
7171
7217
|
console.log(chalk6.dim("\n Cancelled.\n"));
|
|
7172
|
-
|
|
7218
|
+
resolve3(null);
|
|
7173
7219
|
break;
|
|
7174
7220
|
}
|
|
7175
7221
|
}
|
|
@@ -8089,7 +8135,7 @@ ${JSON.stringify(currentSetup, null, 2)}
|
|
|
8089
8135
|
User request: ${message}
|
|
8090
8136
|
|
|
8091
8137
|
Return the complete updated AgentSetup JSON incorporating the user's changes. Respond with ONLY the JSON.`;
|
|
8092
|
-
return new Promise((
|
|
8138
|
+
return new Promise((resolve3) => {
|
|
8093
8139
|
let buffer = "";
|
|
8094
8140
|
provider.stream(
|
|
8095
8141
|
{
|
|
@@ -8109,20 +8155,20 @@ Return the complete updated AgentSetup JSON incorporating the user's changes. Re
|
|
|
8109
8155
|
try {
|
|
8110
8156
|
const setup = JSON.parse(jsonToParse);
|
|
8111
8157
|
if (callbacks) callbacks.onComplete(setup);
|
|
8112
|
-
|
|
8158
|
+
resolve3(setup);
|
|
8113
8159
|
} catch {
|
|
8114
8160
|
if (callbacks) callbacks.onError("Failed to parse AI response. Try rephrasing your request.");
|
|
8115
|
-
|
|
8161
|
+
resolve3(null);
|
|
8116
8162
|
}
|
|
8117
8163
|
},
|
|
8118
8164
|
onError: (error) => {
|
|
8119
8165
|
if (callbacks) callbacks.onError(error.message);
|
|
8120
|
-
|
|
8166
|
+
resolve3(null);
|
|
8121
8167
|
}
|
|
8122
8168
|
}
|
|
8123
8169
|
).catch((error) => {
|
|
8124
8170
|
if (callbacks) callbacks.onError(error.message);
|
|
8125
|
-
|
|
8171
|
+
resolve3(null);
|
|
8126
8172
|
});
|
|
8127
8173
|
});
|
|
8128
8174
|
}
|
|
@@ -8707,6 +8753,7 @@ function log(verbose, ...args) {
|
|
|
8707
8753
|
async function initCommand(options) {
|
|
8708
8754
|
const brand = chalk14.hex("#EB9D83");
|
|
8709
8755
|
const title = chalk14.hex("#83D1EB");
|
|
8756
|
+
const bin = resolveCaliber();
|
|
8710
8757
|
const firstRun = isFirstRun(process.cwd());
|
|
8711
8758
|
if (firstRun) {
|
|
8712
8759
|
console.log(brand.bold(`
|
|
@@ -8804,7 +8851,7 @@ async function initCommand(options) {
|
|
|
8804
8851
|
if (hasExistingConfig && baselineScore.score === 100) {
|
|
8805
8852
|
trackInitScoreComputed(baselineScore.score, passingCount, failingCount, true);
|
|
8806
8853
|
console.log(chalk14.bold.green(" Your config is already optimal \u2014 nothing to change.\n"));
|
|
8807
|
-
console.log(chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(
|
|
8854
|
+
console.log(chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} init --force`) + chalk14.dim(" to regenerate anyway.\n"));
|
|
8808
8855
|
if (!options.force) return;
|
|
8809
8856
|
}
|
|
8810
8857
|
const allFailingChecks = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
|
|
@@ -8820,7 +8867,7 @@ async function initCommand(options) {
|
|
|
8820
8867
|
}
|
|
8821
8868
|
}
|
|
8822
8869
|
console.log("");
|
|
8823
|
-
console.log(chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(
|
|
8870
|
+
console.log(chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} init --force`) + chalk14.dim(" to regenerate anyway.\n"));
|
|
8824
8871
|
return;
|
|
8825
8872
|
}
|
|
8826
8873
|
console.log(title.bold(" Step 2/4 \u2014 Engine\n"));
|
|
@@ -9155,7 +9202,7 @@ ${agentRefs.join(" ")}
|
|
|
9155
9202
|
}
|
|
9156
9203
|
} catch {
|
|
9157
9204
|
}
|
|
9158
|
-
console.log(chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(
|
|
9205
|
+
console.log(chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} init --force`) + chalk14.dim(" to override.\n"));
|
|
9159
9206
|
return;
|
|
9160
9207
|
}
|
|
9161
9208
|
if (report) {
|
|
@@ -9185,7 +9232,7 @@ ${agentRefs.join(" ")}
|
|
|
9185
9232
|
}
|
|
9186
9233
|
}
|
|
9187
9234
|
console.log("");
|
|
9188
|
-
console.log(` ${chalk14.green("\u2713")} Docs auto-refresh ${chalk14.dim(
|
|
9235
|
+
console.log(` ${chalk14.green("\u2713")} Docs auto-refresh ${chalk14.dim(`agents run ${resolveCaliber()} refresh before commits`)}`);
|
|
9189
9236
|
trackInitHookSelected("config-instructions");
|
|
9190
9237
|
const hasLearnableAgent = targetAgent.includes("claude") || targetAgent.includes("cursor");
|
|
9191
9238
|
let enableLearn = false;
|
|
@@ -9204,9 +9251,9 @@ ${agentRefs.join(" ")}
|
|
|
9204
9251
|
if (r.installed) console.log(` ${chalk14.green("\u2713")} Learning hooks installed for Cursor`);
|
|
9205
9252
|
else if (r.alreadyInstalled) console.log(chalk14.dim(" Cursor learning hooks already installed"));
|
|
9206
9253
|
}
|
|
9207
|
-
console.log(chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(
|
|
9254
|
+
console.log(chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} learn status`) + chalk14.dim(" to see insights"));
|
|
9208
9255
|
} else {
|
|
9209
|
-
console.log(chalk14.dim(" Skipped. Run ") + chalk14.hex("#83D1EB")(
|
|
9256
|
+
console.log(chalk14.dim(" Skipped. Run ") + chalk14.hex("#83D1EB")(`${bin} learn install`) + chalk14.dim(" later to enable."));
|
|
9210
9257
|
}
|
|
9211
9258
|
} else {
|
|
9212
9259
|
enableLearn = true;
|
|
@@ -9220,13 +9267,13 @@ ${agentRefs.join(" ")}
|
|
|
9220
9267
|
const done = chalk14.green("\u2713");
|
|
9221
9268
|
const skip = chalk14.dim("\u2013");
|
|
9222
9269
|
console.log(chalk14.bold(" What was configured:\n"));
|
|
9223
|
-
console.log(` ${done} Config generated ${title(
|
|
9224
|
-
console.log(` ${done} Docs auto-refresh ${chalk14.dim(
|
|
9270
|
+
console.log(` ${done} Config generated ${title(`${bin} score`)} ${chalk14.dim("for full breakdown")}`);
|
|
9271
|
+
console.log(` ${done} Docs auto-refresh ${chalk14.dim(`agents run ${bin} refresh before commits`)}`);
|
|
9225
9272
|
if (hasLearnableAgent) {
|
|
9226
9273
|
if (enableLearn) {
|
|
9227
9274
|
console.log(` ${done} Session learning ${chalk14.dim("agent learns from your feedback")}`);
|
|
9228
9275
|
} else {
|
|
9229
|
-
console.log(` ${skip} Session learning ${title(
|
|
9276
|
+
console.log(` ${skip} Session learning ${title(`${bin} learn install`)} to enable later`);
|
|
9230
9277
|
}
|
|
9231
9278
|
}
|
|
9232
9279
|
if (communitySkillsInstalled > 0) {
|
|
@@ -9235,9 +9282,9 @@ ${agentRefs.join(" ")}
|
|
|
9235
9282
|
console.log(` ${skip} Community skills ${chalk14.dim("available but skipped")}`);
|
|
9236
9283
|
}
|
|
9237
9284
|
console.log(chalk14.bold("\n Explore next:\n"));
|
|
9238
|
-
console.log(` ${title(
|
|
9239
|
-
console.log(` ${title(
|
|
9240
|
-
console.log(` ${title(
|
|
9285
|
+
console.log(` ${title(`${bin} skills`)} Find more community skills as your codebase evolves`);
|
|
9286
|
+
console.log(` ${title(`${bin} score`)} See the full scoring breakdown with improvement tips`);
|
|
9287
|
+
console.log(` ${title(`${bin} undo`)} Revert all changes from this run`);
|
|
9241
9288
|
console.log("");
|
|
9242
9289
|
if (options.showTokens) {
|
|
9243
9290
|
displayTokenUsage();
|
|
@@ -9287,6 +9334,7 @@ function undoCommand() {
|
|
|
9287
9334
|
import chalk16 from "chalk";
|
|
9288
9335
|
import fs33 from "fs";
|
|
9289
9336
|
init_config();
|
|
9337
|
+
init_resolve_caliber();
|
|
9290
9338
|
async function statusCommand(options) {
|
|
9291
9339
|
const config = loadConfig();
|
|
9292
9340
|
const manifest = readManifest();
|
|
@@ -9303,11 +9351,12 @@ async function statusCommand(options) {
|
|
|
9303
9351
|
if (config) {
|
|
9304
9352
|
console.log(` LLM: ${chalk16.green(config.provider)} (${config.model})`);
|
|
9305
9353
|
} else {
|
|
9306
|
-
|
|
9354
|
+
const bin = resolveCaliber();
|
|
9355
|
+
console.log(` LLM: ${chalk16.yellow("Not configured")} \u2014 run ${chalk16.hex("#83D1EB")(`${bin} config`)}`);
|
|
9307
9356
|
}
|
|
9308
9357
|
if (!manifest) {
|
|
9309
9358
|
console.log(` Config: ${chalk16.dim("No config applied")}`);
|
|
9310
|
-
console.log(chalk16.dim("\n Run ") + chalk16.hex("#83D1EB")(
|
|
9359
|
+
console.log(chalk16.dim("\n Run ") + chalk16.hex("#83D1EB")(`${resolveCaliber()} init`) + chalk16.dim(" to get started.\n"));
|
|
9311
9360
|
return;
|
|
9312
9361
|
}
|
|
9313
9362
|
console.log(` Files managed: ${chalk16.cyan(manifest.entries.length.toString())}`);
|
|
@@ -9325,15 +9374,17 @@ import ora5 from "ora";
|
|
|
9325
9374
|
import select6 from "@inquirer/select";
|
|
9326
9375
|
init_review();
|
|
9327
9376
|
init_config();
|
|
9377
|
+
init_resolve_caliber();
|
|
9328
9378
|
async function regenerateCommand(options) {
|
|
9379
|
+
const bin = resolveCaliber();
|
|
9329
9380
|
const config = loadConfig();
|
|
9330
9381
|
if (!config) {
|
|
9331
|
-
console.log(chalk17.red("No LLM provider configured. Run ") + chalk17.hex("#83D1EB")(
|
|
9382
|
+
console.log(chalk17.red("No LLM provider configured. Run ") + chalk17.hex("#83D1EB")(`${bin} config`) + chalk17.red(" first."));
|
|
9332
9383
|
throw new Error("__exit__");
|
|
9333
9384
|
}
|
|
9334
9385
|
const manifest = readManifest();
|
|
9335
9386
|
if (!manifest) {
|
|
9336
|
-
console.log(chalk17.yellow("No existing config found. Run ") + chalk17.hex("#83D1EB")(
|
|
9387
|
+
console.log(chalk17.yellow("No existing config found. Run ") + chalk17.hex("#83D1EB")(`${bin} init`) + chalk17.yellow(" first."));
|
|
9337
9388
|
throw new Error("__exit__");
|
|
9338
9389
|
}
|
|
9339
9390
|
const targetAgent = readState()?.targetAgent ?? ["claude", "cursor"];
|
|
@@ -9457,13 +9508,13 @@ async function regenerateCommand(options) {
|
|
|
9457
9508
|
}
|
|
9458
9509
|
} catch {
|
|
9459
9510
|
}
|
|
9460
|
-
console.log(chalk17.dim(" Run ") + chalk17.hex("#83D1EB")(
|
|
9511
|
+
console.log(chalk17.dim(" Run ") + chalk17.hex("#83D1EB")(`${bin} init --force`) + chalk17.dim(" to override.\n"));
|
|
9461
9512
|
return;
|
|
9462
9513
|
}
|
|
9463
9514
|
displayScoreDelta(baselineScore, afterScore);
|
|
9464
9515
|
trackRegenerateCompleted(action, Date.now());
|
|
9465
9516
|
console.log(chalk17.bold.green(" Regeneration complete!"));
|
|
9466
|
-
console.log(chalk17.dim(" Run ") + chalk17.hex("#83D1EB")(
|
|
9517
|
+
console.log(chalk17.dim(" Run ") + chalk17.hex("#83D1EB")(`${bin} undo`) + chalk17.dim(" to revert changes.\n"));
|
|
9467
9518
|
}
|
|
9468
9519
|
|
|
9469
9520
|
// src/commands/score.ts
|
|
@@ -9472,6 +9523,7 @@ import os7 from "os";
|
|
|
9472
9523
|
import path26 from "path";
|
|
9473
9524
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
9474
9525
|
import chalk18 from "chalk";
|
|
9526
|
+
init_resolve_caliber();
|
|
9475
9527
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".cursorrules", "CALIBER_LEARNINGS.md"];
|
|
9476
9528
|
var CONFIG_DIRS = [".claude", ".cursor"];
|
|
9477
9529
|
function scoreBaseRef(ref, target) {
|
|
@@ -9552,12 +9604,13 @@ async function scoreCommand(options) {
|
|
|
9552
9604
|
displayScore(result);
|
|
9553
9605
|
const separator = chalk18.gray(" " + "\u2500".repeat(53));
|
|
9554
9606
|
console.log(separator);
|
|
9607
|
+
const bin = resolveCaliber();
|
|
9555
9608
|
if (result.score < 40) {
|
|
9556
|
-
console.log(chalk18.gray(" Run ") + chalk18.hex("#83D1EB")(
|
|
9609
|
+
console.log(chalk18.gray(" Run ") + chalk18.hex("#83D1EB")(`${bin} init`) + chalk18.gray(" to generate a complete, optimized config."));
|
|
9557
9610
|
} else if (result.score < 70) {
|
|
9558
|
-
console.log(chalk18.gray(" Run ") + chalk18.hex("#83D1EB")(
|
|
9611
|
+
console.log(chalk18.gray(" Run ") + chalk18.hex("#83D1EB")(`${bin} init`) + chalk18.gray(" to improve your config."));
|
|
9559
9612
|
} else {
|
|
9560
|
-
console.log(chalk18.green(" Looking good!") + chalk18.gray(" Run ") + chalk18.hex("#83D1EB")(
|
|
9613
|
+
console.log(chalk18.green(" Looking good!") + chalk18.gray(" Run ") + chalk18.hex("#83D1EB")(`${bin} regenerate`) + chalk18.gray(" to rebuild from scratch."));
|
|
9561
9614
|
}
|
|
9562
9615
|
console.log("");
|
|
9563
9616
|
}
|
|
@@ -9964,6 +10017,7 @@ function migrateInlineLearnings() {
|
|
|
9964
10017
|
|
|
9965
10018
|
// src/commands/refresh.ts
|
|
9966
10019
|
init_config();
|
|
10020
|
+
init_resolve_caliber();
|
|
9967
10021
|
function log2(quiet, ...args) {
|
|
9968
10022
|
if (!quiet) console.log(...args);
|
|
9969
10023
|
}
|
|
@@ -10116,7 +10170,7 @@ async function refreshCommand(options) {
|
|
|
10116
10170
|
const config = loadConfig();
|
|
10117
10171
|
if (!config) {
|
|
10118
10172
|
if (quiet) return;
|
|
10119
|
-
console.log(chalk19.red("No LLM provider configured. Run ") + chalk19.hex("#83D1EB")(
|
|
10173
|
+
console.log(chalk19.red("No LLM provider configured. Run ") + chalk19.hex("#83D1EB")(`${resolveCaliber()} config`) + chalk19.red(" (e.g. choose Cursor) or set an API key."));
|
|
10120
10174
|
throw new Error("__exit__");
|
|
10121
10175
|
}
|
|
10122
10176
|
await validateModel({ fast: true });
|
|
@@ -10230,11 +10284,14 @@ var PRECOMMIT_START = "# caliber:pre-commit:start";
|
|
|
10230
10284
|
var PRECOMMIT_END = "# caliber:pre-commit:end";
|
|
10231
10285
|
function getPrecommitBlock() {
|
|
10232
10286
|
const bin = resolveCaliber();
|
|
10287
|
+
const npx = isNpxResolution();
|
|
10288
|
+
const guard = npx ? "command -v npx >/dev/null 2>&1" : `[ -x "${bin}" ] || command -v "${bin}" >/dev/null 2>&1`;
|
|
10289
|
+
const invoke = npx ? bin : `"${bin}"`;
|
|
10233
10290
|
return `${PRECOMMIT_START}
|
|
10234
|
-
if
|
|
10291
|
+
if ${guard}; then
|
|
10235
10292
|
echo "\\033[2mcaliber: refreshing docs...\\033[0m"
|
|
10236
|
-
|
|
10237
|
-
|
|
10293
|
+
${invoke} refresh 2>/dev/null || true
|
|
10294
|
+
${invoke} learn finalize 2>/dev/null || true
|
|
10238
10295
|
git diff --name-only -- CLAUDE.md .claude/ .cursor/ AGENTS.md CALIBER_LEARNINGS.md 2>/dev/null | xargs git add 2>/dev/null || true
|
|
10239
10296
|
fi
|
|
10240
10297
|
${PRECOMMIT_END}`;
|
|
@@ -10394,7 +10451,7 @@ async function hooksCommand(options) {
|
|
|
10394
10451
|
stdout.write(output + "\n");
|
|
10395
10452
|
lineCount = output.split("\n").length;
|
|
10396
10453
|
}
|
|
10397
|
-
return new Promise((
|
|
10454
|
+
return new Promise((resolve3) => {
|
|
10398
10455
|
console.log("");
|
|
10399
10456
|
draw(true);
|
|
10400
10457
|
stdin.setRawMode(true);
|
|
@@ -10452,14 +10509,14 @@ async function hooksCommand(options) {
|
|
|
10452
10509
|
case "\n":
|
|
10453
10510
|
cleanup();
|
|
10454
10511
|
apply();
|
|
10455
|
-
|
|
10512
|
+
resolve3();
|
|
10456
10513
|
break;
|
|
10457
10514
|
case "q":
|
|
10458
10515
|
case "\x1B":
|
|
10459
10516
|
case "":
|
|
10460
10517
|
cleanup();
|
|
10461
10518
|
console.log(chalk20.dim("\n Cancelled.\n"));
|
|
10462
|
-
|
|
10519
|
+
resolve3();
|
|
10463
10520
|
break;
|
|
10464
10521
|
}
|
|
10465
10522
|
}
|
|
@@ -10519,21 +10576,21 @@ import chalk23 from "chalk";
|
|
|
10519
10576
|
// src/learner/stdin.ts
|
|
10520
10577
|
var STDIN_TIMEOUT_MS = 5e3;
|
|
10521
10578
|
function readStdin() {
|
|
10522
|
-
return new Promise((
|
|
10579
|
+
return new Promise((resolve3, reject) => {
|
|
10523
10580
|
if (process.stdin.isTTY) {
|
|
10524
|
-
|
|
10581
|
+
resolve3("");
|
|
10525
10582
|
return;
|
|
10526
10583
|
}
|
|
10527
10584
|
const chunks = [];
|
|
10528
10585
|
const timer = setTimeout(() => {
|
|
10529
10586
|
process.stdin.removeAllListeners();
|
|
10530
10587
|
process.stdin.destroy();
|
|
10531
|
-
|
|
10588
|
+
resolve3(Buffer.concat(chunks).toString("utf-8"));
|
|
10532
10589
|
}, STDIN_TIMEOUT_MS);
|
|
10533
10590
|
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
10534
10591
|
process.stdin.on("end", () => {
|
|
10535
10592
|
clearTimeout(timer);
|
|
10536
|
-
|
|
10593
|
+
resolve3(Buffer.concat(chunks).toString("utf-8"));
|
|
10537
10594
|
});
|
|
10538
10595
|
process.stdin.on("error", (err) => {
|
|
10539
10596
|
clearTimeout(timer);
|
|
@@ -11139,6 +11196,7 @@ function findStaleLearnings(stats, minSessions = DEFAULT_MIN_SESSIONS) {
|
|
|
11139
11196
|
}
|
|
11140
11197
|
|
|
11141
11198
|
// src/commands/learn.ts
|
|
11199
|
+
init_resolve_caliber();
|
|
11142
11200
|
var MIN_EVENTS_FOR_ANALYSIS = 25;
|
|
11143
11201
|
var MIN_EVENTS_AUTO = 10;
|
|
11144
11202
|
var AUTO_SETTLE_MS = 200;
|
|
@@ -11242,7 +11300,7 @@ async function learnFinalizeCommand(options) {
|
|
|
11242
11300
|
const config = loadConfig();
|
|
11243
11301
|
if (!config) {
|
|
11244
11302
|
if (isAuto) return;
|
|
11245
|
-
console.log(chalk23.yellow(
|
|
11303
|
+
console.log(chalk23.yellow(`caliber: no LLM provider configured \u2014 run \`${resolveCaliber()} config\` first`));
|
|
11246
11304
|
clearSession();
|
|
11247
11305
|
resetState();
|
|
11248
11306
|
return;
|
|
@@ -11390,7 +11448,7 @@ async function learnFinalizeCommand(options) {
|
|
|
11390
11448
|
if (!isIncremental) {
|
|
11391
11449
|
const staleLearnings = findStaleLearnings(roiStats);
|
|
11392
11450
|
if (staleLearnings.length > 0 && !isAuto) {
|
|
11393
|
-
console.log(chalk23.yellow(`caliber: ${staleLearnings.length} learning${staleLearnings.length === 1 ? "" : "s"} never activated \u2014 run
|
|
11451
|
+
console.log(chalk23.yellow(`caliber: ${staleLearnings.length} learning${staleLearnings.length === 1 ? "" : "s"} never activated \u2014 run \`${resolveCaliber()} learn list --verbose\` to review`));
|
|
11394
11452
|
}
|
|
11395
11453
|
}
|
|
11396
11454
|
if (!isAuto && t.estimatedSavingsTokens > 0) {
|
|
@@ -11440,7 +11498,7 @@ async function learnInstallCommand() {
|
|
|
11440
11498
|
}
|
|
11441
11499
|
if (!fs44.existsSync(".claude") && !fs44.existsSync(".cursor")) {
|
|
11442
11500
|
console.log(chalk23.yellow("No .claude/ or .cursor/ directory found."));
|
|
11443
|
-
console.log(chalk23.dim(
|
|
11501
|
+
console.log(chalk23.dim(` Run \`${resolveCaliber()} init\` first, or create the directory manually.`));
|
|
11444
11502
|
return;
|
|
11445
11503
|
}
|
|
11446
11504
|
if (anyInstalled) {
|
|
@@ -11482,7 +11540,7 @@ async function learnStatusCommand() {
|
|
|
11482
11540
|
console.log(chalk23.dim("\u2717") + " Cursor hooks " + chalk23.dim("not installed"));
|
|
11483
11541
|
}
|
|
11484
11542
|
if (!claudeInstalled && !cursorInstalled) {
|
|
11485
|
-
console.log(chalk23.dim(
|
|
11543
|
+
console.log(chalk23.dim(` Run \`${resolveCaliber()} learn install\` to enable session learning.`));
|
|
11486
11544
|
}
|
|
11487
11545
|
console.log();
|
|
11488
11546
|
console.log(`Events recorded: ${chalk23.cyan(String(eventCount))}`);
|
|
@@ -11537,7 +11595,7 @@ function getAllLearnings() {
|
|
|
11537
11595
|
async function learnListCommand(options) {
|
|
11538
11596
|
const items = getAllLearnings();
|
|
11539
11597
|
if (items.length === 0) {
|
|
11540
|
-
console.log(chalk23.dim(
|
|
11598
|
+
console.log(chalk23.dim(`No learnings yet. Run \`${resolveCaliber()} learn install\` to start.`));
|
|
11541
11599
|
return;
|
|
11542
11600
|
}
|
|
11543
11601
|
const roiStats = options?.verbose ? readROIStats() : null;
|
|
@@ -11566,7 +11624,7 @@ async function learnListCommand(options) {
|
|
|
11566
11624
|
async function learnDeleteCommand(indexStr) {
|
|
11567
11625
|
const index = parseInt(indexStr, 10);
|
|
11568
11626
|
if (isNaN(index) || index < 1) {
|
|
11569
|
-
console.log(chalk23.red(`Invalid index: "${indexStr}". Use a number from
|
|
11627
|
+
console.log(chalk23.red(`Invalid index: "${indexStr}". Use a number from \`${resolveCaliber()} learn list\`.`));
|
|
11570
11628
|
return;
|
|
11571
11629
|
}
|
|
11572
11630
|
const items = getAllLearnings();
|
|
@@ -11631,6 +11689,7 @@ async function learnAddCommand(content, options) {
|
|
|
11631
11689
|
|
|
11632
11690
|
// src/commands/insights.ts
|
|
11633
11691
|
import chalk24 from "chalk";
|
|
11692
|
+
init_resolve_caliber();
|
|
11634
11693
|
var MIN_SESSIONS_FULL = 20;
|
|
11635
11694
|
function buildInsightsData(stats) {
|
|
11636
11695
|
const t = stats.totals;
|
|
@@ -11676,7 +11735,7 @@ function displayColdStart(score) {
|
|
|
11676
11735
|
console.log(chalk24.yellow(" Learning hooks not installed."));
|
|
11677
11736
|
console.log(chalk24.dim(" Session learning captures patterns from your AI coding sessions \u2014 what"));
|
|
11678
11737
|
console.log(chalk24.dim(" fails, what works, corrections you make \u2014 so your agents improve over time.\n"));
|
|
11679
|
-
console.log(chalk24.dim(" Run ") + chalk24.cyan(
|
|
11738
|
+
console.log(chalk24.dim(" Run ") + chalk24.cyan(`${resolveCaliber()} learn install`) + chalk24.dim(" to enable."));
|
|
11680
11739
|
} else {
|
|
11681
11740
|
console.log(chalk24.dim(" Learning hooks are active. Use your AI agent and insights"));
|
|
11682
11741
|
console.log(chalk24.dim(" will appear automatically after each session.\n"));
|
|
@@ -11779,13 +11838,14 @@ async function insightsCommand(options) {
|
|
|
11779
11838
|
import fs45 from "fs";
|
|
11780
11839
|
import path36 from "path";
|
|
11781
11840
|
import chalk25 from "chalk";
|
|
11841
|
+
init_resolve_caliber();
|
|
11782
11842
|
async function sourcesListCommand() {
|
|
11783
11843
|
const dir = process.cwd();
|
|
11784
11844
|
const configSources = loadSourcesConfig(dir);
|
|
11785
11845
|
const workspaces = getDetectedWorkspaces(dir);
|
|
11786
11846
|
if (configSources.length === 0 && workspaces.length === 0) {
|
|
11787
11847
|
console.log(chalk25.dim("\n No sources configured.\n"));
|
|
11788
|
-
console.log(chalk25.dim(" Add a source: ") + chalk25.hex("#83D1EB")(
|
|
11848
|
+
console.log(chalk25.dim(" Add a source: ") + chalk25.hex("#83D1EB")(`${resolveCaliber()} sources add <path>`));
|
|
11789
11849
|
console.log(chalk25.dim(" Or add to .caliber/sources.json manually.\n"));
|
|
11790
11850
|
return;
|
|
11791
11851
|
}
|
|
@@ -11882,11 +11942,12 @@ import path37 from "path";
|
|
|
11882
11942
|
import chalk26 from "chalk";
|
|
11883
11943
|
import ora7 from "ora";
|
|
11884
11944
|
init_config();
|
|
11945
|
+
init_resolve_caliber();
|
|
11885
11946
|
async function publishCommand() {
|
|
11886
11947
|
const dir = process.cwd();
|
|
11887
11948
|
const config = loadConfig();
|
|
11888
11949
|
if (!config) {
|
|
11889
|
-
console.log(chalk26.red("No LLM provider configured. Run ") + chalk26.hex("#83D1EB")(
|
|
11950
|
+
console.log(chalk26.red("No LLM provider configured. Run ") + chalk26.hex("#83D1EB")(`${resolveCaliber()} config`) + chalk26.red(" first."));
|
|
11890
11951
|
throw new Error("__exit__");
|
|
11891
11952
|
}
|
|
11892
11953
|
const spinner = ora7("Generating project summary...").start();
|
|
@@ -12031,7 +12092,7 @@ learn.command("add <content>").description("Add a learning directly (used by age
|
|
|
12031
12092
|
import fs48 from "fs";
|
|
12032
12093
|
import path39 from "path";
|
|
12033
12094
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
12034
|
-
import { execSync as execSync16 } from "child_process";
|
|
12095
|
+
import { execSync as execSync16, execFileSync as execFileSync3 } from "child_process";
|
|
12035
12096
|
import chalk27 from "chalk";
|
|
12036
12097
|
import ora8 from "ora";
|
|
12037
12098
|
import confirm2 from "@inquirer/confirm";
|
|
@@ -12107,9 +12168,10 @@ Update available: ${current} -> ${latest}`)
|
|
|
12107
12168
|
return;
|
|
12108
12169
|
}
|
|
12109
12170
|
const tag = channel === "latest" ? latest : channel;
|
|
12171
|
+
if (!/^[\w.\-]+$/.test(tag)) return;
|
|
12110
12172
|
const spinner = ora8("Updating caliber...").start();
|
|
12111
12173
|
try {
|
|
12112
|
-
|
|
12174
|
+
execFileSync3("npm", ["install", "-g", `@rely-ai/caliber@${tag}`], {
|
|
12113
12175
|
stdio: "pipe",
|
|
12114
12176
|
timeout: 12e4,
|
|
12115
12177
|
env: { ...process.env, npm_config_fund: "false", npm_config_audit: "false" }
|
|
@@ -12126,7 +12188,7 @@ Update available: ${current} -> ${latest}`)
|
|
|
12126
12188
|
console.log(chalk27.dim(`
|
|
12127
12189
|
Restarting: caliber ${args.join(" ")}
|
|
12128
12190
|
`));
|
|
12129
|
-
|
|
12191
|
+
execFileSync3("caliber", args, {
|
|
12130
12192
|
stdio: "inherit",
|
|
12131
12193
|
env: { ...process.env, CALIBER_SKIP_UPDATE_CHECK: "1" }
|
|
12132
12194
|
});
|