@rely-ai/caliber 0.2.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -12
- package/dist/bin.js +676 -326
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -56,10 +56,10 @@ import path21 from "path";
|
|
|
56
56
|
import { fileURLToPath } from "url";
|
|
57
57
|
|
|
58
58
|
// src/commands/init.ts
|
|
59
|
-
import
|
|
59
|
+
import chalk4 from "chalk";
|
|
60
60
|
import ora from "ora";
|
|
61
|
-
import
|
|
62
|
-
import
|
|
61
|
+
import readline3 from "readline";
|
|
62
|
+
import select2 from "@inquirer/select";
|
|
63
63
|
import fs17 from "fs";
|
|
64
64
|
|
|
65
65
|
// src/fingerprint/index.ts
|
|
@@ -326,6 +326,7 @@ var IGNORE_DIRS2 = /* @__PURE__ */ new Set([
|
|
|
326
326
|
]);
|
|
327
327
|
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".py"]);
|
|
328
328
|
var CONFIG_FILE_NAMES = /* @__PURE__ */ new Set([
|
|
329
|
+
"package.json",
|
|
329
330
|
"Dockerfile",
|
|
330
331
|
"docker-compose.yml",
|
|
331
332
|
"docker-compose.yaml",
|
|
@@ -333,6 +334,10 @@ var CONFIG_FILE_NAMES = /* @__PURE__ */ new Set([
|
|
|
333
334
|
"tsconfig.json",
|
|
334
335
|
"pyproject.toml",
|
|
335
336
|
"turbo.json",
|
|
337
|
+
"requirements.txt",
|
|
338
|
+
"go.mod",
|
|
339
|
+
"Cargo.toml",
|
|
340
|
+
"Gemfile",
|
|
336
341
|
"next.config.js",
|
|
337
342
|
"next.config.mjs",
|
|
338
343
|
"next.config.ts",
|
|
@@ -586,7 +591,9 @@ var CONFIG_FILE = path6.join(CONFIG_DIR, "config.json");
|
|
|
586
591
|
var DEFAULT_MODELS = {
|
|
587
592
|
anthropic: "claude-sonnet-4-6",
|
|
588
593
|
vertex: "claude-sonnet-4-6",
|
|
589
|
-
openai: "gpt-4.1"
|
|
594
|
+
openai: "gpt-4.1",
|
|
595
|
+
cursor: "default",
|
|
596
|
+
"claude-cli": "default"
|
|
590
597
|
};
|
|
591
598
|
function loadConfig() {
|
|
592
599
|
const envConfig = resolveFromEnv();
|
|
@@ -618,6 +625,18 @@ function resolveFromEnv() {
|
|
|
618
625
|
baseUrl: process.env.OPENAI_BASE_URL
|
|
619
626
|
};
|
|
620
627
|
}
|
|
628
|
+
if (process.env.CALIBER_USE_CURSOR_SEAT === "1" || process.env.CALIBER_USE_CURSOR_SEAT === "true") {
|
|
629
|
+
return {
|
|
630
|
+
provider: "cursor",
|
|
631
|
+
model: DEFAULT_MODELS.cursor
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
if (process.env.CALIBER_USE_CLAUDE_CLI === "1" || process.env.CALIBER_USE_CLAUDE_CLI === "true") {
|
|
635
|
+
return {
|
|
636
|
+
provider: "claude-cli",
|
|
637
|
+
model: DEFAULT_MODELS["claude-cli"]
|
|
638
|
+
};
|
|
639
|
+
}
|
|
621
640
|
return null;
|
|
622
641
|
}
|
|
623
642
|
function readConfigFile() {
|
|
@@ -625,7 +644,7 @@ function readConfigFile() {
|
|
|
625
644
|
if (!fs5.existsSync(CONFIG_FILE)) return null;
|
|
626
645
|
const raw = fs5.readFileSync(CONFIG_FILE, "utf-8");
|
|
627
646
|
const parsed = JSON.parse(raw);
|
|
628
|
-
if (!parsed.provider || !["anthropic", "vertex", "openai"].includes(parsed.provider)) {
|
|
647
|
+
if (!parsed.provider || !["anthropic", "vertex", "openai", "cursor", "claude-cli"].includes(parsed.provider)) {
|
|
629
648
|
return null;
|
|
630
649
|
}
|
|
631
650
|
return parsed;
|
|
@@ -817,6 +836,252 @@ var OpenAICompatProvider = class {
|
|
|
817
836
|
}
|
|
818
837
|
};
|
|
819
838
|
|
|
839
|
+
// src/llm/cursor-acp.ts
|
|
840
|
+
import { spawn, execSync as execSync2 } from "child_process";
|
|
841
|
+
import readline from "readline";
|
|
842
|
+
var ACP_AGENT_BIN = "agent";
|
|
843
|
+
var CursorAcpProvider = class {
|
|
844
|
+
defaultModel;
|
|
845
|
+
cursorApiKey;
|
|
846
|
+
constructor(config) {
|
|
847
|
+
this.defaultModel = config.model || "default";
|
|
848
|
+
this.cursorApiKey = process.env.CURSOR_API_KEY ?? process.env.CURSOR_AUTH_TOKEN;
|
|
849
|
+
}
|
|
850
|
+
async call(options) {
|
|
851
|
+
const chunks = [];
|
|
852
|
+
await this.runAcpPrompt(options, {
|
|
853
|
+
onText: (text) => chunks.push(text),
|
|
854
|
+
onEnd: () => {
|
|
855
|
+
},
|
|
856
|
+
onError: () => {
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
return chunks.join("");
|
|
860
|
+
}
|
|
861
|
+
async stream(options, callbacks) {
|
|
862
|
+
await this.runAcpPrompt(options, callbacks);
|
|
863
|
+
}
|
|
864
|
+
async runAcpPrompt(options, callbacks) {
|
|
865
|
+
const combinedPrompt = this.buildCombinedPrompt(options);
|
|
866
|
+
const args = ["acp"];
|
|
867
|
+
if (this.cursorApiKey) {
|
|
868
|
+
args.unshift("--api-key", this.cursorApiKey);
|
|
869
|
+
}
|
|
870
|
+
const agent = spawn(ACP_AGENT_BIN, args, {
|
|
871
|
+
stdio: ["pipe", "pipe", "inherit"],
|
|
872
|
+
cwd: process.cwd(),
|
|
873
|
+
env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } }
|
|
874
|
+
});
|
|
875
|
+
const pending = /* @__PURE__ */ new Map();
|
|
876
|
+
let nextId = 1;
|
|
877
|
+
let sessionId = null;
|
|
878
|
+
const send = (method, params) => {
|
|
879
|
+
return new Promise((resolve2, reject) => {
|
|
880
|
+
const id = nextId++;
|
|
881
|
+
pending.set(id, { resolve: resolve2, reject });
|
|
882
|
+
const msg = { jsonrpc: "2.0", id, method, params };
|
|
883
|
+
agent.stdin.write(JSON.stringify(msg) + "\n", (err) => {
|
|
884
|
+
if (err) {
|
|
885
|
+
pending.delete(id);
|
|
886
|
+
reject(err);
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
});
|
|
890
|
+
};
|
|
891
|
+
const rl = readline.createInterface({ input: agent.stdout, crlfDelay: Infinity });
|
|
892
|
+
rl.on("line", (line) => {
|
|
893
|
+
let msg;
|
|
894
|
+
try {
|
|
895
|
+
msg = JSON.parse(line);
|
|
896
|
+
} catch {
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
if (msg.id != null && (msg.result !== void 0 || msg.error !== void 0)) {
|
|
900
|
+
const waiter = pending.get(msg.id);
|
|
901
|
+
if (waiter) {
|
|
902
|
+
pending.delete(msg.id);
|
|
903
|
+
if (msg.error) {
|
|
904
|
+
waiter.reject(new Error(msg.error.message || "ACP error"));
|
|
905
|
+
} else {
|
|
906
|
+
waiter.resolve(msg.result);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
if (msg.result && typeof msg.result === "object" && "sessionId" in msg.result) {
|
|
910
|
+
sessionId = msg.result.sessionId;
|
|
911
|
+
}
|
|
912
|
+
if (msg.result && typeof msg.result === "object" && "stopReason" in msg.result) {
|
|
913
|
+
callbacks.onEnd({
|
|
914
|
+
stopReason: msg.result.stopReason
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
if (msg.method === "session/update" && msg.params?.update) {
|
|
920
|
+
const update = msg.params.update;
|
|
921
|
+
if (update.sessionUpdate === "agent_message_chunk" && update.content?.text) {
|
|
922
|
+
callbacks.onText(update.content.text);
|
|
923
|
+
}
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
if (msg.method === "session/request_permission" && msg.id != null) {
|
|
927
|
+
const response = JSON.stringify({
|
|
928
|
+
jsonrpc: "2.0",
|
|
929
|
+
id: msg.id,
|
|
930
|
+
result: { outcome: { outcome: "selected", optionId: "allow-once" } }
|
|
931
|
+
}) + "\n";
|
|
932
|
+
agent.stdin.write(response);
|
|
933
|
+
}
|
|
934
|
+
});
|
|
935
|
+
agent.on("error", (err) => {
|
|
936
|
+
for (const w of pending.values()) w.reject(err);
|
|
937
|
+
callbacks.onError(err);
|
|
938
|
+
});
|
|
939
|
+
agent.on("close", (code) => {
|
|
940
|
+
if (code !== 0 && code !== null) {
|
|
941
|
+
const err = new Error(`Cursor agent exited with code ${code}`);
|
|
942
|
+
for (const w of pending.values()) w.reject(err);
|
|
943
|
+
callbacks.onError(err);
|
|
944
|
+
}
|
|
945
|
+
});
|
|
946
|
+
try {
|
|
947
|
+
await send("initialize", {
|
|
948
|
+
protocolVersion: 1,
|
|
949
|
+
clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false },
|
|
950
|
+
clientInfo: { name: "caliber", version: "1.0.0" }
|
|
951
|
+
});
|
|
952
|
+
await send("authenticate", { methodId: "cursor_login" });
|
|
953
|
+
const sessionResult = await send("session/new", {
|
|
954
|
+
cwd: process.cwd(),
|
|
955
|
+
mcpServers: []
|
|
956
|
+
});
|
|
957
|
+
sessionId = sessionResult.sessionId;
|
|
958
|
+
await send("session/prompt", {
|
|
959
|
+
sessionId,
|
|
960
|
+
prompt: [{ type: "text", text: combinedPrompt }]
|
|
961
|
+
});
|
|
962
|
+
} catch (err) {
|
|
963
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
964
|
+
callbacks.onError(error);
|
|
965
|
+
throw error;
|
|
966
|
+
} finally {
|
|
967
|
+
agent.stdin?.end();
|
|
968
|
+
agent.kill("SIGTERM");
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
buildCombinedPrompt(options) {
|
|
972
|
+
const streamOpts = options;
|
|
973
|
+
const hasHistory = streamOpts.messages && streamOpts.messages.length > 0;
|
|
974
|
+
let combined = "";
|
|
975
|
+
combined += "[[System]]\n" + options.system + "\n\n";
|
|
976
|
+
if (hasHistory) {
|
|
977
|
+
for (const msg of streamOpts.messages) {
|
|
978
|
+
combined += `[[${msg.role === "user" ? "User" : "Assistant"}]]
|
|
979
|
+
${msg.content}
|
|
980
|
+
|
|
981
|
+
`;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
combined += "[[User]]\n" + options.prompt;
|
|
985
|
+
return combined;
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
function isCursorAgentAvailable() {
|
|
989
|
+
try {
|
|
990
|
+
execSync2(`which ${ACP_AGENT_BIN}`, { stdio: "ignore" });
|
|
991
|
+
return true;
|
|
992
|
+
} catch {
|
|
993
|
+
return false;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
// src/llm/claude-cli.ts
|
|
998
|
+
import { spawn as spawn2, execSync as execSync3 } from "child_process";
|
|
999
|
+
var CLAUDE_CLI_BIN = "claude";
|
|
1000
|
+
var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
1001
|
+
var ClaudeCliProvider = class {
|
|
1002
|
+
defaultModel;
|
|
1003
|
+
timeoutMs;
|
|
1004
|
+
constructor(config) {
|
|
1005
|
+
this.defaultModel = config.model || "default";
|
|
1006
|
+
const envTimeout = process.env.CALIBER_CLAUDE_CLI_TIMEOUT_MS;
|
|
1007
|
+
this.timeoutMs = envTimeout ? parseInt(envTimeout, 10) : DEFAULT_TIMEOUT_MS;
|
|
1008
|
+
if (!Number.isFinite(this.timeoutMs) || this.timeoutMs < 1e3) {
|
|
1009
|
+
this.timeoutMs = DEFAULT_TIMEOUT_MS;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
async call(options) {
|
|
1013
|
+
const combined = this.buildCombinedPrompt(options);
|
|
1014
|
+
return this.runClaudePrint(combined);
|
|
1015
|
+
}
|
|
1016
|
+
async stream(options, callbacks) {
|
|
1017
|
+
try {
|
|
1018
|
+
const text = await this.call(options);
|
|
1019
|
+
if (text) callbacks.onText(text);
|
|
1020
|
+
callbacks.onEnd({ stopReason: "end_turn" });
|
|
1021
|
+
} catch (err) {
|
|
1022
|
+
callbacks.onError(err instanceof Error ? err : new Error(String(err)));
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
buildCombinedPrompt(options) {
|
|
1026
|
+
const streamOpts = options;
|
|
1027
|
+
const hasHistory = streamOpts.messages && streamOpts.messages.length > 0;
|
|
1028
|
+
let combined = "";
|
|
1029
|
+
combined += "[[System]]\n" + options.system + "\n\n";
|
|
1030
|
+
if (hasHistory) {
|
|
1031
|
+
for (const msg of streamOpts.messages) {
|
|
1032
|
+
combined += `[[${msg.role === "user" ? "User" : "Assistant"}]]
|
|
1033
|
+
${msg.content}
|
|
1034
|
+
|
|
1035
|
+
`;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
combined += "[[User]]\n" + options.prompt;
|
|
1039
|
+
return combined;
|
|
1040
|
+
}
|
|
1041
|
+
runClaudePrint(combinedPrompt) {
|
|
1042
|
+
return new Promise((resolve2, reject) => {
|
|
1043
|
+
const child = spawn2(CLAUDE_CLI_BIN, ["-p", combinedPrompt], {
|
|
1044
|
+
cwd: process.cwd(),
|
|
1045
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
1046
|
+
env: process.env
|
|
1047
|
+
});
|
|
1048
|
+
const chunks = [];
|
|
1049
|
+
child.stdout.on("data", (chunk) => chunks.push(chunk));
|
|
1050
|
+
child.on("error", (err) => {
|
|
1051
|
+
clearTimeout(timer);
|
|
1052
|
+
reject(err);
|
|
1053
|
+
});
|
|
1054
|
+
child.on("close", (code, signal) => {
|
|
1055
|
+
clearTimeout(timer);
|
|
1056
|
+
const stdout = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1057
|
+
if (code === 0) {
|
|
1058
|
+
resolve2(stdout);
|
|
1059
|
+
} else {
|
|
1060
|
+
const msg = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
|
|
1061
|
+
reject(new Error(stdout ? `${msg}. Output: ${stdout.slice(0, 200)}` : msg));
|
|
1062
|
+
}
|
|
1063
|
+
});
|
|
1064
|
+
const timer = setTimeout(() => {
|
|
1065
|
+
child.kill("SIGTERM");
|
|
1066
|
+
reject(
|
|
1067
|
+
new Error(
|
|
1068
|
+
`Claude CLI timed out after ${this.timeoutMs / 1e3}s. Set CALIBER_CLAUDE_CLI_TIMEOUT_MS to increase.`
|
|
1069
|
+
)
|
|
1070
|
+
);
|
|
1071
|
+
}, this.timeoutMs);
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
function isClaudeCliAvailable() {
|
|
1076
|
+
try {
|
|
1077
|
+
const cmd = process.platform === "win32" ? `where ${CLAUDE_CLI_BIN}` : `which ${CLAUDE_CLI_BIN}`;
|
|
1078
|
+
execSync3(cmd, { stdio: "ignore" });
|
|
1079
|
+
return true;
|
|
1080
|
+
} catch {
|
|
1081
|
+
return false;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
820
1085
|
// src/llm/utils.ts
|
|
821
1086
|
function extractJson(text) {
|
|
822
1087
|
const startIdx = text.search(/[\[{]/);
|
|
@@ -877,6 +1142,22 @@ function createProvider(config) {
|
|
|
877
1142
|
return new VertexProvider(config);
|
|
878
1143
|
case "openai":
|
|
879
1144
|
return new OpenAICompatProvider(config);
|
|
1145
|
+
case "cursor": {
|
|
1146
|
+
if (!isCursorAgentAvailable()) {
|
|
1147
|
+
throw new Error(
|
|
1148
|
+
"Cursor provider requires the Cursor Agent CLI. Install it from https://cursor.com/install then run `agent login`. Alternatively set ANTHROPIC_API_KEY or another provider."
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
return new CursorAcpProvider(config);
|
|
1152
|
+
}
|
|
1153
|
+
case "claude-cli": {
|
|
1154
|
+
if (!isClaudeCliAvailable()) {
|
|
1155
|
+
throw new Error(
|
|
1156
|
+
"Claude Code provider requires the Claude Code CLI. Install it from https://claude.ai/install (or run `claude` once and log in). Alternatively set ANTHROPIC_API_KEY or choose another provider."
|
|
1157
|
+
);
|
|
1158
|
+
}
|
|
1159
|
+
return new ClaudeCliProvider(config);
|
|
1160
|
+
}
|
|
880
1161
|
default:
|
|
881
1162
|
throw new Error(`Unknown provider: ${config.provider}`);
|
|
882
1163
|
}
|
|
@@ -886,7 +1167,7 @@ function getProvider() {
|
|
|
886
1167
|
const config = loadConfig();
|
|
887
1168
|
if (!config) {
|
|
888
1169
|
throw new Error(
|
|
889
|
-
"No LLM provider configured. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or VERTEX_PROJECT_ID
|
|
1170
|
+
"No LLM provider configured. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or VERTEX_PROJECT_ID; or run `caliber config` and choose Cursor or Claude Code; or set CALIBER_USE_CURSOR_SEAT=1 / CALIBER_USE_CLAUDE_CLI=1."
|
|
890
1171
|
);
|
|
891
1172
|
}
|
|
892
1173
|
cachedConfig = config;
|
|
@@ -1265,9 +1546,9 @@ function isTransientError2(error) {
|
|
|
1265
1546
|
const msg = error.message.toLowerCase();
|
|
1266
1547
|
return TRANSIENT_ERRORS.some((e) => msg.includes(e.toLowerCase()));
|
|
1267
1548
|
}
|
|
1268
|
-
async function generateSetup(fingerprint, targetAgent, prompt, callbacks) {
|
|
1549
|
+
async function generateSetup(fingerprint, targetAgent, prompt, callbacks, failingChecks, currentScore) {
|
|
1269
1550
|
const provider = getProvider();
|
|
1270
|
-
const userMessage = buildGeneratePrompt(fingerprint, targetAgent, prompt);
|
|
1551
|
+
const userMessage = buildGeneratePrompt(fingerprint, targetAgent, prompt, failingChecks, currentScore);
|
|
1271
1552
|
let attempt = 0;
|
|
1272
1553
|
const attemptGeneration = async () => {
|
|
1273
1554
|
attempt++;
|
|
@@ -1367,7 +1648,7 @@ var LIMITS = {
|
|
|
1367
1648
|
SKILLS_MAX: 10,
|
|
1368
1649
|
SKILL_CHARS: 3e3,
|
|
1369
1650
|
RULES_MAX: 10,
|
|
1370
|
-
CONFIG_FILES_MAX:
|
|
1651
|
+
CONFIG_FILES_MAX: 15,
|
|
1371
1652
|
CONFIG_FILE_CHARS: 3e3,
|
|
1372
1653
|
ROUTES_MAX: 50,
|
|
1373
1654
|
FILE_SUMMARIES_MAX: 60
|
|
@@ -1377,11 +1658,21 @@ function truncate(text, maxChars) {
|
|
|
1377
1658
|
return text.slice(0, maxChars) + `
|
|
1378
1659
|
... (truncated at ${maxChars} chars)`;
|
|
1379
1660
|
}
|
|
1380
|
-
function buildGeneratePrompt(fingerprint, targetAgent, prompt) {
|
|
1661
|
+
function buildGeneratePrompt(fingerprint, targetAgent, prompt, failingChecks, currentScore) {
|
|
1381
1662
|
const parts = [];
|
|
1382
1663
|
const existing = fingerprint.existingConfigs;
|
|
1383
1664
|
const hasExistingConfigs = !!(existing.claudeMd || existing.claudeSettings || existing.claudeSkills?.length || existing.readmeMd || existing.cursorrules || existing.cursorRules?.length);
|
|
1384
|
-
|
|
1665
|
+
const isTargetedFix = failingChecks && failingChecks.length > 0 && currentScore !== void 0 && currentScore >= 95;
|
|
1666
|
+
if (isTargetedFix) {
|
|
1667
|
+
parts.push(`TARGETED FIX MODE \u2014 current score: ${currentScore}/100, target: ${targetAgent}`);
|
|
1668
|
+
parts.push(`
|
|
1669
|
+
The existing config is already high quality. ONLY fix these specific failing checks:`);
|
|
1670
|
+
for (const check of failingChecks) {
|
|
1671
|
+
parts.push(`- ${check.name}${check.suggestion ? `: ${check.suggestion}` : ""}`);
|
|
1672
|
+
}
|
|
1673
|
+
parts.push(`
|
|
1674
|
+
IMPORTANT: Return the existing CLAUDE.md and skills with MINIMAL changes \u2014 only the edits needed to fix the above checks. Do NOT rewrite, restructure, rephrase, or make cosmetic changes. Preserve the existing content as-is except for targeted fixes.`);
|
|
1675
|
+
} else if (hasExistingConfigs) {
|
|
1385
1676
|
parts.push(`Audit and improve the existing coding agent configuration for target: ${targetAgent}`);
|
|
1386
1677
|
} else {
|
|
1387
1678
|
parts.push(`Generate an initial coding agent configuration for target: ${targetAgent}`);
|
|
@@ -1818,10 +2109,10 @@ function cleanupStaging() {
|
|
|
1818
2109
|
}
|
|
1819
2110
|
|
|
1820
2111
|
// src/utils/editor.ts
|
|
1821
|
-
import { execSync as
|
|
2112
|
+
import { execSync as execSync4, spawn as spawn3 } from "child_process";
|
|
1822
2113
|
function commandExists(cmd) {
|
|
1823
2114
|
try {
|
|
1824
|
-
|
|
2115
|
+
execSync4(`which ${cmd}`, { stdio: "ignore" });
|
|
1825
2116
|
return true;
|
|
1826
2117
|
} catch {
|
|
1827
2118
|
return false;
|
|
@@ -1839,12 +2130,12 @@ function openDiffsInEditor(editor, files) {
|
|
|
1839
2130
|
for (const file of files) {
|
|
1840
2131
|
try {
|
|
1841
2132
|
if (file.originalPath) {
|
|
1842
|
-
|
|
2133
|
+
spawn3(cmd, ["--diff", file.originalPath, file.proposedPath], {
|
|
1843
2134
|
stdio: "ignore",
|
|
1844
2135
|
detached: true
|
|
1845
2136
|
}).unref();
|
|
1846
2137
|
} else {
|
|
1847
|
-
|
|
2138
|
+
spawn3(cmd, [file.proposedPath], {
|
|
1848
2139
|
stdio: "ignore",
|
|
1849
2140
|
detached: true
|
|
1850
2141
|
}).unref();
|
|
@@ -1861,7 +2152,7 @@ import { createTwoFilesPatch } from "diff";
|
|
|
1861
2152
|
// src/lib/hooks.ts
|
|
1862
2153
|
import fs14 from "fs";
|
|
1863
2154
|
import path13 from "path";
|
|
1864
|
-
import { execSync as
|
|
2155
|
+
import { execSync as execSync5 } from "child_process";
|
|
1865
2156
|
var SETTINGS_PATH = path13.join(".claude", "settings.json");
|
|
1866
2157
|
var HOOK_COMMAND = "caliber refresh --quiet";
|
|
1867
2158
|
var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
|
|
@@ -1933,7 +2224,7 @@ fi
|
|
|
1933
2224
|
${PRECOMMIT_END}`;
|
|
1934
2225
|
function getGitHooksDir() {
|
|
1935
2226
|
try {
|
|
1936
|
-
const gitDir =
|
|
2227
|
+
const gitDir = execSync5("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
1937
2228
|
return path13.join(gitDir, "hooks");
|
|
1938
2229
|
} catch {
|
|
1939
2230
|
return null;
|
|
@@ -2079,7 +2370,7 @@ function removeLearningHooks() {
|
|
|
2079
2370
|
init_constants();
|
|
2080
2371
|
import fs16 from "fs";
|
|
2081
2372
|
import path15 from "path";
|
|
2082
|
-
import { execSync as
|
|
2373
|
+
import { execSync as execSync6 } from "child_process";
|
|
2083
2374
|
var STATE_FILE = path15.join(CALIBER_DIR, ".caliber-state.json");
|
|
2084
2375
|
function readState() {
|
|
2085
2376
|
try {
|
|
@@ -2097,7 +2388,7 @@ function writeState(state) {
|
|
|
2097
2388
|
}
|
|
2098
2389
|
function getCurrentHeadSha() {
|
|
2099
2390
|
try {
|
|
2100
|
-
return
|
|
2391
|
+
return execSync6("git rev-parse HEAD", {
|
|
2101
2392
|
encoding: "utf-8",
|
|
2102
2393
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2103
2394
|
}).trim();
|
|
@@ -2193,6 +2484,80 @@ var SpinnerMessages = class {
|
|
|
2193
2484
|
}
|
|
2194
2485
|
};
|
|
2195
2486
|
|
|
2487
|
+
// src/commands/interactive-provider-setup.ts
|
|
2488
|
+
import chalk2 from "chalk";
|
|
2489
|
+
import readline2 from "readline";
|
|
2490
|
+
import select from "@inquirer/select";
|
|
2491
|
+
function promptInput(question) {
|
|
2492
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
2493
|
+
return new Promise((resolve2) => {
|
|
2494
|
+
rl.question(chalk2.cyan(`${question} `), (answer) => {
|
|
2495
|
+
rl.close();
|
|
2496
|
+
resolve2(answer.trim());
|
|
2497
|
+
});
|
|
2498
|
+
});
|
|
2499
|
+
}
|
|
2500
|
+
var PROVIDER_CHOICES = [
|
|
2501
|
+
{ name: "Claude Code (use my app login \u2014 Pro/Max/Team, no API key)", value: "claude-cli" },
|
|
2502
|
+
{ name: "Cursor (use my Cursor subscription \u2014 no API key)", value: "cursor" },
|
|
2503
|
+
{ name: "Anthropic (Claude) \u2014 API key from console.anthropic.com", value: "anthropic" },
|
|
2504
|
+
{ name: "Google Vertex AI (Claude)", value: "vertex" },
|
|
2505
|
+
{ name: "OpenAI / OpenAI-compatible", value: "openai" }
|
|
2506
|
+
];
|
|
2507
|
+
async function runInteractiveProviderSetup(options) {
|
|
2508
|
+
const message = options?.selectMessage ?? "Select LLM provider";
|
|
2509
|
+
const provider = await select({
|
|
2510
|
+
message,
|
|
2511
|
+
choices: PROVIDER_CHOICES
|
|
2512
|
+
});
|
|
2513
|
+
const config = { provider, model: "" };
|
|
2514
|
+
switch (provider) {
|
|
2515
|
+
case "claude-cli": {
|
|
2516
|
+
config.model = "default";
|
|
2517
|
+
console.log(chalk2.dim(" Run `claude` once and log in with your Pro/Max/Team account if you haven't."));
|
|
2518
|
+
break;
|
|
2519
|
+
}
|
|
2520
|
+
case "cursor": {
|
|
2521
|
+
config.model = "default";
|
|
2522
|
+
console.log(chalk2.dim(" Run `agent login` if you haven't, or set CURSOR_API_KEY."));
|
|
2523
|
+
break;
|
|
2524
|
+
}
|
|
2525
|
+
case "anthropic": {
|
|
2526
|
+
console.log(chalk2.dim(" Get a key at https://console.anthropic.com (same account as Claude Pro/Team/Max)."));
|
|
2527
|
+
config.apiKey = await promptInput("Anthropic API key:");
|
|
2528
|
+
if (!config.apiKey) {
|
|
2529
|
+
console.log(chalk2.red("API key is required."));
|
|
2530
|
+
throw new Error("__exit__");
|
|
2531
|
+
}
|
|
2532
|
+
config.model = await promptInput(`Model (default: ${DEFAULT_MODELS.anthropic}):`) || DEFAULT_MODELS.anthropic;
|
|
2533
|
+
break;
|
|
2534
|
+
}
|
|
2535
|
+
case "vertex": {
|
|
2536
|
+
config.vertexProjectId = await promptInput("GCP Project ID:");
|
|
2537
|
+
if (!config.vertexProjectId) {
|
|
2538
|
+
console.log(chalk2.red("Project ID is required."));
|
|
2539
|
+
throw new Error("__exit__");
|
|
2540
|
+
}
|
|
2541
|
+
config.vertexRegion = await promptInput("Region (default: us-east5):") || "us-east5";
|
|
2542
|
+
config.vertexCredentials = await promptInput("Service account credentials JSON (or leave empty for ADC):") || void 0;
|
|
2543
|
+
config.model = await promptInput(`Model (default: ${DEFAULT_MODELS.vertex}):`) || DEFAULT_MODELS.vertex;
|
|
2544
|
+
break;
|
|
2545
|
+
}
|
|
2546
|
+
case "openai": {
|
|
2547
|
+
config.apiKey = await promptInput("API key:");
|
|
2548
|
+
if (!config.apiKey) {
|
|
2549
|
+
console.log(chalk2.red("API key is required."));
|
|
2550
|
+
throw new Error("__exit__");
|
|
2551
|
+
}
|
|
2552
|
+
config.baseUrl = await promptInput("Base URL (leave empty for OpenAI, or enter custom endpoint):") || void 0;
|
|
2553
|
+
config.model = await promptInput(`Model (default: ${DEFAULT_MODELS.openai}):`) || DEFAULT_MODELS.openai;
|
|
2554
|
+
break;
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
writeConfigFile(config);
|
|
2558
|
+
return config;
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2196
2561
|
// src/scoring/index.ts
|
|
2197
2562
|
import { existsSync as existsSync8 } from "fs";
|
|
2198
2563
|
import { join as join7 } from "path";
|
|
@@ -3107,7 +3472,7 @@ function checkFreshness(dir) {
|
|
|
3107
3472
|
|
|
3108
3473
|
// src/scoring/checks/bonus.ts
|
|
3109
3474
|
import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "fs";
|
|
3110
|
-
import { execSync as
|
|
3475
|
+
import { execSync as execSync7 } from "child_process";
|
|
3111
3476
|
import { join as join6 } from "path";
|
|
3112
3477
|
function readFileOrNull5(path23) {
|
|
3113
3478
|
try {
|
|
@@ -3118,7 +3483,7 @@ function readFileOrNull5(path23) {
|
|
|
3118
3483
|
}
|
|
3119
3484
|
function hasPreCommitHook(dir) {
|
|
3120
3485
|
try {
|
|
3121
|
-
const gitDir =
|
|
3486
|
+
const gitDir = execSync7("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
3122
3487
|
const hookPath = join6(gitDir, "hooks", "pre-commit");
|
|
3123
3488
|
const content = readFileOrNull5(hookPath);
|
|
3124
3489
|
return content ? content.includes("caliber") : false;
|
|
@@ -3263,7 +3628,7 @@ function computeLocalScore(dir, targetAgent) {
|
|
|
3263
3628
|
}
|
|
3264
3629
|
|
|
3265
3630
|
// src/scoring/display.ts
|
|
3266
|
-
import
|
|
3631
|
+
import chalk3 from "chalk";
|
|
3267
3632
|
var CATEGORY_LABELS = {
|
|
3268
3633
|
existence: "FILES & SETUP",
|
|
3269
3634
|
quality: "QUALITY",
|
|
@@ -3276,31 +3641,31 @@ var CATEGORY_ORDER = ["existence", "quality", "coverage", "accuracy", "freshness
|
|
|
3276
3641
|
function gradeColor(grade) {
|
|
3277
3642
|
switch (grade) {
|
|
3278
3643
|
case "A":
|
|
3279
|
-
return
|
|
3644
|
+
return chalk3.green;
|
|
3280
3645
|
case "B":
|
|
3281
|
-
return
|
|
3646
|
+
return chalk3.greenBright;
|
|
3282
3647
|
case "C":
|
|
3283
|
-
return
|
|
3648
|
+
return chalk3.yellow;
|
|
3284
3649
|
case "D":
|
|
3285
|
-
return
|
|
3650
|
+
return chalk3.hex("#f97316");
|
|
3286
3651
|
case "F":
|
|
3287
|
-
return
|
|
3652
|
+
return chalk3.red;
|
|
3288
3653
|
default:
|
|
3289
|
-
return
|
|
3654
|
+
return chalk3.white;
|
|
3290
3655
|
}
|
|
3291
3656
|
}
|
|
3292
3657
|
function progressBar(score, max, width = 40) {
|
|
3293
3658
|
const filled = Math.round(score / max * width);
|
|
3294
3659
|
const empty = width - filled;
|
|
3295
|
-
const bar =
|
|
3660
|
+
const bar = chalk3.hex("#f97316")("\u2593".repeat(filled)) + chalk3.gray("\u2591".repeat(empty));
|
|
3296
3661
|
return bar;
|
|
3297
3662
|
}
|
|
3298
3663
|
function formatCheck(check) {
|
|
3299
|
-
const icon = check.passed ?
|
|
3300
|
-
const points = check.passed ?
|
|
3301
|
-
const name = check.passed ?
|
|
3302
|
-
const detail = check.detail ?
|
|
3303
|
-
const suggestion = !check.passed && check.suggestion ?
|
|
3664
|
+
const icon = check.passed ? chalk3.green("\u2713") : check.earnedPoints < 0 ? chalk3.red("\u2717") : chalk3.gray("\u2717");
|
|
3665
|
+
const points = check.passed ? chalk3.green(`+${check.earnedPoints}`.padStart(4)) : check.earnedPoints < 0 ? chalk3.red(`${check.earnedPoints}`.padStart(4)) : chalk3.gray(" \u2014");
|
|
3666
|
+
const name = check.passed ? chalk3.white(check.name) : chalk3.gray(check.name);
|
|
3667
|
+
const detail = check.detail ? chalk3.gray(` (${check.detail})`) : "";
|
|
3668
|
+
const suggestion = !check.passed && check.suggestion ? chalk3.gray(`
|
|
3304
3669
|
\u2192 ${check.suggestion}`) : "";
|
|
3305
3670
|
return ` ${icon} ${name.padEnd(38)}${points}${detail}${suggestion}`;
|
|
3306
3671
|
}
|
|
@@ -3308,21 +3673,21 @@ function displayScore(result) {
|
|
|
3308
3673
|
const gc = gradeColor(result.grade);
|
|
3309
3674
|
const agentLabel = result.targetAgent === "both" ? "Claude Code + Cursor" : result.targetAgent === "claude" ? "Claude Code" : "Cursor";
|
|
3310
3675
|
console.log("");
|
|
3311
|
-
console.log(
|
|
3312
|
-
console.log(
|
|
3676
|
+
console.log(chalk3.gray(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
|
|
3677
|
+
console.log(chalk3.gray(" \u2502") + " " + chalk3.gray("\u2502"));
|
|
3313
3678
|
console.log(
|
|
3314
|
-
|
|
3679
|
+
chalk3.gray(" \u2502") + " Agent Config Score" + gc(` ${String(result.score).padStart(3)} / ${result.maxScore}`) + " Grade " + gc(result.grade) + " " + chalk3.gray("\u2502")
|
|
3315
3680
|
);
|
|
3316
|
-
console.log(
|
|
3317
|
-
console.log(
|
|
3318
|
-
console.log(
|
|
3319
|
-
console.log(
|
|
3681
|
+
console.log(chalk3.gray(" \u2502") + ` ${progressBar(result.score, result.maxScore)} ` + chalk3.gray("\u2502"));
|
|
3682
|
+
console.log(chalk3.gray(" \u2502") + chalk3.dim(` Target: ${agentLabel}`) + " ".repeat(Math.max(1, 40 - agentLabel.length)) + chalk3.gray("\u2502"));
|
|
3683
|
+
console.log(chalk3.gray(" \u2502") + " " + chalk3.gray("\u2502"));
|
|
3684
|
+
console.log(chalk3.gray(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
|
|
3320
3685
|
console.log("");
|
|
3321
3686
|
for (const category of CATEGORY_ORDER) {
|
|
3322
3687
|
const summary = result.categories[category];
|
|
3323
3688
|
const categoryChecks = result.checks.filter((c) => c.category === category);
|
|
3324
3689
|
console.log(
|
|
3325
|
-
|
|
3690
|
+
chalk3.gray(` ${CATEGORY_LABELS[category]}`) + chalk3.gray(" ".repeat(Math.max(1, 45 - CATEGORY_LABELS[category].length))) + chalk3.white(`${summary.earned}`) + chalk3.gray(` / ${summary.max}`)
|
|
3326
3691
|
);
|
|
3327
3692
|
for (const check of categoryChecks) {
|
|
3328
3693
|
console.log(formatCheck(check));
|
|
@@ -3333,7 +3698,7 @@ function displayScore(result) {
|
|
|
3333
3698
|
function displayScoreDelta(before, after) {
|
|
3334
3699
|
const delta = after.score - before.score;
|
|
3335
3700
|
const deltaStr = delta >= 0 ? `+${delta}` : `${delta}`;
|
|
3336
|
-
const deltaColor = delta >= 0 ?
|
|
3701
|
+
const deltaColor = delta >= 0 ? chalk3.green : chalk3.red;
|
|
3337
3702
|
const beforeGc = gradeColor(before.grade);
|
|
3338
3703
|
const afterGc = gradeColor(after.grade);
|
|
3339
3704
|
const BOX_INNER = 51;
|
|
@@ -3344,30 +3709,30 @@ function displayScoreDelta(before, after) {
|
|
|
3344
3709
|
const totalPad = BOX_INNER - contentLen;
|
|
3345
3710
|
const pad1 = Math.max(2, Math.ceil(totalPad / 2));
|
|
3346
3711
|
const pad2 = Math.max(1, totalPad - pad1);
|
|
3347
|
-
const scoreLineFormatted = " Score: " + beforeGc(`${before.score}`) +
|
|
3712
|
+
const scoreLineFormatted = " Score: " + beforeGc(`${before.score}`) + chalk3.gray(" \u2192 ") + afterGc(`${after.score}`) + " ".repeat(pad1) + deltaColor(deltaPart) + " ".repeat(pad2) + beforeGc(before.grade) + chalk3.gray(" \u2192 ") + afterGc(after.grade);
|
|
3348
3713
|
const visibleLen = 3 + scorePart.length + pad1 + deltaPart.length + pad2 + gradePart.length;
|
|
3349
3714
|
const trailingPad = Math.max(0, BOX_INNER - visibleLen);
|
|
3350
3715
|
const barWidth = Math.floor((BOX_INNER - 12) / 2);
|
|
3351
|
-
const barLine = ` ${progressBar(before.score, before.maxScore, barWidth)}` +
|
|
3716
|
+
const barLine = ` ${progressBar(before.score, before.maxScore, barWidth)}` + chalk3.gray(" \u2192 ") + progressBar(after.score, after.maxScore, barWidth) + " ";
|
|
3352
3717
|
console.log("");
|
|
3353
|
-
console.log(
|
|
3354
|
-
console.log(
|
|
3355
|
-
console.log(
|
|
3356
|
-
console.log(
|
|
3357
|
-
console.log(
|
|
3358
|
-
console.log(
|
|
3718
|
+
console.log(chalk3.gray(" \u256D" + "\u2500".repeat(BOX_INNER) + "\u256E"));
|
|
3719
|
+
console.log(chalk3.gray(" \u2502") + " ".repeat(BOX_INNER) + chalk3.gray("\u2502"));
|
|
3720
|
+
console.log(chalk3.gray(" \u2502") + scoreLineFormatted + " ".repeat(trailingPad) + chalk3.gray("\u2502"));
|
|
3721
|
+
console.log(chalk3.gray(" \u2502") + barLine + chalk3.gray("\u2502"));
|
|
3722
|
+
console.log(chalk3.gray(" \u2502") + " ".repeat(BOX_INNER) + chalk3.gray("\u2502"));
|
|
3723
|
+
console.log(chalk3.gray(" \u2570" + "\u2500".repeat(BOX_INNER) + "\u256F"));
|
|
3359
3724
|
console.log("");
|
|
3360
3725
|
const improved = after.checks.filter((ac) => {
|
|
3361
3726
|
const bc = before.checks.find((b) => b.id === ac.id);
|
|
3362
3727
|
return bc && ac.earnedPoints > bc.earnedPoints;
|
|
3363
3728
|
});
|
|
3364
3729
|
if (improved.length > 0) {
|
|
3365
|
-
console.log(
|
|
3730
|
+
console.log(chalk3.gray(" What improved:"));
|
|
3366
3731
|
for (const check of improved) {
|
|
3367
3732
|
const bc = before.checks.find((b) => b.id === check.id);
|
|
3368
3733
|
const gain = check.earnedPoints - bc.earnedPoints;
|
|
3369
3734
|
console.log(
|
|
3370
|
-
|
|
3735
|
+
chalk3.green(" +") + chalk3.white(` ${check.name.padEnd(50)}`) + chalk3.green(`+${gain}`)
|
|
3371
3736
|
);
|
|
3372
3737
|
}
|
|
3373
3738
|
console.log("");
|
|
@@ -3376,7 +3741,7 @@ function displayScoreDelta(before, after) {
|
|
|
3376
3741
|
|
|
3377
3742
|
// src/commands/init.ts
|
|
3378
3743
|
async function initCommand(options) {
|
|
3379
|
-
console.log(
|
|
3744
|
+
console.log(chalk4.bold.hex("#6366f1")(`
|
|
3380
3745
|
\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
3381
3746
|
\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
|
|
3382
3747
|
\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
|
|
@@ -3384,55 +3749,84 @@ async function initCommand(options) {
|
|
|
3384
3749
|
\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551
|
|
3385
3750
|
\u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
|
|
3386
3751
|
`));
|
|
3387
|
-
console.log(
|
|
3388
|
-
console.log(
|
|
3389
|
-
console.log(
|
|
3390
|
-
console.log(
|
|
3391
|
-
console.log(
|
|
3392
|
-
console.log(
|
|
3393
|
-
console.log(
|
|
3394
|
-
console.log(
|
|
3395
|
-
console.log(
|
|
3396
|
-
console.log(
|
|
3397
|
-
console.log(
|
|
3398
|
-
console.log(
|
|
3399
|
-
|
|
3752
|
+
console.log(chalk4.dim(" Configure your coding agent environment\n"));
|
|
3753
|
+
console.log(chalk4.bold(" What is Caliber?\n"));
|
|
3754
|
+
console.log(chalk4.dim(" Caliber audits your AI agent configurations and suggests targeted"));
|
|
3755
|
+
console.log(chalk4.dim(" improvements. It analyzes CLAUDE.md, .cursorrules, and skills"));
|
|
3756
|
+
console.log(chalk4.dim(" against your actual codebase \u2014 keeping what works, fixing"));
|
|
3757
|
+
console.log(chalk4.dim(" what's stale, and adding what's missing.\n"));
|
|
3758
|
+
console.log(chalk4.bold(" How it works:\n"));
|
|
3759
|
+
console.log(chalk4.dim(" 1. Scan Analyze your code, dependencies, and existing configs"));
|
|
3760
|
+
console.log(chalk4.dim(" 2. Generate AI creates or improves config files for your project"));
|
|
3761
|
+
console.log(chalk4.dim(" 3. Review You accept, refine, or decline the proposed changes"));
|
|
3762
|
+
console.log(chalk4.dim(" 4. Apply Config files are written with backups\n"));
|
|
3763
|
+
console.log(chalk4.hex("#6366f1").bold(" Step 1/4 \u2014 How do you want to use Caliber?\n"));
|
|
3764
|
+
let config = loadConfig();
|
|
3400
3765
|
if (!config) {
|
|
3401
|
-
console.log(
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3766
|
+
console.log(chalk4.dim(" No LLM provider set yet. Choose how to run Caliber:\n"));
|
|
3767
|
+
try {
|
|
3768
|
+
await runInteractiveProviderSetup({
|
|
3769
|
+
selectMessage: "How do you want to use Caliber? (choose LLM provider)"
|
|
3770
|
+
});
|
|
3771
|
+
} catch (err) {
|
|
3772
|
+
if (err.message === "__exit__") throw err;
|
|
3773
|
+
throw err;
|
|
3774
|
+
}
|
|
3775
|
+
config = loadConfig();
|
|
3776
|
+
if (!config) {
|
|
3777
|
+
console.log(chalk4.red(" Setup was cancelled or failed.\n"));
|
|
3778
|
+
throw new Error("__exit__");
|
|
3779
|
+
}
|
|
3780
|
+
console.log(chalk4.green(" \u2713 Provider saved. Continuing with init.\n"));
|
|
3408
3781
|
}
|
|
3409
|
-
console.log(
|
|
3782
|
+
console.log(chalk4.dim(` Provider: ${config.provider} | Model: ${config.model}
|
|
3410
3783
|
`));
|
|
3411
|
-
console.log(
|
|
3412
|
-
console.log(
|
|
3784
|
+
console.log(chalk4.hex("#6366f1").bold(" Step 2/4 \u2014 Scan project\n"));
|
|
3785
|
+
console.log(chalk4.dim(" Detecting languages, dependencies, file structure, and existing configs.\n"));
|
|
3413
3786
|
const spinner = ora("Analyzing project...").start();
|
|
3414
3787
|
const fingerprint = collectFingerprint(process.cwd());
|
|
3415
3788
|
await enrichFingerprintWithLLM(fingerprint, process.cwd());
|
|
3416
3789
|
spinner.succeed("Project analyzed");
|
|
3417
|
-
console.log(
|
|
3418
|
-
console.log(
|
|
3790
|
+
console.log(chalk4.dim(` Languages: ${fingerprint.languages.join(", ") || "none detected"}`));
|
|
3791
|
+
console.log(chalk4.dim(` Files: ${fingerprint.fileTree.length} found
|
|
3419
3792
|
`));
|
|
3420
3793
|
const targetAgent = options.agent || await promptAgent();
|
|
3421
3794
|
const baselineScore = computeLocalScore(process.cwd(), targetAgent);
|
|
3795
|
+
const hasExistingConfig = !!(fingerprint.existingConfigs.claudeMd || fingerprint.existingConfigs.claudeSettings || fingerprint.existingConfigs.claudeSkills?.length || fingerprint.existingConfigs.cursorrules || fingerprint.existingConfigs.cursorRules?.length);
|
|
3796
|
+
if (hasExistingConfig && baselineScore.score === 100) {
|
|
3797
|
+
console.log(chalk4.hex("#6366f1").bold(" Step 3/4 \u2014 Score check\n"));
|
|
3798
|
+
displayScore(baselineScore);
|
|
3799
|
+
console.log(chalk4.bold.green(" Your setup is already optimal \u2014 nothing to change.\n"));
|
|
3800
|
+
console.log(chalk4.dim(" Run `caliber init --force` to regenerate anyway.\n"));
|
|
3801
|
+
if (!options.force) return;
|
|
3802
|
+
}
|
|
3422
3803
|
const isEmpty = fingerprint.fileTree.length < 3;
|
|
3423
3804
|
if (isEmpty) {
|
|
3424
|
-
fingerprint.description = await
|
|
3425
|
-
}
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3805
|
+
fingerprint.description = await promptInput2("What will you build in this project?");
|
|
3806
|
+
}
|
|
3807
|
+
let failingChecks;
|
|
3808
|
+
let currentScore;
|
|
3809
|
+
if (hasExistingConfig && baselineScore.score >= 95 && !options.force) {
|
|
3810
|
+
failingChecks = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0).map((c) => ({ name: c.name, suggestion: c.suggestion }));
|
|
3811
|
+
currentScore = baselineScore.score;
|
|
3812
|
+
if (failingChecks.length > 0) {
|
|
3813
|
+
console.log(chalk4.hex("#6366f1").bold(" Step 3/4 \u2014 Targeted fixes\n"));
|
|
3814
|
+
console.log(chalk4.dim(` Score is ${baselineScore.score}/100 \u2014 only fixing ${failingChecks.length} remaining issue${failingChecks.length === 1 ? "" : "s"}:
|
|
3815
|
+
`));
|
|
3816
|
+
for (const check of failingChecks) {
|
|
3817
|
+
console.log(chalk4.dim(` \u2022 ${check.name}`));
|
|
3818
|
+
}
|
|
3819
|
+
console.log("");
|
|
3820
|
+
}
|
|
3821
|
+
} else if (hasExistingConfig) {
|
|
3822
|
+
console.log(chalk4.hex("#6366f1").bold(" Step 3/4 \u2014 Auditing your configs\n"));
|
|
3823
|
+
console.log(chalk4.dim(" AI is reviewing your existing configs against your codebase"));
|
|
3824
|
+
console.log(chalk4.dim(" and suggesting improvements.\n"));
|
|
3431
3825
|
} else {
|
|
3432
|
-
console.log(
|
|
3433
|
-
console.log(
|
|
3826
|
+
console.log(chalk4.hex("#6366f1").bold(" Step 3/4 \u2014 Generating configs\n"));
|
|
3827
|
+
console.log(chalk4.dim(" AI is creating agent config files tailored to your project.\n"));
|
|
3434
3828
|
}
|
|
3435
|
-
console.log(
|
|
3829
|
+
console.log(chalk4.dim(" This usually takes 1\u20133 minutes.\n"));
|
|
3436
3830
|
const genStartTime = Date.now();
|
|
3437
3831
|
const genSpinner = ora("Generating setup...").start();
|
|
3438
3832
|
const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES, { showElapsedTime: true });
|
|
@@ -3455,7 +3849,9 @@ async function initCommand(options) {
|
|
|
3455
3849
|
genMessages.stop();
|
|
3456
3850
|
genSpinner.fail(`Generation error: ${error}`);
|
|
3457
3851
|
}
|
|
3458
|
-
}
|
|
3852
|
+
},
|
|
3853
|
+
failingChecks,
|
|
3854
|
+
currentScore
|
|
3459
3855
|
);
|
|
3460
3856
|
if (!generatedSetup) {
|
|
3461
3857
|
generatedSetup = result.setup;
|
|
@@ -3471,8 +3867,8 @@ async function initCommand(options) {
|
|
|
3471
3867
|
if (!generatedSetup) {
|
|
3472
3868
|
genSpinner.fail("Failed to generate setup.");
|
|
3473
3869
|
if (rawOutput) {
|
|
3474
|
-
console.log(
|
|
3475
|
-
console.log(
|
|
3870
|
+
console.log(chalk4.dim("\nRaw LLM output (JSON parse failed):"));
|
|
3871
|
+
console.log(chalk4.dim(rawOutput.slice(0, 500)));
|
|
3476
3872
|
}
|
|
3477
3873
|
throw new Error("__exit__");
|
|
3478
3874
|
}
|
|
@@ -3480,12 +3876,12 @@ async function initCommand(options) {
|
|
|
3480
3876
|
const mins = Math.floor(elapsedMs / 6e4);
|
|
3481
3877
|
const secs = Math.floor(elapsedMs % 6e4 / 1e3);
|
|
3482
3878
|
const timeStr = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
|
|
3483
|
-
genSpinner.succeed(`Setup generated ${
|
|
3879
|
+
genSpinner.succeed(`Setup generated ${chalk4.dim(`in ${timeStr}`)}`);
|
|
3484
3880
|
printSetupSummary(generatedSetup);
|
|
3485
|
-
console.log(
|
|
3881
|
+
console.log(chalk4.hex("#6366f1").bold(" Step 4/4 \u2014 Review\n"));
|
|
3486
3882
|
const setupFiles = collectSetupFiles(generatedSetup);
|
|
3487
3883
|
const staged = stageFiles(setupFiles, process.cwd());
|
|
3488
|
-
console.log(
|
|
3884
|
+
console.log(chalk4.dim(` ${chalk4.green(`${staged.newFiles} new`)} / ${chalk4.yellow(`${staged.modifiedFiles} modified`)} file${staged.newFiles + staged.modifiedFiles !== 1 ? "s" : ""}
|
|
3489
3885
|
`));
|
|
3490
3886
|
const wantsReview = await promptWantsReview();
|
|
3491
3887
|
if (wantsReview) {
|
|
@@ -3497,12 +3893,12 @@ async function initCommand(options) {
|
|
|
3497
3893
|
generatedSetup = await refineLoop(generatedSetup, targetAgent);
|
|
3498
3894
|
if (!generatedSetup) {
|
|
3499
3895
|
cleanupStaging();
|
|
3500
|
-
console.log(
|
|
3896
|
+
console.log(chalk4.dim("Refinement cancelled. No files were modified."));
|
|
3501
3897
|
return;
|
|
3502
3898
|
}
|
|
3503
3899
|
const updatedFiles = collectSetupFiles(generatedSetup);
|
|
3504
3900
|
const restaged = stageFiles(updatedFiles, process.cwd());
|
|
3505
|
-
console.log(
|
|
3901
|
+
console.log(chalk4.dim(` ${chalk4.green(`${restaged.newFiles} new`)} / ${chalk4.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
|
|
3506
3902
|
`));
|
|
3507
3903
|
printSetupSummary(generatedSetup);
|
|
3508
3904
|
const wantsReviewAgain = await promptWantsReview();
|
|
@@ -3514,11 +3910,11 @@ async function initCommand(options) {
|
|
|
3514
3910
|
}
|
|
3515
3911
|
cleanupStaging();
|
|
3516
3912
|
if (action === "decline") {
|
|
3517
|
-
console.log(
|
|
3913
|
+
console.log(chalk4.dim("Setup declined. No files were modified."));
|
|
3518
3914
|
return;
|
|
3519
3915
|
}
|
|
3520
3916
|
if (options.dryRun) {
|
|
3521
|
-
console.log(
|
|
3917
|
+
console.log(chalk4.yellow("\n[Dry run] Would write the following files:"));
|
|
3522
3918
|
console.log(JSON.stringify(generatedSetup, null, 2));
|
|
3523
3919
|
return;
|
|
3524
3920
|
}
|
|
@@ -3526,29 +3922,29 @@ async function initCommand(options) {
|
|
|
3526
3922
|
try {
|
|
3527
3923
|
const result = writeSetup(generatedSetup);
|
|
3528
3924
|
writeSpinner.succeed("Config files written");
|
|
3529
|
-
console.log(
|
|
3925
|
+
console.log(chalk4.bold("\nFiles created/updated:"));
|
|
3530
3926
|
for (const file of result.written) {
|
|
3531
|
-
console.log(` ${
|
|
3927
|
+
console.log(` ${chalk4.green("\u2713")} ${file}`);
|
|
3532
3928
|
}
|
|
3533
3929
|
if (result.deleted.length > 0) {
|
|
3534
|
-
console.log(
|
|
3930
|
+
console.log(chalk4.bold("\nFiles removed:"));
|
|
3535
3931
|
for (const file of result.deleted) {
|
|
3536
|
-
console.log(` ${
|
|
3932
|
+
console.log(` ${chalk4.red("\u2717")} ${file}`);
|
|
3537
3933
|
}
|
|
3538
3934
|
}
|
|
3539
3935
|
if (result.backupDir) {
|
|
3540
|
-
console.log(
|
|
3936
|
+
console.log(chalk4.dim(`
|
|
3541
3937
|
Backups saved to ${result.backupDir}`));
|
|
3542
3938
|
}
|
|
3543
3939
|
} catch (err) {
|
|
3544
3940
|
writeSpinner.fail("Failed to write files");
|
|
3545
|
-
console.error(
|
|
3941
|
+
console.error(chalk4.red(err instanceof Error ? err.message : "Unknown error"));
|
|
3546
3942
|
throw new Error("__exit__");
|
|
3547
3943
|
}
|
|
3548
3944
|
if (!fs17.existsSync("AGENTS.md")) {
|
|
3549
3945
|
const agentsContent = "# AGENTS.md\n\nThis project uses AI coding agents. See CLAUDE.md for Claude Code configuration and .cursor/rules/ for Cursor rules.\n";
|
|
3550
3946
|
fs17.writeFileSync("AGENTS.md", agentsContent);
|
|
3551
|
-
console.log(` ${
|
|
3947
|
+
console.log(` ${chalk4.green("\u2713")} AGENTS.md`);
|
|
3552
3948
|
}
|
|
3553
3949
|
ensurePermissions();
|
|
3554
3950
|
const sha = getCurrentHeadSha();
|
|
@@ -3561,45 +3957,45 @@ async function initCommand(options) {
|
|
|
3561
3957
|
if (hookChoice === "claude" || hookChoice === "both") {
|
|
3562
3958
|
const hookResult = installHook();
|
|
3563
3959
|
if (hookResult.installed) {
|
|
3564
|
-
console.log(` ${
|
|
3565
|
-
console.log(
|
|
3960
|
+
console.log(` ${chalk4.green("\u2713")} Claude Code hook installed \u2014 docs update on session end`);
|
|
3961
|
+
console.log(chalk4.dim(" Run `caliber hooks remove` to disable"));
|
|
3566
3962
|
} else if (hookResult.alreadyInstalled) {
|
|
3567
|
-
console.log(
|
|
3963
|
+
console.log(chalk4.dim(" Claude Code hook already installed"));
|
|
3568
3964
|
}
|
|
3569
3965
|
const learnResult = installLearningHooks();
|
|
3570
3966
|
if (learnResult.installed) {
|
|
3571
|
-
console.log(` ${
|
|
3572
|
-
console.log(
|
|
3967
|
+
console.log(` ${chalk4.green("\u2713")} Learning hooks installed \u2014 session insights captured automatically`);
|
|
3968
|
+
console.log(chalk4.dim(" Run `caliber learn remove` to disable"));
|
|
3573
3969
|
} else if (learnResult.alreadyInstalled) {
|
|
3574
|
-
console.log(
|
|
3970
|
+
console.log(chalk4.dim(" Learning hooks already installed"));
|
|
3575
3971
|
}
|
|
3576
3972
|
}
|
|
3577
3973
|
if (hookChoice === "precommit" || hookChoice === "both") {
|
|
3578
3974
|
const precommitResult = installPreCommitHook();
|
|
3579
3975
|
if (precommitResult.installed) {
|
|
3580
|
-
console.log(` ${
|
|
3581
|
-
console.log(
|
|
3976
|
+
console.log(` ${chalk4.green("\u2713")} Pre-commit hook installed \u2014 docs refresh before each commit`);
|
|
3977
|
+
console.log(chalk4.dim(" Run `caliber hooks remove-precommit` to disable"));
|
|
3582
3978
|
} else if (precommitResult.alreadyInstalled) {
|
|
3583
|
-
console.log(
|
|
3979
|
+
console.log(chalk4.dim(" Pre-commit hook already installed"));
|
|
3584
3980
|
} else {
|
|
3585
|
-
console.log(
|
|
3981
|
+
console.log(chalk4.yellow(" Could not install pre-commit hook (not a git repository?)"));
|
|
3586
3982
|
}
|
|
3587
3983
|
}
|
|
3588
3984
|
if (hookChoice === "skip") {
|
|
3589
|
-
console.log(
|
|
3985
|
+
console.log(chalk4.dim(" Skipped auto-refresh hooks. Run `caliber hooks install` later to enable."));
|
|
3590
3986
|
}
|
|
3591
3987
|
const afterScore = computeLocalScore(process.cwd(), targetAgent);
|
|
3592
3988
|
displayScoreDelta(baselineScore, afterScore);
|
|
3593
|
-
console.log(
|
|
3594
|
-
console.log(
|
|
3595
|
-
console.log(
|
|
3596
|
-
console.log(` ${
|
|
3989
|
+
console.log(chalk4.bold.green(" Setup complete! Your coding agent is now configured."));
|
|
3990
|
+
console.log(chalk4.dim(" Run `caliber undo` to revert changes.\n"));
|
|
3991
|
+
console.log(chalk4.bold(" Next steps:\n"));
|
|
3992
|
+
console.log(` ${chalk4.hex("#6366f1")("caliber undo")} Revert all changes from this run`);
|
|
3597
3993
|
console.log("");
|
|
3598
3994
|
}
|
|
3599
3995
|
async function refineLoop(currentSetup, _targetAgent) {
|
|
3600
3996
|
const history = [];
|
|
3601
3997
|
while (true) {
|
|
3602
|
-
const message = await
|
|
3998
|
+
const message = await promptInput2("\nWhat would you like to change?");
|
|
3603
3999
|
if (!message || message.toLowerCase() === "done" || message.toLowerCase() === "accept") {
|
|
3604
4000
|
return currentSetup;
|
|
3605
4001
|
}
|
|
@@ -3621,24 +4017,24 @@ async function refineLoop(currentSetup, _targetAgent) {
|
|
|
3621
4017
|
history.push({ role: "assistant", content: JSON.stringify(refined) });
|
|
3622
4018
|
refineSpinner.succeed("Setup updated");
|
|
3623
4019
|
printSetupSummary(refined);
|
|
3624
|
-
console.log(
|
|
4020
|
+
console.log(chalk4.dim('Type "done" to accept, or describe more changes.'));
|
|
3625
4021
|
} else {
|
|
3626
4022
|
refineSpinner.fail("Refinement failed \u2014 could not parse AI response.");
|
|
3627
|
-
console.log(
|
|
4023
|
+
console.log(chalk4.dim('Try rephrasing your request, or type "done" to keep the current setup.'));
|
|
3628
4024
|
}
|
|
3629
4025
|
}
|
|
3630
4026
|
}
|
|
3631
|
-
function
|
|
3632
|
-
const rl =
|
|
4027
|
+
function promptInput2(question) {
|
|
4028
|
+
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
3633
4029
|
return new Promise((resolve2) => {
|
|
3634
|
-
rl.question(
|
|
4030
|
+
rl.question(chalk4.cyan(`${question} `), (answer) => {
|
|
3635
4031
|
rl.close();
|
|
3636
4032
|
resolve2(answer.trim());
|
|
3637
4033
|
});
|
|
3638
4034
|
});
|
|
3639
4035
|
}
|
|
3640
4036
|
async function promptAgent() {
|
|
3641
|
-
return
|
|
4037
|
+
return select2({
|
|
3642
4038
|
message: "Which coding agent are you using?",
|
|
3643
4039
|
choices: [
|
|
3644
4040
|
{ name: "Claude Code", value: "claude" },
|
|
@@ -3657,13 +4053,13 @@ async function promptHookType(targetAgent) {
|
|
|
3657
4053
|
choices.push({ name: "Both (Claude Code + pre-commit)", value: "both" });
|
|
3658
4054
|
}
|
|
3659
4055
|
choices.push({ name: "Skip for now", value: "skip" });
|
|
3660
|
-
return
|
|
4056
|
+
return select2({
|
|
3661
4057
|
message: "How would you like to auto-refresh your docs?",
|
|
3662
4058
|
choices
|
|
3663
4059
|
});
|
|
3664
4060
|
}
|
|
3665
4061
|
async function promptWantsReview() {
|
|
3666
|
-
const answer = await
|
|
4062
|
+
const answer = await select2({
|
|
3667
4063
|
message: "Would you like to review the diffs before deciding?",
|
|
3668
4064
|
choices: [
|
|
3669
4065
|
{ name: "Yes, show me the diffs", value: true },
|
|
@@ -3685,7 +4081,7 @@ async function promptReviewMethod() {
|
|
|
3685
4081
|
return { name: "Terminal", value: "terminal" };
|
|
3686
4082
|
}
|
|
3687
4083
|
});
|
|
3688
|
-
return
|
|
4084
|
+
return select2({ message: "How would you like to review the changes?", choices });
|
|
3689
4085
|
}
|
|
3690
4086
|
function openReview(method, stagedFiles) {
|
|
3691
4087
|
if (method === "cursor" || method === "vscode") {
|
|
@@ -3693,7 +4089,7 @@ function openReview(method, stagedFiles) {
|
|
|
3693
4089
|
originalPath: f.originalPath,
|
|
3694
4090
|
proposedPath: f.proposedPath
|
|
3695
4091
|
})));
|
|
3696
|
-
console.log(
|
|
4092
|
+
console.log(chalk4.dim(" Diffs opened in your editor.\n"));
|
|
3697
4093
|
} else {
|
|
3698
4094
|
for (const file of stagedFiles) {
|
|
3699
4095
|
if (file.currentPath) {
|
|
@@ -3705,19 +4101,19 @@ function openReview(method, stagedFiles) {
|
|
|
3705
4101
|
if (line.startsWith("+") && !line.startsWith("+++")) added++;
|
|
3706
4102
|
if (line.startsWith("-") && !line.startsWith("---")) removed++;
|
|
3707
4103
|
}
|
|
3708
|
-
console.log(` ${
|
|
4104
|
+
console.log(` ${chalk4.yellow("~")} ${file.relativePath} ${chalk4.green(`+${added}`)} ${chalk4.red(`-${removed}`)}`);
|
|
3709
4105
|
} else {
|
|
3710
4106
|
const lines = fs17.readFileSync(file.proposedPath, "utf-8").split("\n").length;
|
|
3711
|
-
console.log(` ${
|
|
4107
|
+
console.log(` ${chalk4.green("+")} ${file.relativePath} ${chalk4.dim(`${lines} lines`)}`);
|
|
3712
4108
|
}
|
|
3713
4109
|
}
|
|
3714
4110
|
console.log("");
|
|
3715
|
-
console.log(
|
|
4111
|
+
console.log(chalk4.dim(` Files staged at .caliber/staged/ for manual inspection.
|
|
3716
4112
|
`));
|
|
3717
4113
|
}
|
|
3718
4114
|
}
|
|
3719
4115
|
async function promptReviewAction() {
|
|
3720
|
-
return
|
|
4116
|
+
return select2({
|
|
3721
4117
|
message: "What would you like to do?",
|
|
3722
4118
|
choices: [
|
|
3723
4119
|
{ name: "Accept and apply", value: "accept" },
|
|
@@ -3732,46 +4128,46 @@ function printSetupSummary(setup) {
|
|
|
3732
4128
|
const fileDescriptions = setup.fileDescriptions;
|
|
3733
4129
|
const deletions = setup.deletions;
|
|
3734
4130
|
console.log("");
|
|
3735
|
-
console.log(
|
|
4131
|
+
console.log(chalk4.bold(" Proposed changes:\n"));
|
|
3736
4132
|
const getDescription = (filePath) => {
|
|
3737
4133
|
return fileDescriptions?.[filePath];
|
|
3738
4134
|
};
|
|
3739
4135
|
if (claude) {
|
|
3740
4136
|
if (claude.claudeMd) {
|
|
3741
|
-
const icon = fs17.existsSync("CLAUDE.md") ?
|
|
4137
|
+
const icon = fs17.existsSync("CLAUDE.md") ? chalk4.yellow("~") : chalk4.green("+");
|
|
3742
4138
|
const desc = getDescription("CLAUDE.md");
|
|
3743
|
-
console.log(` ${icon} ${
|
|
3744
|
-
if (desc) console.log(
|
|
4139
|
+
console.log(` ${icon} ${chalk4.bold("CLAUDE.md")}`);
|
|
4140
|
+
if (desc) console.log(chalk4.dim(` ${desc}`));
|
|
3745
4141
|
console.log("");
|
|
3746
4142
|
}
|
|
3747
4143
|
const skills = claude.skills;
|
|
3748
4144
|
if (Array.isArray(skills) && skills.length > 0) {
|
|
3749
4145
|
for (const skill of skills) {
|
|
3750
4146
|
const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
|
|
3751
|
-
const icon = fs17.existsSync(skillPath) ?
|
|
4147
|
+
const icon = fs17.existsSync(skillPath) ? chalk4.yellow("~") : chalk4.green("+");
|
|
3752
4148
|
const desc = getDescription(skillPath);
|
|
3753
|
-
console.log(` ${icon} ${
|
|
3754
|
-
console.log(
|
|
4149
|
+
console.log(` ${icon} ${chalk4.bold(skillPath)}`);
|
|
4150
|
+
console.log(chalk4.dim(` ${desc || skill.description || skill.name}`));
|
|
3755
4151
|
console.log("");
|
|
3756
4152
|
}
|
|
3757
4153
|
}
|
|
3758
4154
|
}
|
|
3759
4155
|
if (cursor) {
|
|
3760
4156
|
if (cursor.cursorrules) {
|
|
3761
|
-
const icon = fs17.existsSync(".cursorrules") ?
|
|
4157
|
+
const icon = fs17.existsSync(".cursorrules") ? chalk4.yellow("~") : chalk4.green("+");
|
|
3762
4158
|
const desc = getDescription(".cursorrules");
|
|
3763
|
-
console.log(` ${icon} ${
|
|
3764
|
-
if (desc) console.log(
|
|
4159
|
+
console.log(` ${icon} ${chalk4.bold(".cursorrules")}`);
|
|
4160
|
+
if (desc) console.log(chalk4.dim(` ${desc}`));
|
|
3765
4161
|
console.log("");
|
|
3766
4162
|
}
|
|
3767
4163
|
const cursorSkills = cursor.skills;
|
|
3768
4164
|
if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
|
|
3769
4165
|
for (const skill of cursorSkills) {
|
|
3770
4166
|
const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
|
|
3771
|
-
const icon = fs17.existsSync(skillPath) ?
|
|
4167
|
+
const icon = fs17.existsSync(skillPath) ? chalk4.yellow("~") : chalk4.green("+");
|
|
3772
4168
|
const desc = getDescription(skillPath);
|
|
3773
|
-
console.log(` ${icon} ${
|
|
3774
|
-
console.log(
|
|
4169
|
+
console.log(` ${icon} ${chalk4.bold(skillPath)}`);
|
|
4170
|
+
console.log(chalk4.dim(` ${desc || skill.description || skill.name}`));
|
|
3775
4171
|
console.log("");
|
|
3776
4172
|
}
|
|
3777
4173
|
}
|
|
@@ -3779,14 +4175,14 @@ function printSetupSummary(setup) {
|
|
|
3779
4175
|
if (Array.isArray(rules) && rules.length > 0) {
|
|
3780
4176
|
for (const rule of rules) {
|
|
3781
4177
|
const rulePath = `.cursor/rules/${rule.filename}`;
|
|
3782
|
-
const icon = fs17.existsSync(rulePath) ?
|
|
4178
|
+
const icon = fs17.existsSync(rulePath) ? chalk4.yellow("~") : chalk4.green("+");
|
|
3783
4179
|
const desc = getDescription(rulePath);
|
|
3784
|
-
console.log(` ${icon} ${
|
|
4180
|
+
console.log(` ${icon} ${chalk4.bold(rulePath)}`);
|
|
3785
4181
|
if (desc) {
|
|
3786
|
-
console.log(
|
|
4182
|
+
console.log(chalk4.dim(` ${desc}`));
|
|
3787
4183
|
} else {
|
|
3788
4184
|
const firstLine = rule.content.split("\n").filter((l) => l.trim() && !l.trim().startsWith("#"))[0];
|
|
3789
|
-
if (firstLine) console.log(
|
|
4185
|
+
if (firstLine) console.log(chalk4.dim(` ${firstLine.trim().slice(0, 80)}`));
|
|
3790
4186
|
}
|
|
3791
4187
|
console.log("");
|
|
3792
4188
|
}
|
|
@@ -3794,12 +4190,12 @@ function printSetupSummary(setup) {
|
|
|
3794
4190
|
}
|
|
3795
4191
|
if (Array.isArray(deletions) && deletions.length > 0) {
|
|
3796
4192
|
for (const del of deletions) {
|
|
3797
|
-
console.log(` ${
|
|
3798
|
-
console.log(
|
|
4193
|
+
console.log(` ${chalk4.red("-")} ${chalk4.bold(del.filePath)}`);
|
|
4194
|
+
console.log(chalk4.dim(` ${del.reason}`));
|
|
3799
4195
|
console.log("");
|
|
3800
4196
|
}
|
|
3801
4197
|
}
|
|
3802
|
-
console.log(` ${
|
|
4198
|
+
console.log(` ${chalk4.green("+")} ${chalk4.dim("new")} ${chalk4.yellow("~")} ${chalk4.dim("modified")} ${chalk4.red("-")} ${chalk4.dim("removed")}`);
|
|
3803
4199
|
console.log("");
|
|
3804
4200
|
}
|
|
3805
4201
|
function buildSkillContent(skill) {
|
|
@@ -3865,7 +4261,7 @@ function collectSetupFiles(setup) {
|
|
|
3865
4261
|
}
|
|
3866
4262
|
|
|
3867
4263
|
// src/commands/undo.ts
|
|
3868
|
-
import
|
|
4264
|
+
import chalk5 from "chalk";
|
|
3869
4265
|
import ora2 from "ora";
|
|
3870
4266
|
function undoCommand() {
|
|
3871
4267
|
const spinner = ora2("Reverting setup...").start();
|
|
@@ -3877,26 +4273,26 @@ function undoCommand() {
|
|
|
3877
4273
|
}
|
|
3878
4274
|
spinner.succeed("Setup reverted successfully.\n");
|
|
3879
4275
|
if (restored.length > 0) {
|
|
3880
|
-
console.log(
|
|
4276
|
+
console.log(chalk5.cyan(" Restored from backup:"));
|
|
3881
4277
|
for (const file of restored) {
|
|
3882
|
-
console.log(` ${
|
|
4278
|
+
console.log(` ${chalk5.green("\u21A9")} ${file}`);
|
|
3883
4279
|
}
|
|
3884
4280
|
}
|
|
3885
4281
|
if (removed.length > 0) {
|
|
3886
|
-
console.log(
|
|
4282
|
+
console.log(chalk5.cyan(" Removed:"));
|
|
3887
4283
|
for (const file of removed) {
|
|
3888
|
-
console.log(` ${
|
|
4284
|
+
console.log(` ${chalk5.red("\u2717")} ${file}`);
|
|
3889
4285
|
}
|
|
3890
4286
|
}
|
|
3891
4287
|
console.log("");
|
|
3892
4288
|
} catch (err) {
|
|
3893
|
-
spinner.fail(
|
|
4289
|
+
spinner.fail(chalk5.red(err instanceof Error ? err.message : "Undo failed"));
|
|
3894
4290
|
throw new Error("__exit__");
|
|
3895
4291
|
}
|
|
3896
4292
|
}
|
|
3897
4293
|
|
|
3898
4294
|
// src/commands/status.ts
|
|
3899
|
-
import
|
|
4295
|
+
import chalk6 from "chalk";
|
|
3900
4296
|
import fs18 from "fs";
|
|
3901
4297
|
async function statusCommand(options) {
|
|
3902
4298
|
const config = loadConfig();
|
|
@@ -3910,39 +4306,39 @@ async function statusCommand(options) {
|
|
|
3910
4306
|
}, null, 2));
|
|
3911
4307
|
return;
|
|
3912
4308
|
}
|
|
3913
|
-
console.log(
|
|
4309
|
+
console.log(chalk6.bold("\nCaliber Status\n"));
|
|
3914
4310
|
if (config) {
|
|
3915
|
-
console.log(` LLM: ${
|
|
4311
|
+
console.log(` LLM: ${chalk6.green(config.provider)} (${config.model})`);
|
|
3916
4312
|
} else {
|
|
3917
|
-
console.log(` LLM: ${
|
|
4313
|
+
console.log(` LLM: ${chalk6.yellow("Not configured")} \u2014 run \`caliber config\``);
|
|
3918
4314
|
}
|
|
3919
4315
|
if (!manifest) {
|
|
3920
|
-
console.log(` Setup: ${
|
|
3921
|
-
console.log(
|
|
4316
|
+
console.log(` Setup: ${chalk6.dim("No setup applied")}`);
|
|
4317
|
+
console.log(chalk6.dim("\n Run `caliber init` to get started.\n"));
|
|
3922
4318
|
return;
|
|
3923
4319
|
}
|
|
3924
|
-
console.log(` Files managed: ${
|
|
4320
|
+
console.log(` Files managed: ${chalk6.cyan(manifest.entries.length.toString())}`);
|
|
3925
4321
|
for (const entry of manifest.entries) {
|
|
3926
4322
|
const exists = fs18.existsSync(entry.path);
|
|
3927
|
-
const icon = exists ?
|
|
4323
|
+
const icon = exists ? chalk6.green("\u2713") : chalk6.red("\u2717");
|
|
3928
4324
|
console.log(` ${icon} ${entry.path} (${entry.action})`);
|
|
3929
4325
|
}
|
|
3930
4326
|
console.log("");
|
|
3931
4327
|
}
|
|
3932
4328
|
|
|
3933
4329
|
// src/commands/regenerate.ts
|
|
3934
|
-
import
|
|
4330
|
+
import chalk7 from "chalk";
|
|
3935
4331
|
import ora3 from "ora";
|
|
3936
4332
|
import confirm from "@inquirer/confirm";
|
|
3937
4333
|
async function regenerateCommand(options) {
|
|
3938
4334
|
const config = loadConfig();
|
|
3939
4335
|
if (!config) {
|
|
3940
|
-
console.log(
|
|
4336
|
+
console.log(chalk7.red("No LLM provider configured. Run `caliber config` (e.g. choose Cursor) or set ANTHROPIC_API_KEY."));
|
|
3941
4337
|
throw new Error("__exit__");
|
|
3942
4338
|
}
|
|
3943
4339
|
const manifest = readManifest();
|
|
3944
4340
|
if (!manifest) {
|
|
3945
|
-
console.log(
|
|
4341
|
+
console.log(chalk7.yellow("No existing setup found. Run `caliber init` first."));
|
|
3946
4342
|
throw new Error("__exit__");
|
|
3947
4343
|
}
|
|
3948
4344
|
const spinner = ora3("Re-analyzing project...").start();
|
|
@@ -3985,26 +4381,26 @@ async function regenerateCommand(options) {
|
|
|
3985
4381
|
}
|
|
3986
4382
|
genSpinner.succeed("Setup regenerated");
|
|
3987
4383
|
if (options.dryRun) {
|
|
3988
|
-
console.log(
|
|
4384
|
+
console.log(chalk7.yellow("\n[Dry run] Would write:"));
|
|
3989
4385
|
console.log(JSON.stringify(generatedSetup, null, 2));
|
|
3990
4386
|
return;
|
|
3991
4387
|
}
|
|
3992
4388
|
const shouldApply = await confirm({ message: "Apply regenerated setup?", default: true });
|
|
3993
4389
|
if (!shouldApply) {
|
|
3994
|
-
console.log(
|
|
4390
|
+
console.log(chalk7.dim("Regeneration cancelled."));
|
|
3995
4391
|
return;
|
|
3996
4392
|
}
|
|
3997
4393
|
const writeSpinner = ora3("Updating config files...").start();
|
|
3998
4394
|
const result = writeSetup(generatedSetup);
|
|
3999
4395
|
writeSpinner.succeed("Config files updated");
|
|
4000
4396
|
for (const file of result.written) {
|
|
4001
|
-
console.log(` ${
|
|
4397
|
+
console.log(` ${chalk7.green("\u2713")} ${file}`);
|
|
4002
4398
|
}
|
|
4003
4399
|
console.log("");
|
|
4004
4400
|
}
|
|
4005
4401
|
|
|
4006
4402
|
// src/commands/recommend.ts
|
|
4007
|
-
import
|
|
4403
|
+
import chalk8 from "chalk";
|
|
4008
4404
|
import ora4 from "ora";
|
|
4009
4405
|
import { mkdirSync, readFileSync as readFileSync7, existsSync as existsSync9, writeFileSync } from "fs";
|
|
4010
4406
|
import { join as join8, dirname as dirname2 } from "path";
|
|
@@ -4187,7 +4583,7 @@ async function recommendCommand(options) {
|
|
|
4187
4583
|
...extractTopDeps()
|
|
4188
4584
|
].filter(Boolean))];
|
|
4189
4585
|
if (technologies.length === 0) {
|
|
4190
|
-
console.log(
|
|
4586
|
+
console.log(chalk8.yellow("Could not detect any languages or dependencies. Try running from a project root."));
|
|
4191
4587
|
throw new Error("__exit__");
|
|
4192
4588
|
}
|
|
4193
4589
|
const spinner = ora4("Searching for skills...").start();
|
|
@@ -4213,18 +4609,18 @@ async function interactiveSelect(recs) {
|
|
|
4213
4609
|
let lineCount = 0;
|
|
4214
4610
|
function render() {
|
|
4215
4611
|
const lines = [];
|
|
4216
|
-
lines.push(
|
|
4612
|
+
lines.push(chalk8.bold(" Recommendations"));
|
|
4217
4613
|
lines.push("");
|
|
4218
|
-
lines.push(` ${
|
|
4219
|
-
lines.push(
|
|
4614
|
+
lines.push(` ${chalk8.dim("Name".padEnd(30))} ${chalk8.dim("Technology".padEnd(18))} ${chalk8.dim("Source")}`);
|
|
4615
|
+
lines.push(chalk8.dim(" " + "\u2500".repeat(70)));
|
|
4220
4616
|
for (let i = 0; i < recs.length; i++) {
|
|
4221
4617
|
const rec = recs[i];
|
|
4222
|
-
const check = selected.has(i) ?
|
|
4223
|
-
const ptr = i === cursor ?
|
|
4224
|
-
lines.push(` ${ptr} ${check} ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${
|
|
4618
|
+
const check = selected.has(i) ? chalk8.green("[x]") : "[ ]";
|
|
4619
|
+
const ptr = i === cursor ? chalk8.cyan("\u276F") : " ";
|
|
4620
|
+
lines.push(` ${ptr} ${check} ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${chalk8.dim(rec.source_url || "")}`);
|
|
4225
4621
|
}
|
|
4226
4622
|
lines.push("");
|
|
4227
|
-
lines.push(
|
|
4623
|
+
lines.push(chalk8.dim(" \u2191\u2193 navigate \u23B5 toggle a all n none \u23CE install q cancel"));
|
|
4228
4624
|
return lines.join("\n");
|
|
4229
4625
|
}
|
|
4230
4626
|
function draw(initial) {
|
|
@@ -4273,7 +4669,7 @@ async function interactiveSelect(recs) {
|
|
|
4273
4669
|
case "\n":
|
|
4274
4670
|
cleanup();
|
|
4275
4671
|
if (selected.size === 0) {
|
|
4276
|
-
console.log(
|
|
4672
|
+
console.log(chalk8.dim("\n No skills selected.\n"));
|
|
4277
4673
|
resolve2(null);
|
|
4278
4674
|
} else {
|
|
4279
4675
|
resolve2(Array.from(selected).sort().map((i) => recs[i]));
|
|
@@ -4283,7 +4679,7 @@ async function interactiveSelect(recs) {
|
|
|
4283
4679
|
case "\x1B":
|
|
4284
4680
|
case "":
|
|
4285
4681
|
cleanup();
|
|
4286
|
-
console.log(
|
|
4682
|
+
console.log(chalk8.dim("\n Cancelled.\n"));
|
|
4287
4683
|
resolve2(null);
|
|
4288
4684
|
break;
|
|
4289
4685
|
}
|
|
@@ -4323,35 +4719,35 @@ async function installSkills(recs, platforms) {
|
|
|
4323
4719
|
if (installed.length > 0) {
|
|
4324
4720
|
spinner.succeed(`Installed ${installed.length} file${installed.length > 1 ? "s" : ""}`);
|
|
4325
4721
|
for (const p of installed) {
|
|
4326
|
-
console.log(
|
|
4722
|
+
console.log(chalk8.green(` \u2713 ${p}`));
|
|
4327
4723
|
}
|
|
4328
4724
|
} else {
|
|
4329
4725
|
spinner.fail("No skills were installed");
|
|
4330
4726
|
}
|
|
4331
4727
|
for (const w of warnings) {
|
|
4332
|
-
console.log(
|
|
4728
|
+
console.log(chalk8.yellow(` \u26A0 ${w}`));
|
|
4333
4729
|
}
|
|
4334
4730
|
console.log("");
|
|
4335
4731
|
}
|
|
4336
4732
|
function printRecommendations(recs) {
|
|
4337
|
-
console.log(
|
|
4733
|
+
console.log(chalk8.bold("\n Recommendations\n"));
|
|
4338
4734
|
console.log(
|
|
4339
|
-
` ${
|
|
4735
|
+
` ${chalk8.dim("Name".padEnd(30))} ${chalk8.dim("Technology".padEnd(18))} ${chalk8.dim("Source")}`
|
|
4340
4736
|
);
|
|
4341
|
-
console.log(
|
|
4737
|
+
console.log(chalk8.dim(" " + "\u2500".repeat(70)));
|
|
4342
4738
|
for (const rec of recs) {
|
|
4343
4739
|
console.log(
|
|
4344
|
-
` ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${
|
|
4740
|
+
` ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${chalk8.dim(rec.source_url || "")}`
|
|
4345
4741
|
);
|
|
4346
4742
|
if (rec.reason) {
|
|
4347
|
-
console.log(` ${
|
|
4743
|
+
console.log(` ${chalk8.dim(" " + rec.reason)}`);
|
|
4348
4744
|
}
|
|
4349
4745
|
}
|
|
4350
4746
|
console.log("");
|
|
4351
4747
|
}
|
|
4352
4748
|
|
|
4353
4749
|
// src/commands/score.ts
|
|
4354
|
-
import
|
|
4750
|
+
import chalk9 from "chalk";
|
|
4355
4751
|
async function scoreCommand(options) {
|
|
4356
4752
|
const dir = process.cwd();
|
|
4357
4753
|
const target = options.agent ?? readState()?.targetAgent;
|
|
@@ -4365,14 +4761,14 @@ async function scoreCommand(options) {
|
|
|
4365
4761
|
return;
|
|
4366
4762
|
}
|
|
4367
4763
|
displayScore(result);
|
|
4368
|
-
const separator =
|
|
4764
|
+
const separator = chalk9.gray(" " + "\u2500".repeat(53));
|
|
4369
4765
|
console.log(separator);
|
|
4370
4766
|
if (result.score < 40) {
|
|
4371
|
-
console.log(
|
|
4767
|
+
console.log(chalk9.gray(" Run ") + chalk9.hex("#f97316")("caliber init") + chalk9.gray(" to generate a complete, optimized setup."));
|
|
4372
4768
|
} else if (result.score < 70) {
|
|
4373
|
-
console.log(
|
|
4769
|
+
console.log(chalk9.gray(" Run ") + chalk9.hex("#f97316")("caliber init") + chalk9.gray(" to improve your setup."));
|
|
4374
4770
|
} else {
|
|
4375
|
-
console.log(
|
|
4771
|
+
console.log(chalk9.green(" Looking good!") + chalk9.gray(" Run ") + chalk9.hex("#f97316")("caliber update") + chalk9.gray(" to keep it fresh."));
|
|
4376
4772
|
}
|
|
4377
4773
|
console.log("");
|
|
4378
4774
|
}
|
|
@@ -4380,11 +4776,11 @@ async function scoreCommand(options) {
|
|
|
4380
4776
|
// src/commands/refresh.ts
|
|
4381
4777
|
import fs21 from "fs";
|
|
4382
4778
|
import path18 from "path";
|
|
4383
|
-
import
|
|
4779
|
+
import chalk10 from "chalk";
|
|
4384
4780
|
import ora5 from "ora";
|
|
4385
4781
|
|
|
4386
4782
|
// src/lib/git-diff.ts
|
|
4387
|
-
import { execSync as
|
|
4783
|
+
import { execSync as execSync8 } from "child_process";
|
|
4388
4784
|
var MAX_DIFF_BYTES = 1e5;
|
|
4389
4785
|
var DOC_PATTERNS = [
|
|
4390
4786
|
"CLAUDE.md",
|
|
@@ -4398,7 +4794,7 @@ function excludeArgs() {
|
|
|
4398
4794
|
}
|
|
4399
4795
|
function safeExec(cmd) {
|
|
4400
4796
|
try {
|
|
4401
|
-
return
|
|
4797
|
+
return execSync8(cmd, {
|
|
4402
4798
|
encoding: "utf-8",
|
|
4403
4799
|
stdio: ["pipe", "pipe", "pipe"],
|
|
4404
4800
|
maxBuffer: 10 * 1024 * 1024
|
|
@@ -4575,7 +4971,7 @@ function discoverGitRepos(parentDir) {
|
|
|
4575
4971
|
}
|
|
4576
4972
|
async function refreshSingleRepo(repoDir, options) {
|
|
4577
4973
|
const quiet = !!options.quiet;
|
|
4578
|
-
const prefix = options.label ? `${
|
|
4974
|
+
const prefix = options.label ? `${chalk10.bold(options.label)} ` : "";
|
|
4579
4975
|
const state = readState();
|
|
4580
4976
|
const lastSha = state?.lastRefreshSha ?? null;
|
|
4581
4977
|
const diff = collectDiff(lastSha);
|
|
@@ -4584,7 +4980,7 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
4584
4980
|
if (currentSha) {
|
|
4585
4981
|
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
4586
4982
|
}
|
|
4587
|
-
log(quiet,
|
|
4983
|
+
log(quiet, chalk10.dim(`${prefix}No changes since last refresh.`));
|
|
4588
4984
|
return;
|
|
4589
4985
|
}
|
|
4590
4986
|
const spinner = quiet ? null : ora5(`${prefix}Analyzing changes...`).start();
|
|
@@ -4616,10 +5012,10 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
4616
5012
|
if (options.dryRun) {
|
|
4617
5013
|
spinner?.info(`${prefix}Dry run \u2014 would update:`);
|
|
4618
5014
|
for (const doc of response.docsUpdated) {
|
|
4619
|
-
console.log(` ${
|
|
5015
|
+
console.log(` ${chalk10.yellow("~")} ${doc}`);
|
|
4620
5016
|
}
|
|
4621
5017
|
if (response.changesSummary) {
|
|
4622
|
-
console.log(
|
|
5018
|
+
console.log(chalk10.dim(`
|
|
4623
5019
|
${response.changesSummary}`));
|
|
4624
5020
|
}
|
|
4625
5021
|
return;
|
|
@@ -4627,10 +5023,10 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
4627
5023
|
const written = writeRefreshDocs(response.updatedDocs);
|
|
4628
5024
|
spinner?.succeed(`${prefix}Updated ${written.length} doc${written.length === 1 ? "" : "s"}`);
|
|
4629
5025
|
for (const file of written) {
|
|
4630
|
-
log(quiet, ` ${
|
|
5026
|
+
log(quiet, ` ${chalk10.green("\u2713")} ${file}`);
|
|
4631
5027
|
}
|
|
4632
5028
|
if (response.changesSummary) {
|
|
4633
|
-
log(quiet,
|
|
5029
|
+
log(quiet, chalk10.dim(`
|
|
4634
5030
|
${response.changesSummary}`));
|
|
4635
5031
|
}
|
|
4636
5032
|
if (currentSha) {
|
|
@@ -4643,7 +5039,7 @@ async function refreshCommand(options) {
|
|
|
4643
5039
|
const config = loadConfig();
|
|
4644
5040
|
if (!config) {
|
|
4645
5041
|
if (quiet) return;
|
|
4646
|
-
console.log(
|
|
5042
|
+
console.log(chalk10.red("No LLM provider configured. Run `caliber config` (e.g. choose Cursor) or set an API key."));
|
|
4647
5043
|
throw new Error("__exit__");
|
|
4648
5044
|
}
|
|
4649
5045
|
if (isGitRepo()) {
|
|
@@ -4653,10 +5049,10 @@ async function refreshCommand(options) {
|
|
|
4653
5049
|
const repos = discoverGitRepos(process.cwd());
|
|
4654
5050
|
if (repos.length === 0) {
|
|
4655
5051
|
if (quiet) return;
|
|
4656
|
-
console.log(
|
|
5052
|
+
console.log(chalk10.red("Not inside a git repository and no git repos found in child directories."));
|
|
4657
5053
|
throw new Error("__exit__");
|
|
4658
5054
|
}
|
|
4659
|
-
log(quiet,
|
|
5055
|
+
log(quiet, chalk10.dim(`Found ${repos.length} git repo${repos.length === 1 ? "" : "s"}
|
|
4660
5056
|
`));
|
|
4661
5057
|
const originalDir = process.cwd();
|
|
4662
5058
|
for (const repo of repos) {
|
|
@@ -4666,7 +5062,7 @@ async function refreshCommand(options) {
|
|
|
4666
5062
|
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
4667
5063
|
} catch (err) {
|
|
4668
5064
|
if (err instanceof Error && err.message === "__exit__") continue;
|
|
4669
|
-
log(quiet,
|
|
5065
|
+
log(quiet, chalk10.yellow(`${repoName}: refresh failed \u2014 ${err instanceof Error ? err.message : "unknown error"}`));
|
|
4670
5066
|
}
|
|
4671
5067
|
}
|
|
4672
5068
|
process.chdir(originalDir);
|
|
@@ -4674,153 +5070,107 @@ async function refreshCommand(options) {
|
|
|
4674
5070
|
if (err instanceof Error && err.message === "__exit__") throw err;
|
|
4675
5071
|
if (quiet) return;
|
|
4676
5072
|
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
4677
|
-
console.log(
|
|
5073
|
+
console.log(chalk10.red(`Refresh failed: ${msg}`));
|
|
4678
5074
|
throw new Error("__exit__");
|
|
4679
5075
|
}
|
|
4680
5076
|
}
|
|
4681
5077
|
|
|
4682
5078
|
// src/commands/hooks.ts
|
|
4683
|
-
import
|
|
5079
|
+
import chalk11 from "chalk";
|
|
4684
5080
|
async function hooksInstallCommand() {
|
|
4685
5081
|
const result = installHook();
|
|
4686
5082
|
if (result.alreadyInstalled) {
|
|
4687
|
-
console.log(
|
|
5083
|
+
console.log(chalk11.dim("Claude Code hook already installed."));
|
|
4688
5084
|
return;
|
|
4689
5085
|
}
|
|
4690
|
-
console.log(
|
|
4691
|
-
console.log(
|
|
5086
|
+
console.log(chalk11.green("\u2713") + " SessionEnd hook installed in .claude/settings.json");
|
|
5087
|
+
console.log(chalk11.dim(" Docs will auto-refresh when Claude Code sessions end."));
|
|
4692
5088
|
}
|
|
4693
5089
|
async function hooksRemoveCommand() {
|
|
4694
5090
|
const result = removeHook();
|
|
4695
5091
|
if (result.notFound) {
|
|
4696
|
-
console.log(
|
|
5092
|
+
console.log(chalk11.dim("Claude Code hook not found."));
|
|
4697
5093
|
return;
|
|
4698
5094
|
}
|
|
4699
|
-
console.log(
|
|
5095
|
+
console.log(chalk11.green("\u2713") + " SessionEnd hook removed from .claude/settings.json");
|
|
4700
5096
|
}
|
|
4701
5097
|
async function hooksInstallPrecommitCommand() {
|
|
4702
5098
|
const result = installPreCommitHook();
|
|
4703
5099
|
if (result.alreadyInstalled) {
|
|
4704
|
-
console.log(
|
|
5100
|
+
console.log(chalk11.dim("Pre-commit hook already installed."));
|
|
4705
5101
|
return;
|
|
4706
5102
|
}
|
|
4707
5103
|
if (!result.installed) {
|
|
4708
|
-
console.log(
|
|
5104
|
+
console.log(chalk11.red("Failed to install pre-commit hook (not a git repository?)."));
|
|
4709
5105
|
return;
|
|
4710
5106
|
}
|
|
4711
|
-
console.log(
|
|
4712
|
-
console.log(
|
|
5107
|
+
console.log(chalk11.green("\u2713") + " Pre-commit hook installed in .git/hooks/pre-commit");
|
|
5108
|
+
console.log(chalk11.dim(" Docs will auto-refresh before each commit via LLM."));
|
|
4713
5109
|
}
|
|
4714
5110
|
async function hooksRemovePrecommitCommand() {
|
|
4715
5111
|
const result = removePreCommitHook();
|
|
4716
5112
|
if (result.notFound) {
|
|
4717
|
-
console.log(
|
|
5113
|
+
console.log(chalk11.dim("Pre-commit hook not found."));
|
|
4718
5114
|
return;
|
|
4719
5115
|
}
|
|
4720
|
-
console.log(
|
|
5116
|
+
console.log(chalk11.green("\u2713") + " Pre-commit hook removed from .git/hooks/pre-commit");
|
|
4721
5117
|
}
|
|
4722
5118
|
async function hooksStatusCommand() {
|
|
4723
5119
|
const claudeInstalled = isHookInstalled();
|
|
4724
5120
|
const precommitInstalled = isPreCommitHookInstalled();
|
|
4725
5121
|
if (claudeInstalled) {
|
|
4726
|
-
console.log(
|
|
5122
|
+
console.log(chalk11.green("\u2713") + " Claude Code hook is " + chalk11.green("installed"));
|
|
4727
5123
|
} else {
|
|
4728
|
-
console.log(
|
|
5124
|
+
console.log(chalk11.dim("\u2717") + " Claude Code hook is " + chalk11.yellow("not installed"));
|
|
4729
5125
|
}
|
|
4730
5126
|
if (precommitInstalled) {
|
|
4731
|
-
console.log(
|
|
5127
|
+
console.log(chalk11.green("\u2713") + " Pre-commit hook is " + chalk11.green("installed"));
|
|
4732
5128
|
} else {
|
|
4733
|
-
console.log(
|
|
5129
|
+
console.log(chalk11.dim("\u2717") + " Pre-commit hook is " + chalk11.yellow("not installed"));
|
|
4734
5130
|
}
|
|
4735
5131
|
if (!claudeInstalled && !precommitInstalled) {
|
|
4736
|
-
console.log(
|
|
5132
|
+
console.log(chalk11.dim("\n Run `caliber hooks install` or `caliber hooks install-precommit` to enable auto-refresh."));
|
|
4737
5133
|
}
|
|
4738
5134
|
}
|
|
4739
5135
|
|
|
4740
5136
|
// src/commands/config.ts
|
|
4741
|
-
import
|
|
4742
|
-
import readline2 from "readline";
|
|
4743
|
-
import select2 from "@inquirer/select";
|
|
4744
|
-
function promptInput2(question) {
|
|
4745
|
-
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
4746
|
-
return new Promise((resolve2) => {
|
|
4747
|
-
rl.question(chalk11.cyan(`${question} `), (answer) => {
|
|
4748
|
-
rl.close();
|
|
4749
|
-
resolve2(answer.trim());
|
|
4750
|
-
});
|
|
4751
|
-
});
|
|
4752
|
-
}
|
|
5137
|
+
import chalk12 from "chalk";
|
|
4753
5138
|
async function configCommand() {
|
|
4754
5139
|
const existing = loadConfig();
|
|
4755
5140
|
if (existing) {
|
|
4756
|
-
console.log(
|
|
4757
|
-
console.log(` Provider: ${
|
|
4758
|
-
console.log(` Model: ${
|
|
5141
|
+
console.log(chalk12.bold("\nCurrent Configuration\n"));
|
|
5142
|
+
console.log(` Provider: ${chalk12.cyan(existing.provider)}`);
|
|
5143
|
+
console.log(` Model: ${chalk12.cyan(existing.model)}`);
|
|
4759
5144
|
if (existing.apiKey) {
|
|
4760
5145
|
const masked = existing.apiKey.slice(0, 8) + "..." + existing.apiKey.slice(-4);
|
|
4761
|
-
console.log(` API Key: ${
|
|
5146
|
+
console.log(` API Key: ${chalk12.dim(masked)}`);
|
|
5147
|
+
}
|
|
5148
|
+
if (existing.provider === "cursor") {
|
|
5149
|
+
console.log(` Seat: ${chalk12.dim("Cursor (agent acp)")}`);
|
|
5150
|
+
}
|
|
5151
|
+
if (existing.provider === "claude-cli") {
|
|
5152
|
+
console.log(` Seat: ${chalk12.dim("Claude Code (claude -p)")}`);
|
|
4762
5153
|
}
|
|
4763
5154
|
if (existing.baseUrl) {
|
|
4764
|
-
console.log(` Base URL: ${
|
|
5155
|
+
console.log(` Base URL: ${chalk12.dim(existing.baseUrl)}`);
|
|
4765
5156
|
}
|
|
4766
5157
|
if (existing.vertexProjectId) {
|
|
4767
|
-
console.log(` Vertex Project: ${
|
|
4768
|
-
console.log(` Vertex Region: ${
|
|
5158
|
+
console.log(` Vertex Project: ${chalk12.dim(existing.vertexProjectId)}`);
|
|
5159
|
+
console.log(` Vertex Region: ${chalk12.dim(existing.vertexRegion || "us-east5")}`);
|
|
4769
5160
|
}
|
|
4770
|
-
console.log(` Source: ${
|
|
5161
|
+
console.log(` Source: ${chalk12.dim(process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY || process.env.VERTEX_PROJECT_ID || process.env.CALIBER_USE_CURSOR_SEAT || process.env.CALIBER_USE_CLAUDE_CLI ? "environment variables" : getConfigFilePath())}`);
|
|
4771
5162
|
console.log("");
|
|
4772
5163
|
}
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
{ name: "Anthropic (Claude)", value: "anthropic" },
|
|
4777
|
-
{ name: "Google Vertex AI (Claude)", value: "vertex" },
|
|
4778
|
-
{ name: "OpenAI / OpenAI-compatible", value: "openai" }
|
|
4779
|
-
]
|
|
4780
|
-
});
|
|
4781
|
-
const config = { provider, model: "" };
|
|
4782
|
-
switch (provider) {
|
|
4783
|
-
case "anthropic": {
|
|
4784
|
-
config.apiKey = await promptInput2("Anthropic API key:");
|
|
4785
|
-
if (!config.apiKey) {
|
|
4786
|
-
console.log(chalk11.red("API key is required."));
|
|
4787
|
-
throw new Error("__exit__");
|
|
4788
|
-
}
|
|
4789
|
-
config.model = await promptInput2(`Model (default: ${DEFAULT_MODELS.anthropic}):`) || DEFAULT_MODELS.anthropic;
|
|
4790
|
-
break;
|
|
4791
|
-
}
|
|
4792
|
-
case "vertex": {
|
|
4793
|
-
config.vertexProjectId = await promptInput2("GCP Project ID:");
|
|
4794
|
-
if (!config.vertexProjectId) {
|
|
4795
|
-
console.log(chalk11.red("Project ID is required."));
|
|
4796
|
-
throw new Error("__exit__");
|
|
4797
|
-
}
|
|
4798
|
-
config.vertexRegion = await promptInput2("Region (default: us-east5):") || "us-east5";
|
|
4799
|
-
config.vertexCredentials = await promptInput2("Service account credentials JSON (or leave empty for ADC):") || void 0;
|
|
4800
|
-
config.model = await promptInput2(`Model (default: ${DEFAULT_MODELS.vertex}):`) || DEFAULT_MODELS.vertex;
|
|
4801
|
-
break;
|
|
4802
|
-
}
|
|
4803
|
-
case "openai": {
|
|
4804
|
-
config.apiKey = await promptInput2("API key:");
|
|
4805
|
-
if (!config.apiKey) {
|
|
4806
|
-
console.log(chalk11.red("API key is required."));
|
|
4807
|
-
throw new Error("__exit__");
|
|
4808
|
-
}
|
|
4809
|
-
config.baseUrl = await promptInput2("Base URL (leave empty for OpenAI, or enter custom endpoint):") || void 0;
|
|
4810
|
-
config.model = await promptInput2(`Model (default: ${DEFAULT_MODELS.openai}):`) || DEFAULT_MODELS.openai;
|
|
4811
|
-
break;
|
|
4812
|
-
}
|
|
4813
|
-
}
|
|
4814
|
-
writeConfigFile(config);
|
|
4815
|
-
console.log(chalk11.green("\n\u2713 Configuration saved"));
|
|
4816
|
-
console.log(chalk11.dim(` ${getConfigFilePath()}
|
|
5164
|
+
await runInteractiveProviderSetup();
|
|
5165
|
+
console.log(chalk12.green("\n\u2713 Configuration saved"));
|
|
5166
|
+
console.log(chalk12.dim(` ${getConfigFilePath()}
|
|
4817
5167
|
`));
|
|
4818
|
-
console.log(
|
|
4819
|
-
console.log(
|
|
5168
|
+
console.log(chalk12.dim(" You can also set environment variables instead:"));
|
|
5169
|
+
console.log(chalk12.dim(" ANTHROPIC_API_KEY, OPENAI_API_KEY, VERTEX_PROJECT_ID, CALIBER_USE_CURSOR_SEAT=1, or CALIBER_USE_CLAUDE_CLI=1\n"));
|
|
4820
5170
|
}
|
|
4821
5171
|
|
|
4822
5172
|
// src/commands/learn.ts
|
|
4823
|
-
import
|
|
5173
|
+
import chalk13 from "chalk";
|
|
4824
5174
|
|
|
4825
5175
|
// src/learner/stdin.ts
|
|
4826
5176
|
var STDIN_TIMEOUT_MS = 5e3;
|
|
@@ -5121,46 +5471,46 @@ async function learnFinalizeCommand() {
|
|
|
5121
5471
|
async function learnInstallCommand() {
|
|
5122
5472
|
const result = installLearningHooks();
|
|
5123
5473
|
if (result.alreadyInstalled) {
|
|
5124
|
-
console.log(
|
|
5474
|
+
console.log(chalk13.dim("Learning hooks already installed."));
|
|
5125
5475
|
return;
|
|
5126
5476
|
}
|
|
5127
|
-
console.log(
|
|
5128
|
-
console.log(
|
|
5129
|
-
console.log(
|
|
5477
|
+
console.log(chalk13.green("\u2713") + " Learning hooks installed in .claude/settings.json");
|
|
5478
|
+
console.log(chalk13.dim(" PostToolUse, PostToolUseFailure, and SessionEnd hooks active."));
|
|
5479
|
+
console.log(chalk13.dim(" Session learnings will be written to CLAUDE.md and skills."));
|
|
5130
5480
|
}
|
|
5131
5481
|
async function learnRemoveCommand() {
|
|
5132
5482
|
const result = removeLearningHooks();
|
|
5133
5483
|
if (result.notFound) {
|
|
5134
|
-
console.log(
|
|
5484
|
+
console.log(chalk13.dim("Learning hooks not found."));
|
|
5135
5485
|
return;
|
|
5136
5486
|
}
|
|
5137
|
-
console.log(
|
|
5487
|
+
console.log(chalk13.green("\u2713") + " Learning hooks removed from .claude/settings.json");
|
|
5138
5488
|
}
|
|
5139
5489
|
async function learnStatusCommand() {
|
|
5140
5490
|
const installed = areLearningHooksInstalled();
|
|
5141
5491
|
const state = readState2();
|
|
5142
5492
|
const eventCount = getEventCount();
|
|
5143
|
-
console.log(
|
|
5493
|
+
console.log(chalk13.bold("Session Learning Status"));
|
|
5144
5494
|
console.log();
|
|
5145
5495
|
if (installed) {
|
|
5146
|
-
console.log(
|
|
5496
|
+
console.log(chalk13.green("\u2713") + " Learning hooks are " + chalk13.green("installed"));
|
|
5147
5497
|
} else {
|
|
5148
|
-
console.log(
|
|
5149
|
-
console.log(
|
|
5498
|
+
console.log(chalk13.dim("\u2717") + " Learning hooks are " + chalk13.yellow("not installed"));
|
|
5499
|
+
console.log(chalk13.dim(" Run `caliber learn install` to enable session learning."));
|
|
5150
5500
|
}
|
|
5151
5501
|
console.log();
|
|
5152
|
-
console.log(`Events recorded: ${
|
|
5153
|
-
console.log(`Total this session: ${
|
|
5502
|
+
console.log(`Events recorded: ${chalk13.cyan(String(eventCount))}`);
|
|
5503
|
+
console.log(`Total this session: ${chalk13.cyan(String(state.eventCount))}`);
|
|
5154
5504
|
if (state.lastAnalysisTimestamp) {
|
|
5155
|
-
console.log(`Last analysis: ${
|
|
5505
|
+
console.log(`Last analysis: ${chalk13.cyan(state.lastAnalysisTimestamp)}`);
|
|
5156
5506
|
} else {
|
|
5157
|
-
console.log(`Last analysis: ${
|
|
5507
|
+
console.log(`Last analysis: ${chalk13.dim("none")}`);
|
|
5158
5508
|
}
|
|
5159
5509
|
const learnedSection = readLearnedSection();
|
|
5160
5510
|
if (learnedSection) {
|
|
5161
5511
|
const lineCount = learnedSection.split("\n").filter(Boolean).length;
|
|
5162
5512
|
console.log(`
|
|
5163
|
-
Learned items in CLAUDE.md: ${
|
|
5513
|
+
Learned items in CLAUDE.md: ${chalk13.cyan(String(lineCount))}`);
|
|
5164
5514
|
}
|
|
5165
5515
|
}
|
|
5166
5516
|
|
|
@@ -5197,8 +5547,8 @@ learn.command("status").description("Show learning system status").action(learnS
|
|
|
5197
5547
|
import fs25 from "fs";
|
|
5198
5548
|
import path22 from "path";
|
|
5199
5549
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5200
|
-
import { execSync as
|
|
5201
|
-
import
|
|
5550
|
+
import { execSync as execSync9 } from "child_process";
|
|
5551
|
+
import chalk14 from "chalk";
|
|
5202
5552
|
import ora6 from "ora";
|
|
5203
5553
|
import confirm2 from "@inquirer/confirm";
|
|
5204
5554
|
var __dirname_vc = path22.dirname(fileURLToPath2(import.meta.url));
|
|
@@ -5207,7 +5557,7 @@ var pkg2 = JSON.parse(
|
|
|
5207
5557
|
);
|
|
5208
5558
|
function getInstalledVersion() {
|
|
5209
5559
|
try {
|
|
5210
|
-
const globalRoot =
|
|
5560
|
+
const globalRoot = execSync9("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
5211
5561
|
const pkgPath = path22.join(globalRoot, "@rely-ai", "caliber", "package.json");
|
|
5212
5562
|
return JSON.parse(fs25.readFileSync(pkgPath, "utf-8")).version;
|
|
5213
5563
|
} catch {
|
|
@@ -5232,17 +5582,17 @@ async function checkForUpdates() {
|
|
|
5232
5582
|
const isInteractive = process.stdin.isTTY === true;
|
|
5233
5583
|
if (!isInteractive) {
|
|
5234
5584
|
console.log(
|
|
5235
|
-
|
|
5585
|
+
chalk14.yellow(
|
|
5236
5586
|
`
|
|
5237
5587
|
Update available: ${current} -> ${latest}
|
|
5238
|
-
Run ${
|
|
5588
|
+
Run ${chalk14.bold("npm install -g @rely-ai/caliber")} to upgrade.
|
|
5239
5589
|
`
|
|
5240
5590
|
)
|
|
5241
5591
|
);
|
|
5242
5592
|
return;
|
|
5243
5593
|
}
|
|
5244
5594
|
console.log(
|
|
5245
|
-
|
|
5595
|
+
chalk14.yellow(`
|
|
5246
5596
|
Update available: ${current} -> ${latest}`)
|
|
5247
5597
|
);
|
|
5248
5598
|
const shouldUpdate = await confirm2({ message: "Would you like to update now? (Y/n)", default: true });
|
|
@@ -5252,7 +5602,7 @@ Update available: ${current} -> ${latest}`)
|
|
|
5252
5602
|
}
|
|
5253
5603
|
const spinner = ora6("Updating caliber...").start();
|
|
5254
5604
|
try {
|
|
5255
|
-
|
|
5605
|
+
execSync9(`npm install -g @rely-ai/caliber@${latest}`, {
|
|
5256
5606
|
stdio: "pipe",
|
|
5257
5607
|
timeout: 12e4,
|
|
5258
5608
|
env: { ...process.env, npm_config_fund: "false", npm_config_audit: "false" }
|
|
@@ -5260,16 +5610,16 @@ Update available: ${current} -> ${latest}`)
|
|
|
5260
5610
|
const installed = getInstalledVersion();
|
|
5261
5611
|
if (installed !== latest) {
|
|
5262
5612
|
spinner.fail(`Update incomplete \u2014 got ${installed ?? "unknown"}, expected ${latest}`);
|
|
5263
|
-
console.log(
|
|
5613
|
+
console.log(chalk14.yellow(`Run ${chalk14.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually.
|
|
5264
5614
|
`));
|
|
5265
5615
|
return;
|
|
5266
5616
|
}
|
|
5267
|
-
spinner.succeed(
|
|
5617
|
+
spinner.succeed(chalk14.green(`Updated to ${latest}`));
|
|
5268
5618
|
const args = process.argv.slice(2);
|
|
5269
|
-
console.log(
|
|
5619
|
+
console.log(chalk14.dim(`
|
|
5270
5620
|
Restarting: caliber ${args.join(" ")}
|
|
5271
5621
|
`));
|
|
5272
|
-
|
|
5622
|
+
execSync9(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
|
|
5273
5623
|
stdio: "inherit",
|
|
5274
5624
|
env: { ...process.env, CALIBER_SKIP_UPDATE_CHECK: "1" }
|
|
5275
5625
|
});
|
|
@@ -5279,11 +5629,11 @@ Restarting: caliber ${args.join(" ")}
|
|
|
5279
5629
|
if (err instanceof Error) {
|
|
5280
5630
|
const stderr = err.stderr;
|
|
5281
5631
|
const errMsg = stderr ? String(stderr).trim().split("\n").pop() : err.message.split("\n")[0];
|
|
5282
|
-
if (errMsg && !errMsg.includes("SIGTERM")) console.log(
|
|
5632
|
+
if (errMsg && !errMsg.includes("SIGTERM")) console.log(chalk14.dim(` ${errMsg}`));
|
|
5283
5633
|
}
|
|
5284
5634
|
console.log(
|
|
5285
|
-
|
|
5286
|
-
`Run ${
|
|
5635
|
+
chalk14.yellow(
|
|
5636
|
+
`Run ${chalk14.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually to upgrade.
|
|
5287
5637
|
`
|
|
5288
5638
|
)
|
|
5289
5639
|
);
|