@rely-ai/caliber 0.2.3 → 0.3.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 +629 -317
- 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
|
|
@@ -586,7 +586,9 @@ var CONFIG_FILE = path6.join(CONFIG_DIR, "config.json");
|
|
|
586
586
|
var DEFAULT_MODELS = {
|
|
587
587
|
anthropic: "claude-sonnet-4-6",
|
|
588
588
|
vertex: "claude-sonnet-4-6",
|
|
589
|
-
openai: "gpt-4.1"
|
|
589
|
+
openai: "gpt-4.1",
|
|
590
|
+
cursor: "default",
|
|
591
|
+
"claude-cli": "default"
|
|
590
592
|
};
|
|
591
593
|
function loadConfig() {
|
|
592
594
|
const envConfig = resolveFromEnv();
|
|
@@ -618,6 +620,18 @@ function resolveFromEnv() {
|
|
|
618
620
|
baseUrl: process.env.OPENAI_BASE_URL
|
|
619
621
|
};
|
|
620
622
|
}
|
|
623
|
+
if (process.env.CALIBER_USE_CURSOR_SEAT === "1" || process.env.CALIBER_USE_CURSOR_SEAT === "true") {
|
|
624
|
+
return {
|
|
625
|
+
provider: "cursor",
|
|
626
|
+
model: DEFAULT_MODELS.cursor
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
if (process.env.CALIBER_USE_CLAUDE_CLI === "1" || process.env.CALIBER_USE_CLAUDE_CLI === "true") {
|
|
630
|
+
return {
|
|
631
|
+
provider: "claude-cli",
|
|
632
|
+
model: DEFAULT_MODELS["claude-cli"]
|
|
633
|
+
};
|
|
634
|
+
}
|
|
621
635
|
return null;
|
|
622
636
|
}
|
|
623
637
|
function readConfigFile() {
|
|
@@ -625,7 +639,7 @@ function readConfigFile() {
|
|
|
625
639
|
if (!fs5.existsSync(CONFIG_FILE)) return null;
|
|
626
640
|
const raw = fs5.readFileSync(CONFIG_FILE, "utf-8");
|
|
627
641
|
const parsed = JSON.parse(raw);
|
|
628
|
-
if (!parsed.provider || !["anthropic", "vertex", "openai"].includes(parsed.provider)) {
|
|
642
|
+
if (!parsed.provider || !["anthropic", "vertex", "openai", "cursor", "claude-cli"].includes(parsed.provider)) {
|
|
629
643
|
return null;
|
|
630
644
|
}
|
|
631
645
|
return parsed;
|
|
@@ -817,6 +831,252 @@ var OpenAICompatProvider = class {
|
|
|
817
831
|
}
|
|
818
832
|
};
|
|
819
833
|
|
|
834
|
+
// src/llm/cursor-acp.ts
|
|
835
|
+
import { spawn, execSync as execSync2 } from "child_process";
|
|
836
|
+
import readline from "readline";
|
|
837
|
+
var ACP_AGENT_BIN = "agent";
|
|
838
|
+
var CursorAcpProvider = class {
|
|
839
|
+
defaultModel;
|
|
840
|
+
cursorApiKey;
|
|
841
|
+
constructor(config) {
|
|
842
|
+
this.defaultModel = config.model || "default";
|
|
843
|
+
this.cursorApiKey = process.env.CURSOR_API_KEY ?? process.env.CURSOR_AUTH_TOKEN;
|
|
844
|
+
}
|
|
845
|
+
async call(options) {
|
|
846
|
+
const chunks = [];
|
|
847
|
+
await this.runAcpPrompt(options, {
|
|
848
|
+
onText: (text) => chunks.push(text),
|
|
849
|
+
onEnd: () => {
|
|
850
|
+
},
|
|
851
|
+
onError: () => {
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
return chunks.join("");
|
|
855
|
+
}
|
|
856
|
+
async stream(options, callbacks) {
|
|
857
|
+
await this.runAcpPrompt(options, callbacks);
|
|
858
|
+
}
|
|
859
|
+
async runAcpPrompt(options, callbacks) {
|
|
860
|
+
const combinedPrompt = this.buildCombinedPrompt(options);
|
|
861
|
+
const args = ["acp"];
|
|
862
|
+
if (this.cursorApiKey) {
|
|
863
|
+
args.unshift("--api-key", this.cursorApiKey);
|
|
864
|
+
}
|
|
865
|
+
const agent = spawn(ACP_AGENT_BIN, args, {
|
|
866
|
+
stdio: ["pipe", "pipe", "inherit"],
|
|
867
|
+
cwd: process.cwd(),
|
|
868
|
+
env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } }
|
|
869
|
+
});
|
|
870
|
+
const pending = /* @__PURE__ */ new Map();
|
|
871
|
+
let nextId = 1;
|
|
872
|
+
let sessionId = null;
|
|
873
|
+
const send = (method, params) => {
|
|
874
|
+
return new Promise((resolve2, reject) => {
|
|
875
|
+
const id = nextId++;
|
|
876
|
+
pending.set(id, { resolve: resolve2, reject });
|
|
877
|
+
const msg = { jsonrpc: "2.0", id, method, params };
|
|
878
|
+
agent.stdin.write(JSON.stringify(msg) + "\n", (err) => {
|
|
879
|
+
if (err) {
|
|
880
|
+
pending.delete(id);
|
|
881
|
+
reject(err);
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
});
|
|
885
|
+
};
|
|
886
|
+
const rl = readline.createInterface({ input: agent.stdout, crlfDelay: Infinity });
|
|
887
|
+
rl.on("line", (line) => {
|
|
888
|
+
let msg;
|
|
889
|
+
try {
|
|
890
|
+
msg = JSON.parse(line);
|
|
891
|
+
} catch {
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
if (msg.id != null && (msg.result !== void 0 || msg.error !== void 0)) {
|
|
895
|
+
const waiter = pending.get(msg.id);
|
|
896
|
+
if (waiter) {
|
|
897
|
+
pending.delete(msg.id);
|
|
898
|
+
if (msg.error) {
|
|
899
|
+
waiter.reject(new Error(msg.error.message || "ACP error"));
|
|
900
|
+
} else {
|
|
901
|
+
waiter.resolve(msg.result);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
if (msg.result && typeof msg.result === "object" && "sessionId" in msg.result) {
|
|
905
|
+
sessionId = msg.result.sessionId;
|
|
906
|
+
}
|
|
907
|
+
if (msg.result && typeof msg.result === "object" && "stopReason" in msg.result) {
|
|
908
|
+
callbacks.onEnd({
|
|
909
|
+
stopReason: msg.result.stopReason
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
if (msg.method === "session/update" && msg.params?.update) {
|
|
915
|
+
const update = msg.params.update;
|
|
916
|
+
if (update.sessionUpdate === "agent_message_chunk" && update.content?.text) {
|
|
917
|
+
callbacks.onText(update.content.text);
|
|
918
|
+
}
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
if (msg.method === "session/request_permission" && msg.id != null) {
|
|
922
|
+
const response = JSON.stringify({
|
|
923
|
+
jsonrpc: "2.0",
|
|
924
|
+
id: msg.id,
|
|
925
|
+
result: { outcome: { outcome: "selected", optionId: "allow-once" } }
|
|
926
|
+
}) + "\n";
|
|
927
|
+
agent.stdin.write(response);
|
|
928
|
+
}
|
|
929
|
+
});
|
|
930
|
+
agent.on("error", (err) => {
|
|
931
|
+
for (const w of pending.values()) w.reject(err);
|
|
932
|
+
callbacks.onError(err);
|
|
933
|
+
});
|
|
934
|
+
agent.on("close", (code) => {
|
|
935
|
+
if (code !== 0 && code !== null) {
|
|
936
|
+
const err = new Error(`Cursor agent exited with code ${code}`);
|
|
937
|
+
for (const w of pending.values()) w.reject(err);
|
|
938
|
+
callbacks.onError(err);
|
|
939
|
+
}
|
|
940
|
+
});
|
|
941
|
+
try {
|
|
942
|
+
await send("initialize", {
|
|
943
|
+
protocolVersion: 1,
|
|
944
|
+
clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false },
|
|
945
|
+
clientInfo: { name: "caliber", version: "1.0.0" }
|
|
946
|
+
});
|
|
947
|
+
await send("authenticate", { methodId: "cursor_login" });
|
|
948
|
+
const sessionResult = await send("session/new", {
|
|
949
|
+
cwd: process.cwd(),
|
|
950
|
+
mcpServers: []
|
|
951
|
+
});
|
|
952
|
+
sessionId = sessionResult.sessionId;
|
|
953
|
+
await send("session/prompt", {
|
|
954
|
+
sessionId,
|
|
955
|
+
prompt: [{ type: "text", text: combinedPrompt }]
|
|
956
|
+
});
|
|
957
|
+
} catch (err) {
|
|
958
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
959
|
+
callbacks.onError(error);
|
|
960
|
+
throw error;
|
|
961
|
+
} finally {
|
|
962
|
+
agent.stdin?.end();
|
|
963
|
+
agent.kill("SIGTERM");
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
buildCombinedPrompt(options) {
|
|
967
|
+
const streamOpts = options;
|
|
968
|
+
const hasHistory = streamOpts.messages && streamOpts.messages.length > 0;
|
|
969
|
+
let combined = "";
|
|
970
|
+
combined += "[[System]]\n" + options.system + "\n\n";
|
|
971
|
+
if (hasHistory) {
|
|
972
|
+
for (const msg of streamOpts.messages) {
|
|
973
|
+
combined += `[[${msg.role === "user" ? "User" : "Assistant"}]]
|
|
974
|
+
${msg.content}
|
|
975
|
+
|
|
976
|
+
`;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
combined += "[[User]]\n" + options.prompt;
|
|
980
|
+
return combined;
|
|
981
|
+
}
|
|
982
|
+
};
|
|
983
|
+
function isCursorAgentAvailable() {
|
|
984
|
+
try {
|
|
985
|
+
execSync2(`which ${ACP_AGENT_BIN}`, { stdio: "ignore" });
|
|
986
|
+
return true;
|
|
987
|
+
} catch {
|
|
988
|
+
return false;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// src/llm/claude-cli.ts
|
|
993
|
+
import { spawn as spawn2, execSync as execSync3 } from "child_process";
|
|
994
|
+
var CLAUDE_CLI_BIN = "claude";
|
|
995
|
+
var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
996
|
+
var ClaudeCliProvider = class {
|
|
997
|
+
defaultModel;
|
|
998
|
+
timeoutMs;
|
|
999
|
+
constructor(config) {
|
|
1000
|
+
this.defaultModel = config.model || "default";
|
|
1001
|
+
const envTimeout = process.env.CALIBER_CLAUDE_CLI_TIMEOUT_MS;
|
|
1002
|
+
this.timeoutMs = envTimeout ? parseInt(envTimeout, 10) : DEFAULT_TIMEOUT_MS;
|
|
1003
|
+
if (!Number.isFinite(this.timeoutMs) || this.timeoutMs < 1e3) {
|
|
1004
|
+
this.timeoutMs = DEFAULT_TIMEOUT_MS;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
async call(options) {
|
|
1008
|
+
const combined = this.buildCombinedPrompt(options);
|
|
1009
|
+
return this.runClaudePrint(combined);
|
|
1010
|
+
}
|
|
1011
|
+
async stream(options, callbacks) {
|
|
1012
|
+
try {
|
|
1013
|
+
const text = await this.call(options);
|
|
1014
|
+
if (text) callbacks.onText(text);
|
|
1015
|
+
callbacks.onEnd({ stopReason: "end_turn" });
|
|
1016
|
+
} catch (err) {
|
|
1017
|
+
callbacks.onError(err instanceof Error ? err : new Error(String(err)));
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
buildCombinedPrompt(options) {
|
|
1021
|
+
const streamOpts = options;
|
|
1022
|
+
const hasHistory = streamOpts.messages && streamOpts.messages.length > 0;
|
|
1023
|
+
let combined = "";
|
|
1024
|
+
combined += "[[System]]\n" + options.system + "\n\n";
|
|
1025
|
+
if (hasHistory) {
|
|
1026
|
+
for (const msg of streamOpts.messages) {
|
|
1027
|
+
combined += `[[${msg.role === "user" ? "User" : "Assistant"}]]
|
|
1028
|
+
${msg.content}
|
|
1029
|
+
|
|
1030
|
+
`;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
combined += "[[User]]\n" + options.prompt;
|
|
1034
|
+
return combined;
|
|
1035
|
+
}
|
|
1036
|
+
runClaudePrint(combinedPrompt) {
|
|
1037
|
+
return new Promise((resolve2, reject) => {
|
|
1038
|
+
const child = spawn2(CLAUDE_CLI_BIN, ["-p", combinedPrompt], {
|
|
1039
|
+
cwd: process.cwd(),
|
|
1040
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
1041
|
+
env: process.env
|
|
1042
|
+
});
|
|
1043
|
+
const chunks = [];
|
|
1044
|
+
child.stdout.on("data", (chunk) => chunks.push(chunk));
|
|
1045
|
+
child.on("error", (err) => {
|
|
1046
|
+
clearTimeout(timer);
|
|
1047
|
+
reject(err);
|
|
1048
|
+
});
|
|
1049
|
+
child.on("close", (code, signal) => {
|
|
1050
|
+
clearTimeout(timer);
|
|
1051
|
+
const stdout = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1052
|
+
if (code === 0) {
|
|
1053
|
+
resolve2(stdout);
|
|
1054
|
+
} else {
|
|
1055
|
+
const msg = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
|
|
1056
|
+
reject(new Error(stdout ? `${msg}. Output: ${stdout.slice(0, 200)}` : msg));
|
|
1057
|
+
}
|
|
1058
|
+
});
|
|
1059
|
+
const timer = setTimeout(() => {
|
|
1060
|
+
child.kill("SIGTERM");
|
|
1061
|
+
reject(
|
|
1062
|
+
new Error(
|
|
1063
|
+
`Claude CLI timed out after ${this.timeoutMs / 1e3}s. Set CALIBER_CLAUDE_CLI_TIMEOUT_MS to increase.`
|
|
1064
|
+
)
|
|
1065
|
+
);
|
|
1066
|
+
}, this.timeoutMs);
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
};
|
|
1070
|
+
function isClaudeCliAvailable() {
|
|
1071
|
+
try {
|
|
1072
|
+
const cmd = process.platform === "win32" ? `where ${CLAUDE_CLI_BIN}` : `which ${CLAUDE_CLI_BIN}`;
|
|
1073
|
+
execSync3(cmd, { stdio: "ignore" });
|
|
1074
|
+
return true;
|
|
1075
|
+
} catch {
|
|
1076
|
+
return false;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
|
|
820
1080
|
// src/llm/utils.ts
|
|
821
1081
|
function extractJson(text) {
|
|
822
1082
|
const startIdx = text.search(/[\[{]/);
|
|
@@ -877,6 +1137,22 @@ function createProvider(config) {
|
|
|
877
1137
|
return new VertexProvider(config);
|
|
878
1138
|
case "openai":
|
|
879
1139
|
return new OpenAICompatProvider(config);
|
|
1140
|
+
case "cursor": {
|
|
1141
|
+
if (!isCursorAgentAvailable()) {
|
|
1142
|
+
throw new Error(
|
|
1143
|
+
"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."
|
|
1144
|
+
);
|
|
1145
|
+
}
|
|
1146
|
+
return new CursorAcpProvider(config);
|
|
1147
|
+
}
|
|
1148
|
+
case "claude-cli": {
|
|
1149
|
+
if (!isClaudeCliAvailable()) {
|
|
1150
|
+
throw new Error(
|
|
1151
|
+
"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."
|
|
1152
|
+
);
|
|
1153
|
+
}
|
|
1154
|
+
return new ClaudeCliProvider(config);
|
|
1155
|
+
}
|
|
880
1156
|
default:
|
|
881
1157
|
throw new Error(`Unknown provider: ${config.provider}`);
|
|
882
1158
|
}
|
|
@@ -886,7 +1162,7 @@ function getProvider() {
|
|
|
886
1162
|
const config = loadConfig();
|
|
887
1163
|
if (!config) {
|
|
888
1164
|
throw new Error(
|
|
889
|
-
"No LLM provider configured. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or VERTEX_PROJECT_ID
|
|
1165
|
+
"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
1166
|
);
|
|
891
1167
|
}
|
|
892
1168
|
cachedConfig = config;
|
|
@@ -1818,10 +2094,10 @@ function cleanupStaging() {
|
|
|
1818
2094
|
}
|
|
1819
2095
|
|
|
1820
2096
|
// src/utils/editor.ts
|
|
1821
|
-
import { execSync as
|
|
2097
|
+
import { execSync as execSync4, spawn as spawn3 } from "child_process";
|
|
1822
2098
|
function commandExists(cmd) {
|
|
1823
2099
|
try {
|
|
1824
|
-
|
|
2100
|
+
execSync4(`which ${cmd}`, { stdio: "ignore" });
|
|
1825
2101
|
return true;
|
|
1826
2102
|
} catch {
|
|
1827
2103
|
return false;
|
|
@@ -1839,12 +2115,12 @@ function openDiffsInEditor(editor, files) {
|
|
|
1839
2115
|
for (const file of files) {
|
|
1840
2116
|
try {
|
|
1841
2117
|
if (file.originalPath) {
|
|
1842
|
-
|
|
2118
|
+
spawn3(cmd, ["--diff", file.originalPath, file.proposedPath], {
|
|
1843
2119
|
stdio: "ignore",
|
|
1844
2120
|
detached: true
|
|
1845
2121
|
}).unref();
|
|
1846
2122
|
} else {
|
|
1847
|
-
|
|
2123
|
+
spawn3(cmd, [file.proposedPath], {
|
|
1848
2124
|
stdio: "ignore",
|
|
1849
2125
|
detached: true
|
|
1850
2126
|
}).unref();
|
|
@@ -1861,7 +2137,7 @@ import { createTwoFilesPatch } from "diff";
|
|
|
1861
2137
|
// src/lib/hooks.ts
|
|
1862
2138
|
import fs14 from "fs";
|
|
1863
2139
|
import path13 from "path";
|
|
1864
|
-
import { execSync as
|
|
2140
|
+
import { execSync as execSync5 } from "child_process";
|
|
1865
2141
|
var SETTINGS_PATH = path13.join(".claude", "settings.json");
|
|
1866
2142
|
var HOOK_COMMAND = "caliber refresh --quiet";
|
|
1867
2143
|
var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
|
|
@@ -1933,7 +2209,7 @@ fi
|
|
|
1933
2209
|
${PRECOMMIT_END}`;
|
|
1934
2210
|
function getGitHooksDir() {
|
|
1935
2211
|
try {
|
|
1936
|
-
const gitDir =
|
|
2212
|
+
const gitDir = execSync5("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
1937
2213
|
return path13.join(gitDir, "hooks");
|
|
1938
2214
|
} catch {
|
|
1939
2215
|
return null;
|
|
@@ -2079,7 +2355,7 @@ function removeLearningHooks() {
|
|
|
2079
2355
|
init_constants();
|
|
2080
2356
|
import fs16 from "fs";
|
|
2081
2357
|
import path15 from "path";
|
|
2082
|
-
import { execSync as
|
|
2358
|
+
import { execSync as execSync6 } from "child_process";
|
|
2083
2359
|
var STATE_FILE = path15.join(CALIBER_DIR, ".caliber-state.json");
|
|
2084
2360
|
function readState() {
|
|
2085
2361
|
try {
|
|
@@ -2097,7 +2373,7 @@ function writeState(state) {
|
|
|
2097
2373
|
}
|
|
2098
2374
|
function getCurrentHeadSha() {
|
|
2099
2375
|
try {
|
|
2100
|
-
return
|
|
2376
|
+
return execSync6("git rev-parse HEAD", {
|
|
2101
2377
|
encoding: "utf-8",
|
|
2102
2378
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2103
2379
|
}).trim();
|
|
@@ -2193,6 +2469,80 @@ var SpinnerMessages = class {
|
|
|
2193
2469
|
}
|
|
2194
2470
|
};
|
|
2195
2471
|
|
|
2472
|
+
// src/commands/interactive-provider-setup.ts
|
|
2473
|
+
import chalk2 from "chalk";
|
|
2474
|
+
import readline2 from "readline";
|
|
2475
|
+
import select from "@inquirer/select";
|
|
2476
|
+
function promptInput(question) {
|
|
2477
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
2478
|
+
return new Promise((resolve2) => {
|
|
2479
|
+
rl.question(chalk2.cyan(`${question} `), (answer) => {
|
|
2480
|
+
rl.close();
|
|
2481
|
+
resolve2(answer.trim());
|
|
2482
|
+
});
|
|
2483
|
+
});
|
|
2484
|
+
}
|
|
2485
|
+
var PROVIDER_CHOICES = [
|
|
2486
|
+
{ name: "Claude Code (use my app login \u2014 Pro/Max/Team, no API key)", value: "claude-cli" },
|
|
2487
|
+
{ name: "Cursor (use my Cursor subscription \u2014 no API key)", value: "cursor" },
|
|
2488
|
+
{ name: "Anthropic (Claude) \u2014 API key from console.anthropic.com", value: "anthropic" },
|
|
2489
|
+
{ name: "Google Vertex AI (Claude)", value: "vertex" },
|
|
2490
|
+
{ name: "OpenAI / OpenAI-compatible", value: "openai" }
|
|
2491
|
+
];
|
|
2492
|
+
async function runInteractiveProviderSetup(options) {
|
|
2493
|
+
const message = options?.selectMessage ?? "Select LLM provider";
|
|
2494
|
+
const provider = await select({
|
|
2495
|
+
message,
|
|
2496
|
+
choices: PROVIDER_CHOICES
|
|
2497
|
+
});
|
|
2498
|
+
const config = { provider, model: "" };
|
|
2499
|
+
switch (provider) {
|
|
2500
|
+
case "claude-cli": {
|
|
2501
|
+
config.model = "default";
|
|
2502
|
+
console.log(chalk2.dim(" Run `claude` once and log in with your Pro/Max/Team account if you haven't."));
|
|
2503
|
+
break;
|
|
2504
|
+
}
|
|
2505
|
+
case "cursor": {
|
|
2506
|
+
config.model = "default";
|
|
2507
|
+
console.log(chalk2.dim(" Run `agent login` if you haven't, or set CURSOR_API_KEY."));
|
|
2508
|
+
break;
|
|
2509
|
+
}
|
|
2510
|
+
case "anthropic": {
|
|
2511
|
+
console.log(chalk2.dim(" Get a key at https://console.anthropic.com (same account as Claude Pro/Team/Max)."));
|
|
2512
|
+
config.apiKey = await promptInput("Anthropic API key:");
|
|
2513
|
+
if (!config.apiKey) {
|
|
2514
|
+
console.log(chalk2.red("API key is required."));
|
|
2515
|
+
throw new Error("__exit__");
|
|
2516
|
+
}
|
|
2517
|
+
config.model = await promptInput(`Model (default: ${DEFAULT_MODELS.anthropic}):`) || DEFAULT_MODELS.anthropic;
|
|
2518
|
+
break;
|
|
2519
|
+
}
|
|
2520
|
+
case "vertex": {
|
|
2521
|
+
config.vertexProjectId = await promptInput("GCP Project ID:");
|
|
2522
|
+
if (!config.vertexProjectId) {
|
|
2523
|
+
console.log(chalk2.red("Project ID is required."));
|
|
2524
|
+
throw new Error("__exit__");
|
|
2525
|
+
}
|
|
2526
|
+
config.vertexRegion = await promptInput("Region (default: us-east5):") || "us-east5";
|
|
2527
|
+
config.vertexCredentials = await promptInput("Service account credentials JSON (or leave empty for ADC):") || void 0;
|
|
2528
|
+
config.model = await promptInput(`Model (default: ${DEFAULT_MODELS.vertex}):`) || DEFAULT_MODELS.vertex;
|
|
2529
|
+
break;
|
|
2530
|
+
}
|
|
2531
|
+
case "openai": {
|
|
2532
|
+
config.apiKey = await promptInput("API key:");
|
|
2533
|
+
if (!config.apiKey) {
|
|
2534
|
+
console.log(chalk2.red("API key is required."));
|
|
2535
|
+
throw new Error("__exit__");
|
|
2536
|
+
}
|
|
2537
|
+
config.baseUrl = await promptInput("Base URL (leave empty for OpenAI, or enter custom endpoint):") || void 0;
|
|
2538
|
+
config.model = await promptInput(`Model (default: ${DEFAULT_MODELS.openai}):`) || DEFAULT_MODELS.openai;
|
|
2539
|
+
break;
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
writeConfigFile(config);
|
|
2543
|
+
return config;
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2196
2546
|
// src/scoring/index.ts
|
|
2197
2547
|
import { existsSync as existsSync8 } from "fs";
|
|
2198
2548
|
import { join as join7 } from "path";
|
|
@@ -3107,7 +3457,7 @@ function checkFreshness(dir) {
|
|
|
3107
3457
|
|
|
3108
3458
|
// src/scoring/checks/bonus.ts
|
|
3109
3459
|
import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "fs";
|
|
3110
|
-
import { execSync as
|
|
3460
|
+
import { execSync as execSync7 } from "child_process";
|
|
3111
3461
|
import { join as join6 } from "path";
|
|
3112
3462
|
function readFileOrNull5(path23) {
|
|
3113
3463
|
try {
|
|
@@ -3118,7 +3468,7 @@ function readFileOrNull5(path23) {
|
|
|
3118
3468
|
}
|
|
3119
3469
|
function hasPreCommitHook(dir) {
|
|
3120
3470
|
try {
|
|
3121
|
-
const gitDir =
|
|
3471
|
+
const gitDir = execSync7("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
3122
3472
|
const hookPath = join6(gitDir, "hooks", "pre-commit");
|
|
3123
3473
|
const content = readFileOrNull5(hookPath);
|
|
3124
3474
|
return content ? content.includes("caliber") : false;
|
|
@@ -3263,7 +3613,7 @@ function computeLocalScore(dir, targetAgent) {
|
|
|
3263
3613
|
}
|
|
3264
3614
|
|
|
3265
3615
|
// src/scoring/display.ts
|
|
3266
|
-
import
|
|
3616
|
+
import chalk3 from "chalk";
|
|
3267
3617
|
var CATEGORY_LABELS = {
|
|
3268
3618
|
existence: "FILES & SETUP",
|
|
3269
3619
|
quality: "QUALITY",
|
|
@@ -3276,31 +3626,31 @@ var CATEGORY_ORDER = ["existence", "quality", "coverage", "accuracy", "freshness
|
|
|
3276
3626
|
function gradeColor(grade) {
|
|
3277
3627
|
switch (grade) {
|
|
3278
3628
|
case "A":
|
|
3279
|
-
return
|
|
3629
|
+
return chalk3.green;
|
|
3280
3630
|
case "B":
|
|
3281
|
-
return
|
|
3631
|
+
return chalk3.greenBright;
|
|
3282
3632
|
case "C":
|
|
3283
|
-
return
|
|
3633
|
+
return chalk3.yellow;
|
|
3284
3634
|
case "D":
|
|
3285
|
-
return
|
|
3635
|
+
return chalk3.hex("#f97316");
|
|
3286
3636
|
case "F":
|
|
3287
|
-
return
|
|
3637
|
+
return chalk3.red;
|
|
3288
3638
|
default:
|
|
3289
|
-
return
|
|
3639
|
+
return chalk3.white;
|
|
3290
3640
|
}
|
|
3291
3641
|
}
|
|
3292
3642
|
function progressBar(score, max, width = 40) {
|
|
3293
3643
|
const filled = Math.round(score / max * width);
|
|
3294
3644
|
const empty = width - filled;
|
|
3295
|
-
const bar =
|
|
3645
|
+
const bar = chalk3.hex("#f97316")("\u2593".repeat(filled)) + chalk3.gray("\u2591".repeat(empty));
|
|
3296
3646
|
return bar;
|
|
3297
3647
|
}
|
|
3298
3648
|
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 ?
|
|
3649
|
+
const icon = check.passed ? chalk3.green("\u2713") : check.earnedPoints < 0 ? chalk3.red("\u2717") : chalk3.gray("\u2717");
|
|
3650
|
+
const points = check.passed ? chalk3.green(`+${check.earnedPoints}`.padStart(4)) : check.earnedPoints < 0 ? chalk3.red(`${check.earnedPoints}`.padStart(4)) : chalk3.gray(" \u2014");
|
|
3651
|
+
const name = check.passed ? chalk3.white(check.name) : chalk3.gray(check.name);
|
|
3652
|
+
const detail = check.detail ? chalk3.gray(` (${check.detail})`) : "";
|
|
3653
|
+
const suggestion = !check.passed && check.suggestion ? chalk3.gray(`
|
|
3304
3654
|
\u2192 ${check.suggestion}`) : "";
|
|
3305
3655
|
return ` ${icon} ${name.padEnd(38)}${points}${detail}${suggestion}`;
|
|
3306
3656
|
}
|
|
@@ -3308,21 +3658,21 @@ function displayScore(result) {
|
|
|
3308
3658
|
const gc = gradeColor(result.grade);
|
|
3309
3659
|
const agentLabel = result.targetAgent === "both" ? "Claude Code + Cursor" : result.targetAgent === "claude" ? "Claude Code" : "Cursor";
|
|
3310
3660
|
console.log("");
|
|
3311
|
-
console.log(
|
|
3312
|
-
console.log(
|
|
3661
|
+
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"));
|
|
3662
|
+
console.log(chalk3.gray(" \u2502") + " " + chalk3.gray("\u2502"));
|
|
3313
3663
|
console.log(
|
|
3314
|
-
|
|
3664
|
+
chalk3.gray(" \u2502") + " Agent Config Score" + gc(` ${String(result.score).padStart(3)} / ${result.maxScore}`) + " Grade " + gc(result.grade) + " " + chalk3.gray("\u2502")
|
|
3315
3665
|
);
|
|
3316
|
-
console.log(
|
|
3317
|
-
console.log(
|
|
3318
|
-
console.log(
|
|
3319
|
-
console.log(
|
|
3666
|
+
console.log(chalk3.gray(" \u2502") + ` ${progressBar(result.score, result.maxScore)} ` + chalk3.gray("\u2502"));
|
|
3667
|
+
console.log(chalk3.gray(" \u2502") + chalk3.dim(` Target: ${agentLabel}`) + " ".repeat(Math.max(1, 40 - agentLabel.length)) + chalk3.gray("\u2502"));
|
|
3668
|
+
console.log(chalk3.gray(" \u2502") + " " + chalk3.gray("\u2502"));
|
|
3669
|
+
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
3670
|
console.log("");
|
|
3321
3671
|
for (const category of CATEGORY_ORDER) {
|
|
3322
3672
|
const summary = result.categories[category];
|
|
3323
3673
|
const categoryChecks = result.checks.filter((c) => c.category === category);
|
|
3324
3674
|
console.log(
|
|
3325
|
-
|
|
3675
|
+
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
3676
|
);
|
|
3327
3677
|
for (const check of categoryChecks) {
|
|
3328
3678
|
console.log(formatCheck(check));
|
|
@@ -3333,7 +3683,7 @@ function displayScore(result) {
|
|
|
3333
3683
|
function displayScoreDelta(before, after) {
|
|
3334
3684
|
const delta = after.score - before.score;
|
|
3335
3685
|
const deltaStr = delta >= 0 ? `+${delta}` : `${delta}`;
|
|
3336
|
-
const deltaColor = delta >= 0 ?
|
|
3686
|
+
const deltaColor = delta >= 0 ? chalk3.green : chalk3.red;
|
|
3337
3687
|
const beforeGc = gradeColor(before.grade);
|
|
3338
3688
|
const afterGc = gradeColor(after.grade);
|
|
3339
3689
|
const BOX_INNER = 51;
|
|
@@ -3344,30 +3694,30 @@ function displayScoreDelta(before, after) {
|
|
|
3344
3694
|
const totalPad = BOX_INNER - contentLen;
|
|
3345
3695
|
const pad1 = Math.max(2, Math.ceil(totalPad / 2));
|
|
3346
3696
|
const pad2 = Math.max(1, totalPad - pad1);
|
|
3347
|
-
const scoreLineFormatted = " Score: " + beforeGc(`${before.score}`) +
|
|
3697
|
+
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
3698
|
const visibleLen = 3 + scorePart.length + pad1 + deltaPart.length + pad2 + gradePart.length;
|
|
3349
3699
|
const trailingPad = Math.max(0, BOX_INNER - visibleLen);
|
|
3350
3700
|
const barWidth = Math.floor((BOX_INNER - 12) / 2);
|
|
3351
|
-
const barLine = ` ${progressBar(before.score, before.maxScore, barWidth)}` +
|
|
3701
|
+
const barLine = ` ${progressBar(before.score, before.maxScore, barWidth)}` + chalk3.gray(" \u2192 ") + progressBar(after.score, after.maxScore, barWidth) + " ";
|
|
3352
3702
|
console.log("");
|
|
3353
|
-
console.log(
|
|
3354
|
-
console.log(
|
|
3355
|
-
console.log(
|
|
3356
|
-
console.log(
|
|
3357
|
-
console.log(
|
|
3358
|
-
console.log(
|
|
3703
|
+
console.log(chalk3.gray(" \u256D" + "\u2500".repeat(BOX_INNER) + "\u256E"));
|
|
3704
|
+
console.log(chalk3.gray(" \u2502") + " ".repeat(BOX_INNER) + chalk3.gray("\u2502"));
|
|
3705
|
+
console.log(chalk3.gray(" \u2502") + scoreLineFormatted + " ".repeat(trailingPad) + chalk3.gray("\u2502"));
|
|
3706
|
+
console.log(chalk3.gray(" \u2502") + barLine + chalk3.gray("\u2502"));
|
|
3707
|
+
console.log(chalk3.gray(" \u2502") + " ".repeat(BOX_INNER) + chalk3.gray("\u2502"));
|
|
3708
|
+
console.log(chalk3.gray(" \u2570" + "\u2500".repeat(BOX_INNER) + "\u256F"));
|
|
3359
3709
|
console.log("");
|
|
3360
3710
|
const improved = after.checks.filter((ac) => {
|
|
3361
3711
|
const bc = before.checks.find((b) => b.id === ac.id);
|
|
3362
3712
|
return bc && ac.earnedPoints > bc.earnedPoints;
|
|
3363
3713
|
});
|
|
3364
3714
|
if (improved.length > 0) {
|
|
3365
|
-
console.log(
|
|
3715
|
+
console.log(chalk3.gray(" What improved:"));
|
|
3366
3716
|
for (const check of improved) {
|
|
3367
3717
|
const bc = before.checks.find((b) => b.id === check.id);
|
|
3368
3718
|
const gain = check.earnedPoints - bc.earnedPoints;
|
|
3369
3719
|
console.log(
|
|
3370
|
-
|
|
3720
|
+
chalk3.green(" +") + chalk3.white(` ${check.name.padEnd(50)}`) + chalk3.green(`+${gain}`)
|
|
3371
3721
|
);
|
|
3372
3722
|
}
|
|
3373
3723
|
console.log("");
|
|
@@ -3376,7 +3726,7 @@ function displayScoreDelta(before, after) {
|
|
|
3376
3726
|
|
|
3377
3727
|
// src/commands/init.ts
|
|
3378
3728
|
async function initCommand(options) {
|
|
3379
|
-
console.log(
|
|
3729
|
+
console.log(chalk4.bold.hex("#6366f1")(`
|
|
3380
3730
|
\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
3731
|
\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
3732
|
\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 +3734,63 @@ async function initCommand(options) {
|
|
|
3384
3734
|
\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
3735
|
\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
3736
|
`));
|
|
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
|
-
|
|
3737
|
+
console.log(chalk4.dim(" Configure your coding agent environment\n"));
|
|
3738
|
+
console.log(chalk4.bold(" What is Caliber?\n"));
|
|
3739
|
+
console.log(chalk4.dim(" Caliber audits your AI agent configurations and suggests targeted"));
|
|
3740
|
+
console.log(chalk4.dim(" improvements. It analyzes CLAUDE.md, .cursorrules, and skills"));
|
|
3741
|
+
console.log(chalk4.dim(" against your actual codebase \u2014 keeping what works, fixing"));
|
|
3742
|
+
console.log(chalk4.dim(" what's stale, and adding what's missing.\n"));
|
|
3743
|
+
console.log(chalk4.bold(" How it works:\n"));
|
|
3744
|
+
console.log(chalk4.dim(" 1. Scan Analyze your code, dependencies, and existing configs"));
|
|
3745
|
+
console.log(chalk4.dim(" 2. Generate AI creates or improves config files for your project"));
|
|
3746
|
+
console.log(chalk4.dim(" 3. Review You accept, refine, or decline the proposed changes"));
|
|
3747
|
+
console.log(chalk4.dim(" 4. Apply Config files are written with backups\n"));
|
|
3748
|
+
console.log(chalk4.hex("#6366f1").bold(" Step 1/4 \u2014 How do you want to use Caliber?\n"));
|
|
3749
|
+
let config = loadConfig();
|
|
3400
3750
|
if (!config) {
|
|
3401
|
-
console.log(
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3751
|
+
console.log(chalk4.dim(" No LLM provider set yet. Choose how to run Caliber:\n"));
|
|
3752
|
+
try {
|
|
3753
|
+
await runInteractiveProviderSetup({
|
|
3754
|
+
selectMessage: "How do you want to use Caliber? (choose LLM provider)"
|
|
3755
|
+
});
|
|
3756
|
+
} catch (err) {
|
|
3757
|
+
if (err.message === "__exit__") throw err;
|
|
3758
|
+
throw err;
|
|
3759
|
+
}
|
|
3760
|
+
config = loadConfig();
|
|
3761
|
+
if (!config) {
|
|
3762
|
+
console.log(chalk4.red(" Setup was cancelled or failed.\n"));
|
|
3763
|
+
throw new Error("__exit__");
|
|
3764
|
+
}
|
|
3765
|
+
console.log(chalk4.green(" \u2713 Provider saved. Continuing with init.\n"));
|
|
3408
3766
|
}
|
|
3409
|
-
console.log(
|
|
3767
|
+
console.log(chalk4.dim(` Provider: ${config.provider} | Model: ${config.model}
|
|
3410
3768
|
`));
|
|
3411
|
-
console.log(
|
|
3412
|
-
console.log(
|
|
3769
|
+
console.log(chalk4.hex("#6366f1").bold(" Step 2/4 \u2014 Scan project\n"));
|
|
3770
|
+
console.log(chalk4.dim(" Detecting languages, dependencies, file structure, and existing configs.\n"));
|
|
3413
3771
|
const spinner = ora("Analyzing project...").start();
|
|
3414
3772
|
const fingerprint = collectFingerprint(process.cwd());
|
|
3415
3773
|
await enrichFingerprintWithLLM(fingerprint, process.cwd());
|
|
3416
3774
|
spinner.succeed("Project analyzed");
|
|
3417
|
-
console.log(
|
|
3418
|
-
console.log(
|
|
3775
|
+
console.log(chalk4.dim(` Languages: ${fingerprint.languages.join(", ") || "none detected"}`));
|
|
3776
|
+
console.log(chalk4.dim(` Files: ${fingerprint.fileTree.length} found
|
|
3419
3777
|
`));
|
|
3420
3778
|
const targetAgent = options.agent || await promptAgent();
|
|
3421
3779
|
const baselineScore = computeLocalScore(process.cwd(), targetAgent);
|
|
3422
3780
|
const isEmpty = fingerprint.fileTree.length < 3;
|
|
3423
3781
|
if (isEmpty) {
|
|
3424
|
-
fingerprint.description = await
|
|
3782
|
+
fingerprint.description = await promptInput2("What will you build in this project?");
|
|
3425
3783
|
}
|
|
3426
3784
|
const hasExistingConfig = !!(fingerprint.existingConfigs.claudeMd || fingerprint.existingConfigs.claudeSettings || fingerprint.existingConfigs.claudeSkills?.length || fingerprint.existingConfigs.cursorrules || fingerprint.existingConfigs.cursorRules?.length);
|
|
3427
3785
|
if (hasExistingConfig) {
|
|
3428
|
-
console.log(
|
|
3429
|
-
console.log(
|
|
3430
|
-
console.log(
|
|
3786
|
+
console.log(chalk4.hex("#6366f1").bold(" Step 3/4 \u2014 Auditing your configs\n"));
|
|
3787
|
+
console.log(chalk4.dim(" AI is reviewing your existing configs against your codebase"));
|
|
3788
|
+
console.log(chalk4.dim(" and suggesting improvements.\n"));
|
|
3431
3789
|
} else {
|
|
3432
|
-
console.log(
|
|
3433
|
-
console.log(
|
|
3790
|
+
console.log(chalk4.hex("#6366f1").bold(" Step 3/4 \u2014 Generating configs\n"));
|
|
3791
|
+
console.log(chalk4.dim(" AI is creating agent config files tailored to your project.\n"));
|
|
3434
3792
|
}
|
|
3435
|
-
console.log(
|
|
3793
|
+
console.log(chalk4.dim(" This usually takes 1\u20133 minutes.\n"));
|
|
3436
3794
|
const genStartTime = Date.now();
|
|
3437
3795
|
const genSpinner = ora("Generating setup...").start();
|
|
3438
3796
|
const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES, { showElapsedTime: true });
|
|
@@ -3471,8 +3829,8 @@ async function initCommand(options) {
|
|
|
3471
3829
|
if (!generatedSetup) {
|
|
3472
3830
|
genSpinner.fail("Failed to generate setup.");
|
|
3473
3831
|
if (rawOutput) {
|
|
3474
|
-
console.log(
|
|
3475
|
-
console.log(
|
|
3832
|
+
console.log(chalk4.dim("\nRaw LLM output (JSON parse failed):"));
|
|
3833
|
+
console.log(chalk4.dim(rawOutput.slice(0, 500)));
|
|
3476
3834
|
}
|
|
3477
3835
|
throw new Error("__exit__");
|
|
3478
3836
|
}
|
|
@@ -3480,12 +3838,12 @@ async function initCommand(options) {
|
|
|
3480
3838
|
const mins = Math.floor(elapsedMs / 6e4);
|
|
3481
3839
|
const secs = Math.floor(elapsedMs % 6e4 / 1e3);
|
|
3482
3840
|
const timeStr = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
|
|
3483
|
-
genSpinner.succeed(`Setup generated ${
|
|
3841
|
+
genSpinner.succeed(`Setup generated ${chalk4.dim(`in ${timeStr}`)}`);
|
|
3484
3842
|
printSetupSummary(generatedSetup);
|
|
3485
|
-
console.log(
|
|
3843
|
+
console.log(chalk4.hex("#6366f1").bold(" Step 4/4 \u2014 Review\n"));
|
|
3486
3844
|
const setupFiles = collectSetupFiles(generatedSetup);
|
|
3487
3845
|
const staged = stageFiles(setupFiles, process.cwd());
|
|
3488
|
-
console.log(
|
|
3846
|
+
console.log(chalk4.dim(` ${chalk4.green(`${staged.newFiles} new`)} / ${chalk4.yellow(`${staged.modifiedFiles} modified`)} file${staged.newFiles + staged.modifiedFiles !== 1 ? "s" : ""}
|
|
3489
3847
|
`));
|
|
3490
3848
|
const wantsReview = await promptWantsReview();
|
|
3491
3849
|
if (wantsReview) {
|
|
@@ -3497,12 +3855,12 @@ async function initCommand(options) {
|
|
|
3497
3855
|
generatedSetup = await refineLoop(generatedSetup, targetAgent);
|
|
3498
3856
|
if (!generatedSetup) {
|
|
3499
3857
|
cleanupStaging();
|
|
3500
|
-
console.log(
|
|
3858
|
+
console.log(chalk4.dim("Refinement cancelled. No files were modified."));
|
|
3501
3859
|
return;
|
|
3502
3860
|
}
|
|
3503
3861
|
const updatedFiles = collectSetupFiles(generatedSetup);
|
|
3504
3862
|
const restaged = stageFiles(updatedFiles, process.cwd());
|
|
3505
|
-
console.log(
|
|
3863
|
+
console.log(chalk4.dim(` ${chalk4.green(`${restaged.newFiles} new`)} / ${chalk4.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
|
|
3506
3864
|
`));
|
|
3507
3865
|
printSetupSummary(generatedSetup);
|
|
3508
3866
|
const wantsReviewAgain = await promptWantsReview();
|
|
@@ -3514,11 +3872,11 @@ async function initCommand(options) {
|
|
|
3514
3872
|
}
|
|
3515
3873
|
cleanupStaging();
|
|
3516
3874
|
if (action === "decline") {
|
|
3517
|
-
console.log(
|
|
3875
|
+
console.log(chalk4.dim("Setup declined. No files were modified."));
|
|
3518
3876
|
return;
|
|
3519
3877
|
}
|
|
3520
3878
|
if (options.dryRun) {
|
|
3521
|
-
console.log(
|
|
3879
|
+
console.log(chalk4.yellow("\n[Dry run] Would write the following files:"));
|
|
3522
3880
|
console.log(JSON.stringify(generatedSetup, null, 2));
|
|
3523
3881
|
return;
|
|
3524
3882
|
}
|
|
@@ -3526,29 +3884,29 @@ async function initCommand(options) {
|
|
|
3526
3884
|
try {
|
|
3527
3885
|
const result = writeSetup(generatedSetup);
|
|
3528
3886
|
writeSpinner.succeed("Config files written");
|
|
3529
|
-
console.log(
|
|
3887
|
+
console.log(chalk4.bold("\nFiles created/updated:"));
|
|
3530
3888
|
for (const file of result.written) {
|
|
3531
|
-
console.log(` ${
|
|
3889
|
+
console.log(` ${chalk4.green("\u2713")} ${file}`);
|
|
3532
3890
|
}
|
|
3533
3891
|
if (result.deleted.length > 0) {
|
|
3534
|
-
console.log(
|
|
3892
|
+
console.log(chalk4.bold("\nFiles removed:"));
|
|
3535
3893
|
for (const file of result.deleted) {
|
|
3536
|
-
console.log(` ${
|
|
3894
|
+
console.log(` ${chalk4.red("\u2717")} ${file}`);
|
|
3537
3895
|
}
|
|
3538
3896
|
}
|
|
3539
3897
|
if (result.backupDir) {
|
|
3540
|
-
console.log(
|
|
3898
|
+
console.log(chalk4.dim(`
|
|
3541
3899
|
Backups saved to ${result.backupDir}`));
|
|
3542
3900
|
}
|
|
3543
3901
|
} catch (err) {
|
|
3544
3902
|
writeSpinner.fail("Failed to write files");
|
|
3545
|
-
console.error(
|
|
3903
|
+
console.error(chalk4.red(err instanceof Error ? err.message : "Unknown error"));
|
|
3546
3904
|
throw new Error("__exit__");
|
|
3547
3905
|
}
|
|
3548
3906
|
if (!fs17.existsSync("AGENTS.md")) {
|
|
3549
3907
|
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
3908
|
fs17.writeFileSync("AGENTS.md", agentsContent);
|
|
3551
|
-
console.log(` ${
|
|
3909
|
+
console.log(` ${chalk4.green("\u2713")} AGENTS.md`);
|
|
3552
3910
|
}
|
|
3553
3911
|
ensurePermissions();
|
|
3554
3912
|
const sha = getCurrentHeadSha();
|
|
@@ -3561,45 +3919,45 @@ async function initCommand(options) {
|
|
|
3561
3919
|
if (hookChoice === "claude" || hookChoice === "both") {
|
|
3562
3920
|
const hookResult = installHook();
|
|
3563
3921
|
if (hookResult.installed) {
|
|
3564
|
-
console.log(` ${
|
|
3565
|
-
console.log(
|
|
3922
|
+
console.log(` ${chalk4.green("\u2713")} Claude Code hook installed \u2014 docs update on session end`);
|
|
3923
|
+
console.log(chalk4.dim(" Run `caliber hooks remove` to disable"));
|
|
3566
3924
|
} else if (hookResult.alreadyInstalled) {
|
|
3567
|
-
console.log(
|
|
3925
|
+
console.log(chalk4.dim(" Claude Code hook already installed"));
|
|
3568
3926
|
}
|
|
3569
3927
|
const learnResult = installLearningHooks();
|
|
3570
3928
|
if (learnResult.installed) {
|
|
3571
|
-
console.log(` ${
|
|
3572
|
-
console.log(
|
|
3929
|
+
console.log(` ${chalk4.green("\u2713")} Learning hooks installed \u2014 session insights captured automatically`);
|
|
3930
|
+
console.log(chalk4.dim(" Run `caliber learn remove` to disable"));
|
|
3573
3931
|
} else if (learnResult.alreadyInstalled) {
|
|
3574
|
-
console.log(
|
|
3932
|
+
console.log(chalk4.dim(" Learning hooks already installed"));
|
|
3575
3933
|
}
|
|
3576
3934
|
}
|
|
3577
3935
|
if (hookChoice === "precommit" || hookChoice === "both") {
|
|
3578
3936
|
const precommitResult = installPreCommitHook();
|
|
3579
3937
|
if (precommitResult.installed) {
|
|
3580
|
-
console.log(` ${
|
|
3581
|
-
console.log(
|
|
3938
|
+
console.log(` ${chalk4.green("\u2713")} Pre-commit hook installed \u2014 docs refresh before each commit`);
|
|
3939
|
+
console.log(chalk4.dim(" Run `caliber hooks remove-precommit` to disable"));
|
|
3582
3940
|
} else if (precommitResult.alreadyInstalled) {
|
|
3583
|
-
console.log(
|
|
3941
|
+
console.log(chalk4.dim(" Pre-commit hook already installed"));
|
|
3584
3942
|
} else {
|
|
3585
|
-
console.log(
|
|
3943
|
+
console.log(chalk4.yellow(" Could not install pre-commit hook (not a git repository?)"));
|
|
3586
3944
|
}
|
|
3587
3945
|
}
|
|
3588
3946
|
if (hookChoice === "skip") {
|
|
3589
|
-
console.log(
|
|
3947
|
+
console.log(chalk4.dim(" Skipped auto-refresh hooks. Run `caliber hooks install` later to enable."));
|
|
3590
3948
|
}
|
|
3591
3949
|
const afterScore = computeLocalScore(process.cwd(), targetAgent);
|
|
3592
3950
|
displayScoreDelta(baselineScore, afterScore);
|
|
3593
|
-
console.log(
|
|
3594
|
-
console.log(
|
|
3595
|
-
console.log(
|
|
3596
|
-
console.log(` ${
|
|
3951
|
+
console.log(chalk4.bold.green(" Setup complete! Your coding agent is now configured."));
|
|
3952
|
+
console.log(chalk4.dim(" Run `caliber undo` to revert changes.\n"));
|
|
3953
|
+
console.log(chalk4.bold(" Next steps:\n"));
|
|
3954
|
+
console.log(` ${chalk4.hex("#6366f1")("caliber undo")} Revert all changes from this run`);
|
|
3597
3955
|
console.log("");
|
|
3598
3956
|
}
|
|
3599
3957
|
async function refineLoop(currentSetup, _targetAgent) {
|
|
3600
3958
|
const history = [];
|
|
3601
3959
|
while (true) {
|
|
3602
|
-
const message = await
|
|
3960
|
+
const message = await promptInput2("\nWhat would you like to change?");
|
|
3603
3961
|
if (!message || message.toLowerCase() === "done" || message.toLowerCase() === "accept") {
|
|
3604
3962
|
return currentSetup;
|
|
3605
3963
|
}
|
|
@@ -3621,24 +3979,24 @@ async function refineLoop(currentSetup, _targetAgent) {
|
|
|
3621
3979
|
history.push({ role: "assistant", content: JSON.stringify(refined) });
|
|
3622
3980
|
refineSpinner.succeed("Setup updated");
|
|
3623
3981
|
printSetupSummary(refined);
|
|
3624
|
-
console.log(
|
|
3982
|
+
console.log(chalk4.dim('Type "done" to accept, or describe more changes.'));
|
|
3625
3983
|
} else {
|
|
3626
3984
|
refineSpinner.fail("Refinement failed \u2014 could not parse AI response.");
|
|
3627
|
-
console.log(
|
|
3985
|
+
console.log(chalk4.dim('Try rephrasing your request, or type "done" to keep the current setup.'));
|
|
3628
3986
|
}
|
|
3629
3987
|
}
|
|
3630
3988
|
}
|
|
3631
|
-
function
|
|
3632
|
-
const rl =
|
|
3989
|
+
function promptInput2(question) {
|
|
3990
|
+
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
3633
3991
|
return new Promise((resolve2) => {
|
|
3634
|
-
rl.question(
|
|
3992
|
+
rl.question(chalk4.cyan(`${question} `), (answer) => {
|
|
3635
3993
|
rl.close();
|
|
3636
3994
|
resolve2(answer.trim());
|
|
3637
3995
|
});
|
|
3638
3996
|
});
|
|
3639
3997
|
}
|
|
3640
3998
|
async function promptAgent() {
|
|
3641
|
-
return
|
|
3999
|
+
return select2({
|
|
3642
4000
|
message: "Which coding agent are you using?",
|
|
3643
4001
|
choices: [
|
|
3644
4002
|
{ name: "Claude Code", value: "claude" },
|
|
@@ -3657,13 +4015,13 @@ async function promptHookType(targetAgent) {
|
|
|
3657
4015
|
choices.push({ name: "Both (Claude Code + pre-commit)", value: "both" });
|
|
3658
4016
|
}
|
|
3659
4017
|
choices.push({ name: "Skip for now", value: "skip" });
|
|
3660
|
-
return
|
|
4018
|
+
return select2({
|
|
3661
4019
|
message: "How would you like to auto-refresh your docs?",
|
|
3662
4020
|
choices
|
|
3663
4021
|
});
|
|
3664
4022
|
}
|
|
3665
4023
|
async function promptWantsReview() {
|
|
3666
|
-
const answer = await
|
|
4024
|
+
const answer = await select2({
|
|
3667
4025
|
message: "Would you like to review the diffs before deciding?",
|
|
3668
4026
|
choices: [
|
|
3669
4027
|
{ name: "Yes, show me the diffs", value: true },
|
|
@@ -3685,7 +4043,7 @@ async function promptReviewMethod() {
|
|
|
3685
4043
|
return { name: "Terminal", value: "terminal" };
|
|
3686
4044
|
}
|
|
3687
4045
|
});
|
|
3688
|
-
return
|
|
4046
|
+
return select2({ message: "How would you like to review the changes?", choices });
|
|
3689
4047
|
}
|
|
3690
4048
|
function openReview(method, stagedFiles) {
|
|
3691
4049
|
if (method === "cursor" || method === "vscode") {
|
|
@@ -3693,7 +4051,7 @@ function openReview(method, stagedFiles) {
|
|
|
3693
4051
|
originalPath: f.originalPath,
|
|
3694
4052
|
proposedPath: f.proposedPath
|
|
3695
4053
|
})));
|
|
3696
|
-
console.log(
|
|
4054
|
+
console.log(chalk4.dim(" Diffs opened in your editor.\n"));
|
|
3697
4055
|
} else {
|
|
3698
4056
|
for (const file of stagedFiles) {
|
|
3699
4057
|
if (file.currentPath) {
|
|
@@ -3705,19 +4063,19 @@ function openReview(method, stagedFiles) {
|
|
|
3705
4063
|
if (line.startsWith("+") && !line.startsWith("+++")) added++;
|
|
3706
4064
|
if (line.startsWith("-") && !line.startsWith("---")) removed++;
|
|
3707
4065
|
}
|
|
3708
|
-
console.log(` ${
|
|
4066
|
+
console.log(` ${chalk4.yellow("~")} ${file.relativePath} ${chalk4.green(`+${added}`)} ${chalk4.red(`-${removed}`)}`);
|
|
3709
4067
|
} else {
|
|
3710
4068
|
const lines = fs17.readFileSync(file.proposedPath, "utf-8").split("\n").length;
|
|
3711
|
-
console.log(` ${
|
|
4069
|
+
console.log(` ${chalk4.green("+")} ${file.relativePath} ${chalk4.dim(`${lines} lines`)}`);
|
|
3712
4070
|
}
|
|
3713
4071
|
}
|
|
3714
4072
|
console.log("");
|
|
3715
|
-
console.log(
|
|
4073
|
+
console.log(chalk4.dim(` Files staged at .caliber/staged/ for manual inspection.
|
|
3716
4074
|
`));
|
|
3717
4075
|
}
|
|
3718
4076
|
}
|
|
3719
4077
|
async function promptReviewAction() {
|
|
3720
|
-
return
|
|
4078
|
+
return select2({
|
|
3721
4079
|
message: "What would you like to do?",
|
|
3722
4080
|
choices: [
|
|
3723
4081
|
{ name: "Accept and apply", value: "accept" },
|
|
@@ -3732,46 +4090,46 @@ function printSetupSummary(setup) {
|
|
|
3732
4090
|
const fileDescriptions = setup.fileDescriptions;
|
|
3733
4091
|
const deletions = setup.deletions;
|
|
3734
4092
|
console.log("");
|
|
3735
|
-
console.log(
|
|
4093
|
+
console.log(chalk4.bold(" Proposed changes:\n"));
|
|
3736
4094
|
const getDescription = (filePath) => {
|
|
3737
4095
|
return fileDescriptions?.[filePath];
|
|
3738
4096
|
};
|
|
3739
4097
|
if (claude) {
|
|
3740
4098
|
if (claude.claudeMd) {
|
|
3741
|
-
const icon = fs17.existsSync("CLAUDE.md") ?
|
|
4099
|
+
const icon = fs17.existsSync("CLAUDE.md") ? chalk4.yellow("~") : chalk4.green("+");
|
|
3742
4100
|
const desc = getDescription("CLAUDE.md");
|
|
3743
|
-
console.log(` ${icon} ${
|
|
3744
|
-
if (desc) console.log(
|
|
4101
|
+
console.log(` ${icon} ${chalk4.bold("CLAUDE.md")}`);
|
|
4102
|
+
if (desc) console.log(chalk4.dim(` ${desc}`));
|
|
3745
4103
|
console.log("");
|
|
3746
4104
|
}
|
|
3747
4105
|
const skills = claude.skills;
|
|
3748
4106
|
if (Array.isArray(skills) && skills.length > 0) {
|
|
3749
4107
|
for (const skill of skills) {
|
|
3750
4108
|
const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
|
|
3751
|
-
const icon = fs17.existsSync(skillPath) ?
|
|
4109
|
+
const icon = fs17.existsSync(skillPath) ? chalk4.yellow("~") : chalk4.green("+");
|
|
3752
4110
|
const desc = getDescription(skillPath);
|
|
3753
|
-
console.log(` ${icon} ${
|
|
3754
|
-
console.log(
|
|
4111
|
+
console.log(` ${icon} ${chalk4.bold(skillPath)}`);
|
|
4112
|
+
console.log(chalk4.dim(` ${desc || skill.description || skill.name}`));
|
|
3755
4113
|
console.log("");
|
|
3756
4114
|
}
|
|
3757
4115
|
}
|
|
3758
4116
|
}
|
|
3759
4117
|
if (cursor) {
|
|
3760
4118
|
if (cursor.cursorrules) {
|
|
3761
|
-
const icon = fs17.existsSync(".cursorrules") ?
|
|
4119
|
+
const icon = fs17.existsSync(".cursorrules") ? chalk4.yellow("~") : chalk4.green("+");
|
|
3762
4120
|
const desc = getDescription(".cursorrules");
|
|
3763
|
-
console.log(` ${icon} ${
|
|
3764
|
-
if (desc) console.log(
|
|
4121
|
+
console.log(` ${icon} ${chalk4.bold(".cursorrules")}`);
|
|
4122
|
+
if (desc) console.log(chalk4.dim(` ${desc}`));
|
|
3765
4123
|
console.log("");
|
|
3766
4124
|
}
|
|
3767
4125
|
const cursorSkills = cursor.skills;
|
|
3768
4126
|
if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
|
|
3769
4127
|
for (const skill of cursorSkills) {
|
|
3770
4128
|
const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
|
|
3771
|
-
const icon = fs17.existsSync(skillPath) ?
|
|
4129
|
+
const icon = fs17.existsSync(skillPath) ? chalk4.yellow("~") : chalk4.green("+");
|
|
3772
4130
|
const desc = getDescription(skillPath);
|
|
3773
|
-
console.log(` ${icon} ${
|
|
3774
|
-
console.log(
|
|
4131
|
+
console.log(` ${icon} ${chalk4.bold(skillPath)}`);
|
|
4132
|
+
console.log(chalk4.dim(` ${desc || skill.description || skill.name}`));
|
|
3775
4133
|
console.log("");
|
|
3776
4134
|
}
|
|
3777
4135
|
}
|
|
@@ -3779,14 +4137,14 @@ function printSetupSummary(setup) {
|
|
|
3779
4137
|
if (Array.isArray(rules) && rules.length > 0) {
|
|
3780
4138
|
for (const rule of rules) {
|
|
3781
4139
|
const rulePath = `.cursor/rules/${rule.filename}`;
|
|
3782
|
-
const icon = fs17.existsSync(rulePath) ?
|
|
4140
|
+
const icon = fs17.existsSync(rulePath) ? chalk4.yellow("~") : chalk4.green("+");
|
|
3783
4141
|
const desc = getDescription(rulePath);
|
|
3784
|
-
console.log(` ${icon} ${
|
|
4142
|
+
console.log(` ${icon} ${chalk4.bold(rulePath)}`);
|
|
3785
4143
|
if (desc) {
|
|
3786
|
-
console.log(
|
|
4144
|
+
console.log(chalk4.dim(` ${desc}`));
|
|
3787
4145
|
} else {
|
|
3788
4146
|
const firstLine = rule.content.split("\n").filter((l) => l.trim() && !l.trim().startsWith("#"))[0];
|
|
3789
|
-
if (firstLine) console.log(
|
|
4147
|
+
if (firstLine) console.log(chalk4.dim(` ${firstLine.trim().slice(0, 80)}`));
|
|
3790
4148
|
}
|
|
3791
4149
|
console.log("");
|
|
3792
4150
|
}
|
|
@@ -3794,12 +4152,12 @@ function printSetupSummary(setup) {
|
|
|
3794
4152
|
}
|
|
3795
4153
|
if (Array.isArray(deletions) && deletions.length > 0) {
|
|
3796
4154
|
for (const del of deletions) {
|
|
3797
|
-
console.log(` ${
|
|
3798
|
-
console.log(
|
|
4155
|
+
console.log(` ${chalk4.red("-")} ${chalk4.bold(del.filePath)}`);
|
|
4156
|
+
console.log(chalk4.dim(` ${del.reason}`));
|
|
3799
4157
|
console.log("");
|
|
3800
4158
|
}
|
|
3801
4159
|
}
|
|
3802
|
-
console.log(` ${
|
|
4160
|
+
console.log(` ${chalk4.green("+")} ${chalk4.dim("new")} ${chalk4.yellow("~")} ${chalk4.dim("modified")} ${chalk4.red("-")} ${chalk4.dim("removed")}`);
|
|
3803
4161
|
console.log("");
|
|
3804
4162
|
}
|
|
3805
4163
|
function buildSkillContent(skill) {
|
|
@@ -3865,7 +4223,7 @@ function collectSetupFiles(setup) {
|
|
|
3865
4223
|
}
|
|
3866
4224
|
|
|
3867
4225
|
// src/commands/undo.ts
|
|
3868
|
-
import
|
|
4226
|
+
import chalk5 from "chalk";
|
|
3869
4227
|
import ora2 from "ora";
|
|
3870
4228
|
function undoCommand() {
|
|
3871
4229
|
const spinner = ora2("Reverting setup...").start();
|
|
@@ -3877,26 +4235,26 @@ function undoCommand() {
|
|
|
3877
4235
|
}
|
|
3878
4236
|
spinner.succeed("Setup reverted successfully.\n");
|
|
3879
4237
|
if (restored.length > 0) {
|
|
3880
|
-
console.log(
|
|
4238
|
+
console.log(chalk5.cyan(" Restored from backup:"));
|
|
3881
4239
|
for (const file of restored) {
|
|
3882
|
-
console.log(` ${
|
|
4240
|
+
console.log(` ${chalk5.green("\u21A9")} ${file}`);
|
|
3883
4241
|
}
|
|
3884
4242
|
}
|
|
3885
4243
|
if (removed.length > 0) {
|
|
3886
|
-
console.log(
|
|
4244
|
+
console.log(chalk5.cyan(" Removed:"));
|
|
3887
4245
|
for (const file of removed) {
|
|
3888
|
-
console.log(` ${
|
|
4246
|
+
console.log(` ${chalk5.red("\u2717")} ${file}`);
|
|
3889
4247
|
}
|
|
3890
4248
|
}
|
|
3891
4249
|
console.log("");
|
|
3892
4250
|
} catch (err) {
|
|
3893
|
-
spinner.fail(
|
|
4251
|
+
spinner.fail(chalk5.red(err instanceof Error ? err.message : "Undo failed"));
|
|
3894
4252
|
throw new Error("__exit__");
|
|
3895
4253
|
}
|
|
3896
4254
|
}
|
|
3897
4255
|
|
|
3898
4256
|
// src/commands/status.ts
|
|
3899
|
-
import
|
|
4257
|
+
import chalk6 from "chalk";
|
|
3900
4258
|
import fs18 from "fs";
|
|
3901
4259
|
async function statusCommand(options) {
|
|
3902
4260
|
const config = loadConfig();
|
|
@@ -3910,39 +4268,39 @@ async function statusCommand(options) {
|
|
|
3910
4268
|
}, null, 2));
|
|
3911
4269
|
return;
|
|
3912
4270
|
}
|
|
3913
|
-
console.log(
|
|
4271
|
+
console.log(chalk6.bold("\nCaliber Status\n"));
|
|
3914
4272
|
if (config) {
|
|
3915
|
-
console.log(` LLM: ${
|
|
4273
|
+
console.log(` LLM: ${chalk6.green(config.provider)} (${config.model})`);
|
|
3916
4274
|
} else {
|
|
3917
|
-
console.log(` LLM: ${
|
|
4275
|
+
console.log(` LLM: ${chalk6.yellow("Not configured")} \u2014 run \`caliber config\``);
|
|
3918
4276
|
}
|
|
3919
4277
|
if (!manifest) {
|
|
3920
|
-
console.log(` Setup: ${
|
|
3921
|
-
console.log(
|
|
4278
|
+
console.log(` Setup: ${chalk6.dim("No setup applied")}`);
|
|
4279
|
+
console.log(chalk6.dim("\n Run `caliber init` to get started.\n"));
|
|
3922
4280
|
return;
|
|
3923
4281
|
}
|
|
3924
|
-
console.log(` Files managed: ${
|
|
4282
|
+
console.log(` Files managed: ${chalk6.cyan(manifest.entries.length.toString())}`);
|
|
3925
4283
|
for (const entry of manifest.entries) {
|
|
3926
4284
|
const exists = fs18.existsSync(entry.path);
|
|
3927
|
-
const icon = exists ?
|
|
4285
|
+
const icon = exists ? chalk6.green("\u2713") : chalk6.red("\u2717");
|
|
3928
4286
|
console.log(` ${icon} ${entry.path} (${entry.action})`);
|
|
3929
4287
|
}
|
|
3930
4288
|
console.log("");
|
|
3931
4289
|
}
|
|
3932
4290
|
|
|
3933
4291
|
// src/commands/regenerate.ts
|
|
3934
|
-
import
|
|
4292
|
+
import chalk7 from "chalk";
|
|
3935
4293
|
import ora3 from "ora";
|
|
3936
4294
|
import confirm from "@inquirer/confirm";
|
|
3937
4295
|
async function regenerateCommand(options) {
|
|
3938
4296
|
const config = loadConfig();
|
|
3939
4297
|
if (!config) {
|
|
3940
|
-
console.log(
|
|
4298
|
+
console.log(chalk7.red("No LLM provider configured. Run `caliber config` (e.g. choose Cursor) or set ANTHROPIC_API_KEY."));
|
|
3941
4299
|
throw new Error("__exit__");
|
|
3942
4300
|
}
|
|
3943
4301
|
const manifest = readManifest();
|
|
3944
4302
|
if (!manifest) {
|
|
3945
|
-
console.log(
|
|
4303
|
+
console.log(chalk7.yellow("No existing setup found. Run `caliber init` first."));
|
|
3946
4304
|
throw new Error("__exit__");
|
|
3947
4305
|
}
|
|
3948
4306
|
const spinner = ora3("Re-analyzing project...").start();
|
|
@@ -3985,26 +4343,26 @@ async function regenerateCommand(options) {
|
|
|
3985
4343
|
}
|
|
3986
4344
|
genSpinner.succeed("Setup regenerated");
|
|
3987
4345
|
if (options.dryRun) {
|
|
3988
|
-
console.log(
|
|
4346
|
+
console.log(chalk7.yellow("\n[Dry run] Would write:"));
|
|
3989
4347
|
console.log(JSON.stringify(generatedSetup, null, 2));
|
|
3990
4348
|
return;
|
|
3991
4349
|
}
|
|
3992
4350
|
const shouldApply = await confirm({ message: "Apply regenerated setup?", default: true });
|
|
3993
4351
|
if (!shouldApply) {
|
|
3994
|
-
console.log(
|
|
4352
|
+
console.log(chalk7.dim("Regeneration cancelled."));
|
|
3995
4353
|
return;
|
|
3996
4354
|
}
|
|
3997
4355
|
const writeSpinner = ora3("Updating config files...").start();
|
|
3998
4356
|
const result = writeSetup(generatedSetup);
|
|
3999
4357
|
writeSpinner.succeed("Config files updated");
|
|
4000
4358
|
for (const file of result.written) {
|
|
4001
|
-
console.log(` ${
|
|
4359
|
+
console.log(` ${chalk7.green("\u2713")} ${file}`);
|
|
4002
4360
|
}
|
|
4003
4361
|
console.log("");
|
|
4004
4362
|
}
|
|
4005
4363
|
|
|
4006
4364
|
// src/commands/recommend.ts
|
|
4007
|
-
import
|
|
4365
|
+
import chalk8 from "chalk";
|
|
4008
4366
|
import ora4 from "ora";
|
|
4009
4367
|
import { mkdirSync, readFileSync as readFileSync7, existsSync as existsSync9, writeFileSync } from "fs";
|
|
4010
4368
|
import { join as join8, dirname as dirname2 } from "path";
|
|
@@ -4187,7 +4545,7 @@ async function recommendCommand(options) {
|
|
|
4187
4545
|
...extractTopDeps()
|
|
4188
4546
|
].filter(Boolean))];
|
|
4189
4547
|
if (technologies.length === 0) {
|
|
4190
|
-
console.log(
|
|
4548
|
+
console.log(chalk8.yellow("Could not detect any languages or dependencies. Try running from a project root."));
|
|
4191
4549
|
throw new Error("__exit__");
|
|
4192
4550
|
}
|
|
4193
4551
|
const spinner = ora4("Searching for skills...").start();
|
|
@@ -4213,18 +4571,18 @@ async function interactiveSelect(recs) {
|
|
|
4213
4571
|
let lineCount = 0;
|
|
4214
4572
|
function render() {
|
|
4215
4573
|
const lines = [];
|
|
4216
|
-
lines.push(
|
|
4574
|
+
lines.push(chalk8.bold(" Recommendations"));
|
|
4217
4575
|
lines.push("");
|
|
4218
|
-
lines.push(` ${
|
|
4219
|
-
lines.push(
|
|
4576
|
+
lines.push(` ${chalk8.dim("Name".padEnd(30))} ${chalk8.dim("Technology".padEnd(18))} ${chalk8.dim("Source")}`);
|
|
4577
|
+
lines.push(chalk8.dim(" " + "\u2500".repeat(70)));
|
|
4220
4578
|
for (let i = 0; i < recs.length; i++) {
|
|
4221
4579
|
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)} ${
|
|
4580
|
+
const check = selected.has(i) ? chalk8.green("[x]") : "[ ]";
|
|
4581
|
+
const ptr = i === cursor ? chalk8.cyan("\u276F") : " ";
|
|
4582
|
+
lines.push(` ${ptr} ${check} ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${chalk8.dim(rec.source_url || "")}`);
|
|
4225
4583
|
}
|
|
4226
4584
|
lines.push("");
|
|
4227
|
-
lines.push(
|
|
4585
|
+
lines.push(chalk8.dim(" \u2191\u2193 navigate \u23B5 toggle a all n none \u23CE install q cancel"));
|
|
4228
4586
|
return lines.join("\n");
|
|
4229
4587
|
}
|
|
4230
4588
|
function draw(initial) {
|
|
@@ -4273,7 +4631,7 @@ async function interactiveSelect(recs) {
|
|
|
4273
4631
|
case "\n":
|
|
4274
4632
|
cleanup();
|
|
4275
4633
|
if (selected.size === 0) {
|
|
4276
|
-
console.log(
|
|
4634
|
+
console.log(chalk8.dim("\n No skills selected.\n"));
|
|
4277
4635
|
resolve2(null);
|
|
4278
4636
|
} else {
|
|
4279
4637
|
resolve2(Array.from(selected).sort().map((i) => recs[i]));
|
|
@@ -4283,7 +4641,7 @@ async function interactiveSelect(recs) {
|
|
|
4283
4641
|
case "\x1B":
|
|
4284
4642
|
case "":
|
|
4285
4643
|
cleanup();
|
|
4286
|
-
console.log(
|
|
4644
|
+
console.log(chalk8.dim("\n Cancelled.\n"));
|
|
4287
4645
|
resolve2(null);
|
|
4288
4646
|
break;
|
|
4289
4647
|
}
|
|
@@ -4323,35 +4681,35 @@ async function installSkills(recs, platforms) {
|
|
|
4323
4681
|
if (installed.length > 0) {
|
|
4324
4682
|
spinner.succeed(`Installed ${installed.length} file${installed.length > 1 ? "s" : ""}`);
|
|
4325
4683
|
for (const p of installed) {
|
|
4326
|
-
console.log(
|
|
4684
|
+
console.log(chalk8.green(` \u2713 ${p}`));
|
|
4327
4685
|
}
|
|
4328
4686
|
} else {
|
|
4329
4687
|
spinner.fail("No skills were installed");
|
|
4330
4688
|
}
|
|
4331
4689
|
for (const w of warnings) {
|
|
4332
|
-
console.log(
|
|
4690
|
+
console.log(chalk8.yellow(` \u26A0 ${w}`));
|
|
4333
4691
|
}
|
|
4334
4692
|
console.log("");
|
|
4335
4693
|
}
|
|
4336
4694
|
function printRecommendations(recs) {
|
|
4337
|
-
console.log(
|
|
4695
|
+
console.log(chalk8.bold("\n Recommendations\n"));
|
|
4338
4696
|
console.log(
|
|
4339
|
-
` ${
|
|
4697
|
+
` ${chalk8.dim("Name".padEnd(30))} ${chalk8.dim("Technology".padEnd(18))} ${chalk8.dim("Source")}`
|
|
4340
4698
|
);
|
|
4341
|
-
console.log(
|
|
4699
|
+
console.log(chalk8.dim(" " + "\u2500".repeat(70)));
|
|
4342
4700
|
for (const rec of recs) {
|
|
4343
4701
|
console.log(
|
|
4344
|
-
` ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${
|
|
4702
|
+
` ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${chalk8.dim(rec.source_url || "")}`
|
|
4345
4703
|
);
|
|
4346
4704
|
if (rec.reason) {
|
|
4347
|
-
console.log(` ${
|
|
4705
|
+
console.log(` ${chalk8.dim(" " + rec.reason)}`);
|
|
4348
4706
|
}
|
|
4349
4707
|
}
|
|
4350
4708
|
console.log("");
|
|
4351
4709
|
}
|
|
4352
4710
|
|
|
4353
4711
|
// src/commands/score.ts
|
|
4354
|
-
import
|
|
4712
|
+
import chalk9 from "chalk";
|
|
4355
4713
|
async function scoreCommand(options) {
|
|
4356
4714
|
const dir = process.cwd();
|
|
4357
4715
|
const target = options.agent ?? readState()?.targetAgent;
|
|
@@ -4365,14 +4723,14 @@ async function scoreCommand(options) {
|
|
|
4365
4723
|
return;
|
|
4366
4724
|
}
|
|
4367
4725
|
displayScore(result);
|
|
4368
|
-
const separator =
|
|
4726
|
+
const separator = chalk9.gray(" " + "\u2500".repeat(53));
|
|
4369
4727
|
console.log(separator);
|
|
4370
4728
|
if (result.score < 40) {
|
|
4371
|
-
console.log(
|
|
4729
|
+
console.log(chalk9.gray(" Run ") + chalk9.hex("#f97316")("caliber init") + chalk9.gray(" to generate a complete, optimized setup."));
|
|
4372
4730
|
} else if (result.score < 70) {
|
|
4373
|
-
console.log(
|
|
4731
|
+
console.log(chalk9.gray(" Run ") + chalk9.hex("#f97316")("caliber init") + chalk9.gray(" to improve your setup."));
|
|
4374
4732
|
} else {
|
|
4375
|
-
console.log(
|
|
4733
|
+
console.log(chalk9.green(" Looking good!") + chalk9.gray(" Run ") + chalk9.hex("#f97316")("caliber update") + chalk9.gray(" to keep it fresh."));
|
|
4376
4734
|
}
|
|
4377
4735
|
console.log("");
|
|
4378
4736
|
}
|
|
@@ -4380,11 +4738,11 @@ async function scoreCommand(options) {
|
|
|
4380
4738
|
// src/commands/refresh.ts
|
|
4381
4739
|
import fs21 from "fs";
|
|
4382
4740
|
import path18 from "path";
|
|
4383
|
-
import
|
|
4741
|
+
import chalk10 from "chalk";
|
|
4384
4742
|
import ora5 from "ora";
|
|
4385
4743
|
|
|
4386
4744
|
// src/lib/git-diff.ts
|
|
4387
|
-
import { execSync as
|
|
4745
|
+
import { execSync as execSync8 } from "child_process";
|
|
4388
4746
|
var MAX_DIFF_BYTES = 1e5;
|
|
4389
4747
|
var DOC_PATTERNS = [
|
|
4390
4748
|
"CLAUDE.md",
|
|
@@ -4398,7 +4756,7 @@ function excludeArgs() {
|
|
|
4398
4756
|
}
|
|
4399
4757
|
function safeExec(cmd) {
|
|
4400
4758
|
try {
|
|
4401
|
-
return
|
|
4759
|
+
return execSync8(cmd, {
|
|
4402
4760
|
encoding: "utf-8",
|
|
4403
4761
|
stdio: ["pipe", "pipe", "pipe"],
|
|
4404
4762
|
maxBuffer: 10 * 1024 * 1024
|
|
@@ -4575,7 +4933,7 @@ function discoverGitRepos(parentDir) {
|
|
|
4575
4933
|
}
|
|
4576
4934
|
async function refreshSingleRepo(repoDir, options) {
|
|
4577
4935
|
const quiet = !!options.quiet;
|
|
4578
|
-
const prefix = options.label ? `${
|
|
4936
|
+
const prefix = options.label ? `${chalk10.bold(options.label)} ` : "";
|
|
4579
4937
|
const state = readState();
|
|
4580
4938
|
const lastSha = state?.lastRefreshSha ?? null;
|
|
4581
4939
|
const diff = collectDiff(lastSha);
|
|
@@ -4584,7 +4942,7 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
4584
4942
|
if (currentSha) {
|
|
4585
4943
|
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
4586
4944
|
}
|
|
4587
|
-
log(quiet,
|
|
4945
|
+
log(quiet, chalk10.dim(`${prefix}No changes since last refresh.`));
|
|
4588
4946
|
return;
|
|
4589
4947
|
}
|
|
4590
4948
|
const spinner = quiet ? null : ora5(`${prefix}Analyzing changes...`).start();
|
|
@@ -4616,10 +4974,10 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
4616
4974
|
if (options.dryRun) {
|
|
4617
4975
|
spinner?.info(`${prefix}Dry run \u2014 would update:`);
|
|
4618
4976
|
for (const doc of response.docsUpdated) {
|
|
4619
|
-
console.log(` ${
|
|
4977
|
+
console.log(` ${chalk10.yellow("~")} ${doc}`);
|
|
4620
4978
|
}
|
|
4621
4979
|
if (response.changesSummary) {
|
|
4622
|
-
console.log(
|
|
4980
|
+
console.log(chalk10.dim(`
|
|
4623
4981
|
${response.changesSummary}`));
|
|
4624
4982
|
}
|
|
4625
4983
|
return;
|
|
@@ -4627,10 +4985,10 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
4627
4985
|
const written = writeRefreshDocs(response.updatedDocs);
|
|
4628
4986
|
spinner?.succeed(`${prefix}Updated ${written.length} doc${written.length === 1 ? "" : "s"}`);
|
|
4629
4987
|
for (const file of written) {
|
|
4630
|
-
log(quiet, ` ${
|
|
4988
|
+
log(quiet, ` ${chalk10.green("\u2713")} ${file}`);
|
|
4631
4989
|
}
|
|
4632
4990
|
if (response.changesSummary) {
|
|
4633
|
-
log(quiet,
|
|
4991
|
+
log(quiet, chalk10.dim(`
|
|
4634
4992
|
${response.changesSummary}`));
|
|
4635
4993
|
}
|
|
4636
4994
|
if (currentSha) {
|
|
@@ -4643,7 +5001,7 @@ async function refreshCommand(options) {
|
|
|
4643
5001
|
const config = loadConfig();
|
|
4644
5002
|
if (!config) {
|
|
4645
5003
|
if (quiet) return;
|
|
4646
|
-
console.log(
|
|
5004
|
+
console.log(chalk10.red("No LLM provider configured. Run `caliber config` (e.g. choose Cursor) or set an API key."));
|
|
4647
5005
|
throw new Error("__exit__");
|
|
4648
5006
|
}
|
|
4649
5007
|
if (isGitRepo()) {
|
|
@@ -4653,10 +5011,10 @@ async function refreshCommand(options) {
|
|
|
4653
5011
|
const repos = discoverGitRepos(process.cwd());
|
|
4654
5012
|
if (repos.length === 0) {
|
|
4655
5013
|
if (quiet) return;
|
|
4656
|
-
console.log(
|
|
5014
|
+
console.log(chalk10.red("Not inside a git repository and no git repos found in child directories."));
|
|
4657
5015
|
throw new Error("__exit__");
|
|
4658
5016
|
}
|
|
4659
|
-
log(quiet,
|
|
5017
|
+
log(quiet, chalk10.dim(`Found ${repos.length} git repo${repos.length === 1 ? "" : "s"}
|
|
4660
5018
|
`));
|
|
4661
5019
|
const originalDir = process.cwd();
|
|
4662
5020
|
for (const repo of repos) {
|
|
@@ -4666,7 +5024,7 @@ async function refreshCommand(options) {
|
|
|
4666
5024
|
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
4667
5025
|
} catch (err) {
|
|
4668
5026
|
if (err instanceof Error && err.message === "__exit__") continue;
|
|
4669
|
-
log(quiet,
|
|
5027
|
+
log(quiet, chalk10.yellow(`${repoName}: refresh failed \u2014 ${err instanceof Error ? err.message : "unknown error"}`));
|
|
4670
5028
|
}
|
|
4671
5029
|
}
|
|
4672
5030
|
process.chdir(originalDir);
|
|
@@ -4674,153 +5032,107 @@ async function refreshCommand(options) {
|
|
|
4674
5032
|
if (err instanceof Error && err.message === "__exit__") throw err;
|
|
4675
5033
|
if (quiet) return;
|
|
4676
5034
|
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
4677
|
-
console.log(
|
|
5035
|
+
console.log(chalk10.red(`Refresh failed: ${msg}`));
|
|
4678
5036
|
throw new Error("__exit__");
|
|
4679
5037
|
}
|
|
4680
5038
|
}
|
|
4681
5039
|
|
|
4682
5040
|
// src/commands/hooks.ts
|
|
4683
|
-
import
|
|
5041
|
+
import chalk11 from "chalk";
|
|
4684
5042
|
async function hooksInstallCommand() {
|
|
4685
5043
|
const result = installHook();
|
|
4686
5044
|
if (result.alreadyInstalled) {
|
|
4687
|
-
console.log(
|
|
5045
|
+
console.log(chalk11.dim("Claude Code hook already installed."));
|
|
4688
5046
|
return;
|
|
4689
5047
|
}
|
|
4690
|
-
console.log(
|
|
4691
|
-
console.log(
|
|
5048
|
+
console.log(chalk11.green("\u2713") + " SessionEnd hook installed in .claude/settings.json");
|
|
5049
|
+
console.log(chalk11.dim(" Docs will auto-refresh when Claude Code sessions end."));
|
|
4692
5050
|
}
|
|
4693
5051
|
async function hooksRemoveCommand() {
|
|
4694
5052
|
const result = removeHook();
|
|
4695
5053
|
if (result.notFound) {
|
|
4696
|
-
console.log(
|
|
5054
|
+
console.log(chalk11.dim("Claude Code hook not found."));
|
|
4697
5055
|
return;
|
|
4698
5056
|
}
|
|
4699
|
-
console.log(
|
|
5057
|
+
console.log(chalk11.green("\u2713") + " SessionEnd hook removed from .claude/settings.json");
|
|
4700
5058
|
}
|
|
4701
5059
|
async function hooksInstallPrecommitCommand() {
|
|
4702
5060
|
const result = installPreCommitHook();
|
|
4703
5061
|
if (result.alreadyInstalled) {
|
|
4704
|
-
console.log(
|
|
5062
|
+
console.log(chalk11.dim("Pre-commit hook already installed."));
|
|
4705
5063
|
return;
|
|
4706
5064
|
}
|
|
4707
5065
|
if (!result.installed) {
|
|
4708
|
-
console.log(
|
|
5066
|
+
console.log(chalk11.red("Failed to install pre-commit hook (not a git repository?)."));
|
|
4709
5067
|
return;
|
|
4710
5068
|
}
|
|
4711
|
-
console.log(
|
|
4712
|
-
console.log(
|
|
5069
|
+
console.log(chalk11.green("\u2713") + " Pre-commit hook installed in .git/hooks/pre-commit");
|
|
5070
|
+
console.log(chalk11.dim(" Docs will auto-refresh before each commit via LLM."));
|
|
4713
5071
|
}
|
|
4714
5072
|
async function hooksRemovePrecommitCommand() {
|
|
4715
5073
|
const result = removePreCommitHook();
|
|
4716
5074
|
if (result.notFound) {
|
|
4717
|
-
console.log(
|
|
5075
|
+
console.log(chalk11.dim("Pre-commit hook not found."));
|
|
4718
5076
|
return;
|
|
4719
5077
|
}
|
|
4720
|
-
console.log(
|
|
5078
|
+
console.log(chalk11.green("\u2713") + " Pre-commit hook removed from .git/hooks/pre-commit");
|
|
4721
5079
|
}
|
|
4722
5080
|
async function hooksStatusCommand() {
|
|
4723
5081
|
const claudeInstalled = isHookInstalled();
|
|
4724
5082
|
const precommitInstalled = isPreCommitHookInstalled();
|
|
4725
5083
|
if (claudeInstalled) {
|
|
4726
|
-
console.log(
|
|
5084
|
+
console.log(chalk11.green("\u2713") + " Claude Code hook is " + chalk11.green("installed"));
|
|
4727
5085
|
} else {
|
|
4728
|
-
console.log(
|
|
5086
|
+
console.log(chalk11.dim("\u2717") + " Claude Code hook is " + chalk11.yellow("not installed"));
|
|
4729
5087
|
}
|
|
4730
5088
|
if (precommitInstalled) {
|
|
4731
|
-
console.log(
|
|
5089
|
+
console.log(chalk11.green("\u2713") + " Pre-commit hook is " + chalk11.green("installed"));
|
|
4732
5090
|
} else {
|
|
4733
|
-
console.log(
|
|
5091
|
+
console.log(chalk11.dim("\u2717") + " Pre-commit hook is " + chalk11.yellow("not installed"));
|
|
4734
5092
|
}
|
|
4735
5093
|
if (!claudeInstalled && !precommitInstalled) {
|
|
4736
|
-
console.log(
|
|
5094
|
+
console.log(chalk11.dim("\n Run `caliber hooks install` or `caliber hooks install-precommit` to enable auto-refresh."));
|
|
4737
5095
|
}
|
|
4738
5096
|
}
|
|
4739
5097
|
|
|
4740
5098
|
// 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
|
-
}
|
|
5099
|
+
import chalk12 from "chalk";
|
|
4753
5100
|
async function configCommand() {
|
|
4754
5101
|
const existing = loadConfig();
|
|
4755
5102
|
if (existing) {
|
|
4756
|
-
console.log(
|
|
4757
|
-
console.log(` Provider: ${
|
|
4758
|
-
console.log(` Model: ${
|
|
5103
|
+
console.log(chalk12.bold("\nCurrent Configuration\n"));
|
|
5104
|
+
console.log(` Provider: ${chalk12.cyan(existing.provider)}`);
|
|
5105
|
+
console.log(` Model: ${chalk12.cyan(existing.model)}`);
|
|
4759
5106
|
if (existing.apiKey) {
|
|
4760
5107
|
const masked = existing.apiKey.slice(0, 8) + "..." + existing.apiKey.slice(-4);
|
|
4761
|
-
console.log(` API Key: ${
|
|
5108
|
+
console.log(` API Key: ${chalk12.dim(masked)}`);
|
|
5109
|
+
}
|
|
5110
|
+
if (existing.provider === "cursor") {
|
|
5111
|
+
console.log(` Seat: ${chalk12.dim("Cursor (agent acp)")}`);
|
|
5112
|
+
}
|
|
5113
|
+
if (existing.provider === "claude-cli") {
|
|
5114
|
+
console.log(` Seat: ${chalk12.dim("Claude Code (claude -p)")}`);
|
|
4762
5115
|
}
|
|
4763
5116
|
if (existing.baseUrl) {
|
|
4764
|
-
console.log(` Base URL: ${
|
|
5117
|
+
console.log(` Base URL: ${chalk12.dim(existing.baseUrl)}`);
|
|
4765
5118
|
}
|
|
4766
5119
|
if (existing.vertexProjectId) {
|
|
4767
|
-
console.log(` Vertex Project: ${
|
|
4768
|
-
console.log(` Vertex Region: ${
|
|
5120
|
+
console.log(` Vertex Project: ${chalk12.dim(existing.vertexProjectId)}`);
|
|
5121
|
+
console.log(` Vertex Region: ${chalk12.dim(existing.vertexRegion || "us-east5")}`);
|
|
4769
5122
|
}
|
|
4770
|
-
console.log(` Source: ${
|
|
5123
|
+
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
5124
|
console.log("");
|
|
4772
5125
|
}
|
|
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()}
|
|
5126
|
+
await runInteractiveProviderSetup();
|
|
5127
|
+
console.log(chalk12.green("\n\u2713 Configuration saved"));
|
|
5128
|
+
console.log(chalk12.dim(` ${getConfigFilePath()}
|
|
4817
5129
|
`));
|
|
4818
|
-
console.log(
|
|
4819
|
-
console.log(
|
|
5130
|
+
console.log(chalk12.dim(" You can also set environment variables instead:"));
|
|
5131
|
+
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
5132
|
}
|
|
4821
5133
|
|
|
4822
5134
|
// src/commands/learn.ts
|
|
4823
|
-
import
|
|
5135
|
+
import chalk13 from "chalk";
|
|
4824
5136
|
|
|
4825
5137
|
// src/learner/stdin.ts
|
|
4826
5138
|
var STDIN_TIMEOUT_MS = 5e3;
|
|
@@ -5121,46 +5433,46 @@ async function learnFinalizeCommand() {
|
|
|
5121
5433
|
async function learnInstallCommand() {
|
|
5122
5434
|
const result = installLearningHooks();
|
|
5123
5435
|
if (result.alreadyInstalled) {
|
|
5124
|
-
console.log(
|
|
5436
|
+
console.log(chalk13.dim("Learning hooks already installed."));
|
|
5125
5437
|
return;
|
|
5126
5438
|
}
|
|
5127
|
-
console.log(
|
|
5128
|
-
console.log(
|
|
5129
|
-
console.log(
|
|
5439
|
+
console.log(chalk13.green("\u2713") + " Learning hooks installed in .claude/settings.json");
|
|
5440
|
+
console.log(chalk13.dim(" PostToolUse, PostToolUseFailure, and SessionEnd hooks active."));
|
|
5441
|
+
console.log(chalk13.dim(" Session learnings will be written to CLAUDE.md and skills."));
|
|
5130
5442
|
}
|
|
5131
5443
|
async function learnRemoveCommand() {
|
|
5132
5444
|
const result = removeLearningHooks();
|
|
5133
5445
|
if (result.notFound) {
|
|
5134
|
-
console.log(
|
|
5446
|
+
console.log(chalk13.dim("Learning hooks not found."));
|
|
5135
5447
|
return;
|
|
5136
5448
|
}
|
|
5137
|
-
console.log(
|
|
5449
|
+
console.log(chalk13.green("\u2713") + " Learning hooks removed from .claude/settings.json");
|
|
5138
5450
|
}
|
|
5139
5451
|
async function learnStatusCommand() {
|
|
5140
5452
|
const installed = areLearningHooksInstalled();
|
|
5141
5453
|
const state = readState2();
|
|
5142
5454
|
const eventCount = getEventCount();
|
|
5143
|
-
console.log(
|
|
5455
|
+
console.log(chalk13.bold("Session Learning Status"));
|
|
5144
5456
|
console.log();
|
|
5145
5457
|
if (installed) {
|
|
5146
|
-
console.log(
|
|
5458
|
+
console.log(chalk13.green("\u2713") + " Learning hooks are " + chalk13.green("installed"));
|
|
5147
5459
|
} else {
|
|
5148
|
-
console.log(
|
|
5149
|
-
console.log(
|
|
5460
|
+
console.log(chalk13.dim("\u2717") + " Learning hooks are " + chalk13.yellow("not installed"));
|
|
5461
|
+
console.log(chalk13.dim(" Run `caliber learn install` to enable session learning."));
|
|
5150
5462
|
}
|
|
5151
5463
|
console.log();
|
|
5152
|
-
console.log(`Events recorded: ${
|
|
5153
|
-
console.log(`Total this session: ${
|
|
5464
|
+
console.log(`Events recorded: ${chalk13.cyan(String(eventCount))}`);
|
|
5465
|
+
console.log(`Total this session: ${chalk13.cyan(String(state.eventCount))}`);
|
|
5154
5466
|
if (state.lastAnalysisTimestamp) {
|
|
5155
|
-
console.log(`Last analysis: ${
|
|
5467
|
+
console.log(`Last analysis: ${chalk13.cyan(state.lastAnalysisTimestamp)}`);
|
|
5156
5468
|
} else {
|
|
5157
|
-
console.log(`Last analysis: ${
|
|
5469
|
+
console.log(`Last analysis: ${chalk13.dim("none")}`);
|
|
5158
5470
|
}
|
|
5159
5471
|
const learnedSection = readLearnedSection();
|
|
5160
5472
|
if (learnedSection) {
|
|
5161
5473
|
const lineCount = learnedSection.split("\n").filter(Boolean).length;
|
|
5162
5474
|
console.log(`
|
|
5163
|
-
Learned items in CLAUDE.md: ${
|
|
5475
|
+
Learned items in CLAUDE.md: ${chalk13.cyan(String(lineCount))}`);
|
|
5164
5476
|
}
|
|
5165
5477
|
}
|
|
5166
5478
|
|
|
@@ -5197,8 +5509,8 @@ learn.command("status").description("Show learning system status").action(learnS
|
|
|
5197
5509
|
import fs25 from "fs";
|
|
5198
5510
|
import path22 from "path";
|
|
5199
5511
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5200
|
-
import { execSync as
|
|
5201
|
-
import
|
|
5512
|
+
import { execSync as execSync9 } from "child_process";
|
|
5513
|
+
import chalk14 from "chalk";
|
|
5202
5514
|
import ora6 from "ora";
|
|
5203
5515
|
import confirm2 from "@inquirer/confirm";
|
|
5204
5516
|
var __dirname_vc = path22.dirname(fileURLToPath2(import.meta.url));
|
|
@@ -5207,7 +5519,7 @@ var pkg2 = JSON.parse(
|
|
|
5207
5519
|
);
|
|
5208
5520
|
function getInstalledVersion() {
|
|
5209
5521
|
try {
|
|
5210
|
-
const globalRoot =
|
|
5522
|
+
const globalRoot = execSync9("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
5211
5523
|
const pkgPath = path22.join(globalRoot, "@rely-ai", "caliber", "package.json");
|
|
5212
5524
|
return JSON.parse(fs25.readFileSync(pkgPath, "utf-8")).version;
|
|
5213
5525
|
} catch {
|
|
@@ -5232,17 +5544,17 @@ async function checkForUpdates() {
|
|
|
5232
5544
|
const isInteractive = process.stdin.isTTY === true;
|
|
5233
5545
|
if (!isInteractive) {
|
|
5234
5546
|
console.log(
|
|
5235
|
-
|
|
5547
|
+
chalk14.yellow(
|
|
5236
5548
|
`
|
|
5237
5549
|
Update available: ${current} -> ${latest}
|
|
5238
|
-
Run ${
|
|
5550
|
+
Run ${chalk14.bold("npm install -g @rely-ai/caliber")} to upgrade.
|
|
5239
5551
|
`
|
|
5240
5552
|
)
|
|
5241
5553
|
);
|
|
5242
5554
|
return;
|
|
5243
5555
|
}
|
|
5244
5556
|
console.log(
|
|
5245
|
-
|
|
5557
|
+
chalk14.yellow(`
|
|
5246
5558
|
Update available: ${current} -> ${latest}`)
|
|
5247
5559
|
);
|
|
5248
5560
|
const shouldUpdate = await confirm2({ message: "Would you like to update now? (Y/n)", default: true });
|
|
@@ -5252,7 +5564,7 @@ Update available: ${current} -> ${latest}`)
|
|
|
5252
5564
|
}
|
|
5253
5565
|
const spinner = ora6("Updating caliber...").start();
|
|
5254
5566
|
try {
|
|
5255
|
-
|
|
5567
|
+
execSync9(`npm install -g @rely-ai/caliber@${latest}`, {
|
|
5256
5568
|
stdio: "pipe",
|
|
5257
5569
|
timeout: 12e4,
|
|
5258
5570
|
env: { ...process.env, npm_config_fund: "false", npm_config_audit: "false" }
|
|
@@ -5260,16 +5572,16 @@ Update available: ${current} -> ${latest}`)
|
|
|
5260
5572
|
const installed = getInstalledVersion();
|
|
5261
5573
|
if (installed !== latest) {
|
|
5262
5574
|
spinner.fail(`Update incomplete \u2014 got ${installed ?? "unknown"}, expected ${latest}`);
|
|
5263
|
-
console.log(
|
|
5575
|
+
console.log(chalk14.yellow(`Run ${chalk14.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually.
|
|
5264
5576
|
`));
|
|
5265
5577
|
return;
|
|
5266
5578
|
}
|
|
5267
|
-
spinner.succeed(
|
|
5579
|
+
spinner.succeed(chalk14.green(`Updated to ${latest}`));
|
|
5268
5580
|
const args = process.argv.slice(2);
|
|
5269
|
-
console.log(
|
|
5581
|
+
console.log(chalk14.dim(`
|
|
5270
5582
|
Restarting: caliber ${args.join(" ")}
|
|
5271
5583
|
`));
|
|
5272
|
-
|
|
5584
|
+
execSync9(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
|
|
5273
5585
|
stdio: "inherit",
|
|
5274
5586
|
env: { ...process.env, CALIBER_SKIP_UPDATE_CHECK: "1" }
|
|
5275
5587
|
});
|
|
@@ -5279,11 +5591,11 @@ Restarting: caliber ${args.join(" ")}
|
|
|
5279
5591
|
if (err instanceof Error) {
|
|
5280
5592
|
const stderr = err.stderr;
|
|
5281
5593
|
const errMsg = stderr ? String(stderr).trim().split("\n").pop() : err.message.split("\n")[0];
|
|
5282
|
-
if (errMsg && !errMsg.includes("SIGTERM")) console.log(
|
|
5594
|
+
if (errMsg && !errMsg.includes("SIGTERM")) console.log(chalk14.dim(` ${errMsg}`));
|
|
5283
5595
|
}
|
|
5284
5596
|
console.log(
|
|
5285
|
-
|
|
5286
|
-
`Run ${
|
|
5597
|
+
chalk14.yellow(
|
|
5598
|
+
`Run ${chalk14.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually to upgrade.
|
|
5287
5599
|
`
|
|
5288
5600
|
)
|
|
5289
5601
|
);
|