@rely-ai/caliber 1.30.1 → 1.30.3
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 +269 -214
- 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";
|
|
@@ -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
|
}
|
|
@@ -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";
|
|
@@ -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 {
|
|
@@ -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";
|
|
@@ -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
|
}
|
|
@@ -6574,6 +6613,7 @@ function trackInsightsViewed(totalSessions, learningCount) {
|
|
|
6574
6613
|
}
|
|
6575
6614
|
|
|
6576
6615
|
// src/commands/recommend.ts
|
|
6616
|
+
init_resolve_caliber();
|
|
6577
6617
|
function detectLocalPlatforms() {
|
|
6578
6618
|
const items = scanLocalState(process.cwd());
|
|
6579
6619
|
const platforms = /* @__PURE__ */ new Set();
|
|
@@ -6950,7 +6990,7 @@ async function querySkills(query) {
|
|
|
6950
6990
|
console.log(` ${r.reason || r.name}`);
|
|
6951
6991
|
}
|
|
6952
6992
|
console.log("");
|
|
6953
|
-
console.log(chalk6.dim(` Install with:
|
|
6993
|
+
console.log(chalk6.dim(` Install with: ${resolveCaliber()} skills --install ${available.map((r) => r.slug).join(",")}`));
|
|
6954
6994
|
console.log("");
|
|
6955
6995
|
}
|
|
6956
6996
|
async function installBySlug(slugStr) {
|
|
@@ -8713,6 +8753,7 @@ function log(verbose, ...args) {
|
|
|
8713
8753
|
async function initCommand(options) {
|
|
8714
8754
|
const brand = chalk14.hex("#EB9D83");
|
|
8715
8755
|
const title = chalk14.hex("#83D1EB");
|
|
8756
|
+
const bin = resolveCaliber();
|
|
8716
8757
|
const firstRun = isFirstRun(process.cwd());
|
|
8717
8758
|
if (firstRun) {
|
|
8718
8759
|
console.log(brand.bold(`
|
|
@@ -8810,7 +8851,7 @@ async function initCommand(options) {
|
|
|
8810
8851
|
if (hasExistingConfig && baselineScore.score === 100) {
|
|
8811
8852
|
trackInitScoreComputed(baselineScore.score, passingCount, failingCount, true);
|
|
8812
8853
|
console.log(chalk14.bold.green(" Your config is already optimal \u2014 nothing to change.\n"));
|
|
8813
|
-
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"));
|
|
8814
8855
|
if (!options.force) return;
|
|
8815
8856
|
}
|
|
8816
8857
|
const allFailingChecks = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
|
|
@@ -8826,7 +8867,7 @@ async function initCommand(options) {
|
|
|
8826
8867
|
}
|
|
8827
8868
|
}
|
|
8828
8869
|
console.log("");
|
|
8829
|
-
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"));
|
|
8830
8871
|
return;
|
|
8831
8872
|
}
|
|
8832
8873
|
console.log(title.bold(" Step 2/4 \u2014 Engine\n"));
|
|
@@ -9161,7 +9202,7 @@ ${agentRefs.join(" ")}
|
|
|
9161
9202
|
}
|
|
9162
9203
|
} catch {
|
|
9163
9204
|
}
|
|
9164
|
-
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"));
|
|
9165
9206
|
return;
|
|
9166
9207
|
}
|
|
9167
9208
|
if (report) {
|
|
@@ -9191,7 +9232,7 @@ ${agentRefs.join(" ")}
|
|
|
9191
9232
|
}
|
|
9192
9233
|
}
|
|
9193
9234
|
console.log("");
|
|
9194
|
-
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`)}`);
|
|
9195
9236
|
trackInitHookSelected("config-instructions");
|
|
9196
9237
|
const hasLearnableAgent = targetAgent.includes("claude") || targetAgent.includes("cursor");
|
|
9197
9238
|
let enableLearn = false;
|
|
@@ -9210,9 +9251,9 @@ ${agentRefs.join(" ")}
|
|
|
9210
9251
|
if (r.installed) console.log(` ${chalk14.green("\u2713")} Learning hooks installed for Cursor`);
|
|
9211
9252
|
else if (r.alreadyInstalled) console.log(chalk14.dim(" Cursor learning hooks already installed"));
|
|
9212
9253
|
}
|
|
9213
|
-
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"));
|
|
9214
9255
|
} else {
|
|
9215
|
-
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."));
|
|
9216
9257
|
}
|
|
9217
9258
|
} else {
|
|
9218
9259
|
enableLearn = true;
|
|
@@ -9226,13 +9267,13 @@ ${agentRefs.join(" ")}
|
|
|
9226
9267
|
const done = chalk14.green("\u2713");
|
|
9227
9268
|
const skip = chalk14.dim("\u2013");
|
|
9228
9269
|
console.log(chalk14.bold(" What was configured:\n"));
|
|
9229
|
-
console.log(` ${done} Config generated ${title(
|
|
9230
|
-
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`)}`);
|
|
9231
9272
|
if (hasLearnableAgent) {
|
|
9232
9273
|
if (enableLearn) {
|
|
9233
9274
|
console.log(` ${done} Session learning ${chalk14.dim("agent learns from your feedback")}`);
|
|
9234
9275
|
} else {
|
|
9235
|
-
console.log(` ${skip} Session learning ${title(
|
|
9276
|
+
console.log(` ${skip} Session learning ${title(`${bin} learn install`)} to enable later`);
|
|
9236
9277
|
}
|
|
9237
9278
|
}
|
|
9238
9279
|
if (communitySkillsInstalled > 0) {
|
|
@@ -9241,9 +9282,9 @@ ${agentRefs.join(" ")}
|
|
|
9241
9282
|
console.log(` ${skip} Community skills ${chalk14.dim("available but skipped")}`);
|
|
9242
9283
|
}
|
|
9243
9284
|
console.log(chalk14.bold("\n Explore next:\n"));
|
|
9244
|
-
console.log(` ${title(
|
|
9245
|
-
console.log(` ${title(
|
|
9246
|
-
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`);
|
|
9247
9288
|
console.log("");
|
|
9248
9289
|
if (options.showTokens) {
|
|
9249
9290
|
displayTokenUsage();
|
|
@@ -9293,6 +9334,7 @@ function undoCommand() {
|
|
|
9293
9334
|
import chalk16 from "chalk";
|
|
9294
9335
|
import fs33 from "fs";
|
|
9295
9336
|
init_config();
|
|
9337
|
+
init_resolve_caliber();
|
|
9296
9338
|
async function statusCommand(options) {
|
|
9297
9339
|
const config = loadConfig();
|
|
9298
9340
|
const manifest = readManifest();
|
|
@@ -9309,11 +9351,12 @@ async function statusCommand(options) {
|
|
|
9309
9351
|
if (config) {
|
|
9310
9352
|
console.log(` LLM: ${chalk16.green(config.provider)} (${config.model})`);
|
|
9311
9353
|
} else {
|
|
9312
|
-
|
|
9354
|
+
const bin = resolveCaliber();
|
|
9355
|
+
console.log(` LLM: ${chalk16.yellow("Not configured")} \u2014 run ${chalk16.hex("#83D1EB")(`${bin} config`)}`);
|
|
9313
9356
|
}
|
|
9314
9357
|
if (!manifest) {
|
|
9315
9358
|
console.log(` Config: ${chalk16.dim("No config applied")}`);
|
|
9316
|
-
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"));
|
|
9317
9360
|
return;
|
|
9318
9361
|
}
|
|
9319
9362
|
console.log(` Files managed: ${chalk16.cyan(manifest.entries.length.toString())}`);
|
|
@@ -9331,15 +9374,17 @@ import ora5 from "ora";
|
|
|
9331
9374
|
import select6 from "@inquirer/select";
|
|
9332
9375
|
init_review();
|
|
9333
9376
|
init_config();
|
|
9377
|
+
init_resolve_caliber();
|
|
9334
9378
|
async function regenerateCommand(options) {
|
|
9379
|
+
const bin = resolveCaliber();
|
|
9335
9380
|
const config = loadConfig();
|
|
9336
9381
|
if (!config) {
|
|
9337
|
-
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."));
|
|
9338
9383
|
throw new Error("__exit__");
|
|
9339
9384
|
}
|
|
9340
9385
|
const manifest = readManifest();
|
|
9341
9386
|
if (!manifest) {
|
|
9342
|
-
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."));
|
|
9343
9388
|
throw new Error("__exit__");
|
|
9344
9389
|
}
|
|
9345
9390
|
const targetAgent = readState()?.targetAgent ?? ["claude", "cursor"];
|
|
@@ -9463,13 +9508,13 @@ async function regenerateCommand(options) {
|
|
|
9463
9508
|
}
|
|
9464
9509
|
} catch {
|
|
9465
9510
|
}
|
|
9466
|
-
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"));
|
|
9467
9512
|
return;
|
|
9468
9513
|
}
|
|
9469
9514
|
displayScoreDelta(baselineScore, afterScore);
|
|
9470
9515
|
trackRegenerateCompleted(action, Date.now());
|
|
9471
9516
|
console.log(chalk17.bold.green(" Regeneration complete!"));
|
|
9472
|
-
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"));
|
|
9473
9518
|
}
|
|
9474
9519
|
|
|
9475
9520
|
// src/commands/score.ts
|
|
@@ -9478,6 +9523,7 @@ import os7 from "os";
|
|
|
9478
9523
|
import path26 from "path";
|
|
9479
9524
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
9480
9525
|
import chalk18 from "chalk";
|
|
9526
|
+
init_resolve_caliber();
|
|
9481
9527
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".cursorrules", "CALIBER_LEARNINGS.md"];
|
|
9482
9528
|
var CONFIG_DIRS = [".claude", ".cursor"];
|
|
9483
9529
|
function scoreBaseRef(ref, target) {
|
|
@@ -9558,12 +9604,13 @@ async function scoreCommand(options) {
|
|
|
9558
9604
|
displayScore(result);
|
|
9559
9605
|
const separator = chalk18.gray(" " + "\u2500".repeat(53));
|
|
9560
9606
|
console.log(separator);
|
|
9607
|
+
const bin = resolveCaliber();
|
|
9561
9608
|
if (result.score < 40) {
|
|
9562
|
-
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."));
|
|
9563
9610
|
} else if (result.score < 70) {
|
|
9564
|
-
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."));
|
|
9565
9612
|
} else {
|
|
9566
|
-
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."));
|
|
9567
9614
|
}
|
|
9568
9615
|
console.log("");
|
|
9569
9616
|
}
|
|
@@ -9970,6 +10017,7 @@ function migrateInlineLearnings() {
|
|
|
9970
10017
|
|
|
9971
10018
|
// src/commands/refresh.ts
|
|
9972
10019
|
init_config();
|
|
10020
|
+
init_resolve_caliber();
|
|
9973
10021
|
function log2(quiet, ...args) {
|
|
9974
10022
|
if (!quiet) console.log(...args);
|
|
9975
10023
|
}
|
|
@@ -10122,7 +10170,7 @@ async function refreshCommand(options) {
|
|
|
10122
10170
|
const config = loadConfig();
|
|
10123
10171
|
if (!config) {
|
|
10124
10172
|
if (quiet) return;
|
|
10125
|
-
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."));
|
|
10126
10174
|
throw new Error("__exit__");
|
|
10127
10175
|
}
|
|
10128
10176
|
await validateModel({ fast: true });
|
|
@@ -10236,11 +10284,14 @@ var PRECOMMIT_START = "# caliber:pre-commit:start";
|
|
|
10236
10284
|
var PRECOMMIT_END = "# caliber:pre-commit:end";
|
|
10237
10285
|
function getPrecommitBlock() {
|
|
10238
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}"`;
|
|
10239
10290
|
return `${PRECOMMIT_START}
|
|
10240
|
-
if
|
|
10291
|
+
if ${guard}; then
|
|
10241
10292
|
echo "\\033[2mcaliber: refreshing docs...\\033[0m"
|
|
10242
|
-
|
|
10243
|
-
|
|
10293
|
+
${invoke} refresh 2>/dev/null || true
|
|
10294
|
+
${invoke} learn finalize 2>/dev/null || true
|
|
10244
10295
|
git diff --name-only -- CLAUDE.md .claude/ .cursor/ AGENTS.md CALIBER_LEARNINGS.md 2>/dev/null | xargs git add 2>/dev/null || true
|
|
10245
10296
|
fi
|
|
10246
10297
|
${PRECOMMIT_END}`;
|
|
@@ -11145,6 +11196,7 @@ function findStaleLearnings(stats, minSessions = DEFAULT_MIN_SESSIONS) {
|
|
|
11145
11196
|
}
|
|
11146
11197
|
|
|
11147
11198
|
// src/commands/learn.ts
|
|
11199
|
+
init_resolve_caliber();
|
|
11148
11200
|
var MIN_EVENTS_FOR_ANALYSIS = 25;
|
|
11149
11201
|
var MIN_EVENTS_AUTO = 10;
|
|
11150
11202
|
var AUTO_SETTLE_MS = 200;
|
|
@@ -11248,7 +11300,7 @@ async function learnFinalizeCommand(options) {
|
|
|
11248
11300
|
const config = loadConfig();
|
|
11249
11301
|
if (!config) {
|
|
11250
11302
|
if (isAuto) return;
|
|
11251
|
-
console.log(chalk23.yellow(
|
|
11303
|
+
console.log(chalk23.yellow(`caliber: no LLM provider configured \u2014 run \`${resolveCaliber()} config\` first`));
|
|
11252
11304
|
clearSession();
|
|
11253
11305
|
resetState();
|
|
11254
11306
|
return;
|
|
@@ -11396,7 +11448,7 @@ async function learnFinalizeCommand(options) {
|
|
|
11396
11448
|
if (!isIncremental) {
|
|
11397
11449
|
const staleLearnings = findStaleLearnings(roiStats);
|
|
11398
11450
|
if (staleLearnings.length > 0 && !isAuto) {
|
|
11399
|
-
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`));
|
|
11400
11452
|
}
|
|
11401
11453
|
}
|
|
11402
11454
|
if (!isAuto && t.estimatedSavingsTokens > 0) {
|
|
@@ -11446,7 +11498,7 @@ async function learnInstallCommand() {
|
|
|
11446
11498
|
}
|
|
11447
11499
|
if (!fs44.existsSync(".claude") && !fs44.existsSync(".cursor")) {
|
|
11448
11500
|
console.log(chalk23.yellow("No .claude/ or .cursor/ directory found."));
|
|
11449
|
-
console.log(chalk23.dim(
|
|
11501
|
+
console.log(chalk23.dim(` Run \`${resolveCaliber()} init\` first, or create the directory manually.`));
|
|
11450
11502
|
return;
|
|
11451
11503
|
}
|
|
11452
11504
|
if (anyInstalled) {
|
|
@@ -11488,7 +11540,7 @@ async function learnStatusCommand() {
|
|
|
11488
11540
|
console.log(chalk23.dim("\u2717") + " Cursor hooks " + chalk23.dim("not installed"));
|
|
11489
11541
|
}
|
|
11490
11542
|
if (!claudeInstalled && !cursorInstalled) {
|
|
11491
|
-
console.log(chalk23.dim(
|
|
11543
|
+
console.log(chalk23.dim(` Run \`${resolveCaliber()} learn install\` to enable session learning.`));
|
|
11492
11544
|
}
|
|
11493
11545
|
console.log();
|
|
11494
11546
|
console.log(`Events recorded: ${chalk23.cyan(String(eventCount))}`);
|
|
@@ -11543,7 +11595,7 @@ function getAllLearnings() {
|
|
|
11543
11595
|
async function learnListCommand(options) {
|
|
11544
11596
|
const items = getAllLearnings();
|
|
11545
11597
|
if (items.length === 0) {
|
|
11546
|
-
console.log(chalk23.dim(
|
|
11598
|
+
console.log(chalk23.dim(`No learnings yet. Run \`${resolveCaliber()} learn install\` to start.`));
|
|
11547
11599
|
return;
|
|
11548
11600
|
}
|
|
11549
11601
|
const roiStats = options?.verbose ? readROIStats() : null;
|
|
@@ -11572,7 +11624,7 @@ async function learnListCommand(options) {
|
|
|
11572
11624
|
async function learnDeleteCommand(indexStr) {
|
|
11573
11625
|
const index = parseInt(indexStr, 10);
|
|
11574
11626
|
if (isNaN(index) || index < 1) {
|
|
11575
|
-
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\`.`));
|
|
11576
11628
|
return;
|
|
11577
11629
|
}
|
|
11578
11630
|
const items = getAllLearnings();
|
|
@@ -11637,6 +11689,7 @@ async function learnAddCommand(content, options) {
|
|
|
11637
11689
|
|
|
11638
11690
|
// src/commands/insights.ts
|
|
11639
11691
|
import chalk24 from "chalk";
|
|
11692
|
+
init_resolve_caliber();
|
|
11640
11693
|
var MIN_SESSIONS_FULL = 20;
|
|
11641
11694
|
function buildInsightsData(stats) {
|
|
11642
11695
|
const t = stats.totals;
|
|
@@ -11682,7 +11735,7 @@ function displayColdStart(score) {
|
|
|
11682
11735
|
console.log(chalk24.yellow(" Learning hooks not installed."));
|
|
11683
11736
|
console.log(chalk24.dim(" Session learning captures patterns from your AI coding sessions \u2014 what"));
|
|
11684
11737
|
console.log(chalk24.dim(" fails, what works, corrections you make \u2014 so your agents improve over time.\n"));
|
|
11685
|
-
console.log(chalk24.dim(" Run ") + chalk24.cyan(
|
|
11738
|
+
console.log(chalk24.dim(" Run ") + chalk24.cyan(`${resolveCaliber()} learn install`) + chalk24.dim(" to enable."));
|
|
11686
11739
|
} else {
|
|
11687
11740
|
console.log(chalk24.dim(" Learning hooks are active. Use your AI agent and insights"));
|
|
11688
11741
|
console.log(chalk24.dim(" will appear automatically after each session.\n"));
|
|
@@ -11785,13 +11838,14 @@ async function insightsCommand(options) {
|
|
|
11785
11838
|
import fs45 from "fs";
|
|
11786
11839
|
import path36 from "path";
|
|
11787
11840
|
import chalk25 from "chalk";
|
|
11841
|
+
init_resolve_caliber();
|
|
11788
11842
|
async function sourcesListCommand() {
|
|
11789
11843
|
const dir = process.cwd();
|
|
11790
11844
|
const configSources = loadSourcesConfig(dir);
|
|
11791
11845
|
const workspaces = getDetectedWorkspaces(dir);
|
|
11792
11846
|
if (configSources.length === 0 && workspaces.length === 0) {
|
|
11793
11847
|
console.log(chalk25.dim("\n No sources configured.\n"));
|
|
11794
|
-
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>`));
|
|
11795
11849
|
console.log(chalk25.dim(" Or add to .caliber/sources.json manually.\n"));
|
|
11796
11850
|
return;
|
|
11797
11851
|
}
|
|
@@ -11888,11 +11942,12 @@ import path37 from "path";
|
|
|
11888
11942
|
import chalk26 from "chalk";
|
|
11889
11943
|
import ora7 from "ora";
|
|
11890
11944
|
init_config();
|
|
11945
|
+
init_resolve_caliber();
|
|
11891
11946
|
async function publishCommand() {
|
|
11892
11947
|
const dir = process.cwd();
|
|
11893
11948
|
const config = loadConfig();
|
|
11894
11949
|
if (!config) {
|
|
11895
|
-
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."));
|
|
11896
11951
|
throw new Error("__exit__");
|
|
11897
11952
|
}
|
|
11898
11953
|
const spinner = ora7("Generating project summary...").start();
|