@caliber-ai/cli 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +504 -330
- package/dist/bin.js.map +1 -1
- package/package.json +4 -2
- package/scripts/postinstall.js +11 -0
package/dist/bin.js
CHANGED
|
@@ -75,8 +75,8 @@ if (dsn) {
|
|
|
75
75
|
|
|
76
76
|
// src/cli.ts
|
|
77
77
|
import { Command } from "commander";
|
|
78
|
-
import
|
|
79
|
-
import
|
|
78
|
+
import fs22 from "fs";
|
|
79
|
+
import path19 from "path";
|
|
80
80
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
81
81
|
|
|
82
82
|
// src/commands/init.ts
|
|
@@ -84,8 +84,7 @@ import chalk3 from "chalk";
|
|
|
84
84
|
import ora2 from "ora";
|
|
85
85
|
import readline from "readline";
|
|
86
86
|
import select from "@inquirer/select";
|
|
87
|
-
import
|
|
88
|
-
import { createTwoFilesPatch } from "diff";
|
|
87
|
+
import fs18 from "fs";
|
|
89
88
|
|
|
90
89
|
// src/auth/token-store.ts
|
|
91
90
|
init_constants();
|
|
@@ -314,6 +313,11 @@ ${authUrl}
|
|
|
314
313
|
identifyUser(result.user.id, result.user.email);
|
|
315
314
|
trackEvent("signin", { method: "cli_pkce" });
|
|
316
315
|
spinner.succeed(chalk.green(`Logged in as ${result.user.email}`));
|
|
316
|
+
console.log("");
|
|
317
|
+
console.log(chalk.bold(" Next step:"));
|
|
318
|
+
console.log("");
|
|
319
|
+
console.log(` ${chalk.hex("#6366f1")("caliber init")} ${chalk.dim("Run this inside a project to generate your agent config")}`);
|
|
320
|
+
console.log("");
|
|
317
321
|
} catch (err) {
|
|
318
322
|
trackEvent("error_occurred", { error_type: "auth_failed", error_message: err instanceof Error ? err.message : "Unknown error", command: "login" });
|
|
319
323
|
spinner.fail(chalk.red(`Authentication failed: ${err instanceof Error ? err.message : "Unknown error"}`));
|
|
@@ -323,6 +327,8 @@ ${authUrl}
|
|
|
323
327
|
|
|
324
328
|
// src/fingerprint/index.ts
|
|
325
329
|
import crypto2 from "crypto";
|
|
330
|
+
import fs8 from "fs";
|
|
331
|
+
import path9 from "path";
|
|
326
332
|
|
|
327
333
|
// src/fingerprint/git.ts
|
|
328
334
|
import { execSync } from "child_process";
|
|
@@ -600,6 +606,20 @@ function readExistingConfigs(dir) {
|
|
|
600
606
|
} catch {
|
|
601
607
|
}
|
|
602
608
|
}
|
|
609
|
+
const cursorSkillsDir = path7.join(dir, ".cursor", "skills");
|
|
610
|
+
if (fs6.existsSync(cursorSkillsDir)) {
|
|
611
|
+
try {
|
|
612
|
+
const slugs = fs6.readdirSync(cursorSkillsDir).filter((f) => {
|
|
613
|
+
return fs6.statSync(path7.join(cursorSkillsDir, f)).isDirectory();
|
|
614
|
+
});
|
|
615
|
+
configs.cursorSkills = slugs.filter((slug) => fs6.existsSync(path7.join(cursorSkillsDir, slug, "SKILL.md"))).map((slug) => ({
|
|
616
|
+
slug,
|
|
617
|
+
filename: "SKILL.md",
|
|
618
|
+
content: fs6.readFileSync(path7.join(cursorSkillsDir, slug, "SKILL.md"), "utf-8")
|
|
619
|
+
}));
|
|
620
|
+
} catch {
|
|
621
|
+
}
|
|
622
|
+
}
|
|
603
623
|
const mcpJsonPath = path7.join(dir, ".mcp.json");
|
|
604
624
|
if (fs6.existsSync(mcpJsonPath)) {
|
|
605
625
|
try {
|
|
@@ -894,6 +914,127 @@ function estimateSummarySize(summary) {
|
|
|
894
914
|
return summary.path.length + summary.imports.reduce((s, i) => s + i.length, 0) + summary.exports.reduce((s, e) => s + e.length, 0) + summary.functions.reduce((s, f) => s + f.length, 0) + summary.classes.reduce((s, c) => s + c.length, 0) + summary.types.reduce((s, t) => s + t.length, 0) + summary.routes.reduce((s, r) => s + r.length, 0);
|
|
895
915
|
}
|
|
896
916
|
|
|
917
|
+
// src/api/client.ts
|
|
918
|
+
init_constants();
|
|
919
|
+
async function forceRefreshToken() {
|
|
920
|
+
const auth2 = getStoredAuth();
|
|
921
|
+
if (!auth2) return null;
|
|
922
|
+
try {
|
|
923
|
+
const resp = await fetch(`${API_URL}/api/auth/refresh`, {
|
|
924
|
+
method: "POST",
|
|
925
|
+
headers: { "Content-Type": "application/json" },
|
|
926
|
+
body: JSON.stringify({ refreshToken: auth2.refreshToken })
|
|
927
|
+
});
|
|
928
|
+
if (!resp.ok) return null;
|
|
929
|
+
const { data } = await resp.json();
|
|
930
|
+
storeAuth({
|
|
931
|
+
accessToken: data.accessToken,
|
|
932
|
+
refreshToken: data.refreshToken,
|
|
933
|
+
expiresIn: data.expiresIn,
|
|
934
|
+
user: { id: auth2.userId, email: auth2.email }
|
|
935
|
+
});
|
|
936
|
+
return data.accessToken;
|
|
937
|
+
} catch {
|
|
938
|
+
return null;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
async function getValidToken() {
|
|
942
|
+
const auth2 = getStoredAuth();
|
|
943
|
+
if (!auth2) {
|
|
944
|
+
throw new Error("Not authenticated. Run `caliber login` first.");
|
|
945
|
+
}
|
|
946
|
+
if (!isTokenExpired()) return auth2.accessToken;
|
|
947
|
+
const refreshed = await forceRefreshToken();
|
|
948
|
+
if (!refreshed) {
|
|
949
|
+
throw new Error("Session expired. Run `caliber login` to re-authenticate.");
|
|
950
|
+
}
|
|
951
|
+
return refreshed;
|
|
952
|
+
}
|
|
953
|
+
async function apiRequest(path21, options = {}) {
|
|
954
|
+
let token = await getValidToken();
|
|
955
|
+
let resp = await fetch(`${API_URL}${path21}`, {
|
|
956
|
+
method: options.method || "GET",
|
|
957
|
+
headers: {
|
|
958
|
+
"Content-Type": "application/json",
|
|
959
|
+
Authorization: `Bearer ${token}`
|
|
960
|
+
},
|
|
961
|
+
body: options.body ? JSON.stringify(options.body) : void 0
|
|
962
|
+
});
|
|
963
|
+
if (resp.status === 401) {
|
|
964
|
+
const refreshed = await forceRefreshToken();
|
|
965
|
+
if (!refreshed) {
|
|
966
|
+
throw new Error("Session expired. Run `caliber login` to re-authenticate.");
|
|
967
|
+
}
|
|
968
|
+
token = refreshed;
|
|
969
|
+
resp = await fetch(`${API_URL}${path21}`, {
|
|
970
|
+
method: options.method || "GET",
|
|
971
|
+
headers: {
|
|
972
|
+
"Content-Type": "application/json",
|
|
973
|
+
Authorization: `Bearer ${token}`
|
|
974
|
+
},
|
|
975
|
+
body: options.body ? JSON.stringify(options.body) : void 0
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
if (!resp.ok) {
|
|
979
|
+
const error = await resp.json().catch(() => ({ error: resp.statusText }));
|
|
980
|
+
throw new Error(error.error || `API error: ${resp.status}`);
|
|
981
|
+
}
|
|
982
|
+
const json = await resp.json();
|
|
983
|
+
return json.data;
|
|
984
|
+
}
|
|
985
|
+
async function apiStream(path21, body, onChunk, onComplete, onError, onStatus) {
|
|
986
|
+
let token = await getValidToken();
|
|
987
|
+
let resp = await fetch(`${API_URL}${path21}`, {
|
|
988
|
+
method: "POST",
|
|
989
|
+
headers: {
|
|
990
|
+
"Content-Type": "application/json",
|
|
991
|
+
Authorization: `Bearer ${token}`
|
|
992
|
+
},
|
|
993
|
+
body: JSON.stringify(body)
|
|
994
|
+
});
|
|
995
|
+
if (resp.status === 401) {
|
|
996
|
+
const refreshed = await forceRefreshToken();
|
|
997
|
+
if (!refreshed) {
|
|
998
|
+
throw new Error("Session expired. Run `caliber login` to re-authenticate.");
|
|
999
|
+
}
|
|
1000
|
+
token = refreshed;
|
|
1001
|
+
resp = await fetch(`${API_URL}${path21}`, {
|
|
1002
|
+
method: "POST",
|
|
1003
|
+
headers: {
|
|
1004
|
+
"Content-Type": "application/json",
|
|
1005
|
+
Authorization: `Bearer ${token}`
|
|
1006
|
+
},
|
|
1007
|
+
body: JSON.stringify(body)
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
if (!resp.ok || !resp.body) {
|
|
1011
|
+
throw new Error(`API error: ${resp.status}`);
|
|
1012
|
+
}
|
|
1013
|
+
const reader = resp.body.getReader();
|
|
1014
|
+
const decoder = new TextDecoder();
|
|
1015
|
+
let buffer = "";
|
|
1016
|
+
while (true) {
|
|
1017
|
+
const { done, value } = await reader.read();
|
|
1018
|
+
if (done) break;
|
|
1019
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1020
|
+
const lines = buffer.split("\n");
|
|
1021
|
+
buffer = lines.pop() || "";
|
|
1022
|
+
for (const line of lines) {
|
|
1023
|
+
if (!line.startsWith("data: ")) continue;
|
|
1024
|
+
const data = line.slice(6);
|
|
1025
|
+
if (data === "[DONE]") return;
|
|
1026
|
+
try {
|
|
1027
|
+
const parsed = JSON.parse(data);
|
|
1028
|
+
if (parsed.type === "chunk") onChunk(parsed.content);
|
|
1029
|
+
else if (parsed.type === "status" && onStatus) onStatus(parsed.message);
|
|
1030
|
+
else if (parsed.type === "complete") onComplete({ setup: parsed.setup, explanation: parsed.explanation, raw: parsed.raw });
|
|
1031
|
+
else if (parsed.type === "error") onError(parsed.message);
|
|
1032
|
+
} catch {
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
|
|
897
1038
|
// src/fingerprint/index.ts
|
|
898
1039
|
function collectFingerprint(dir) {
|
|
899
1040
|
const gitRemoteUrl = getGitRemoteUrl();
|
|
@@ -920,15 +1061,68 @@ function computeFingerprintHash(fingerprint) {
|
|
|
920
1061
|
].join("::");
|
|
921
1062
|
return crypto2.createHash("sha256").update(key).digest("hex");
|
|
922
1063
|
}
|
|
1064
|
+
var DEP_FILE_PATTERNS = [
|
|
1065
|
+
"package.json",
|
|
1066
|
+
"pyproject.toml",
|
|
1067
|
+
"requirements.txt",
|
|
1068
|
+
"setup.py",
|
|
1069
|
+
"Pipfile",
|
|
1070
|
+
"Cargo.toml",
|
|
1071
|
+
"go.mod",
|
|
1072
|
+
"Gemfile",
|
|
1073
|
+
"build.gradle",
|
|
1074
|
+
"pom.xml",
|
|
1075
|
+
"composer.json"
|
|
1076
|
+
];
|
|
1077
|
+
var MAX_CONTENT_SIZE = 50 * 1024;
|
|
1078
|
+
async function enrichFingerprintWithLLM(fingerprint, dir) {
|
|
1079
|
+
try {
|
|
1080
|
+
const fileContents = {};
|
|
1081
|
+
let totalSize = 0;
|
|
1082
|
+
for (const treePath of fingerprint.fileTree) {
|
|
1083
|
+
const basename = path9.basename(treePath);
|
|
1084
|
+
if (!DEP_FILE_PATTERNS.includes(basename)) continue;
|
|
1085
|
+
const fullPath = path9.join(dir, treePath);
|
|
1086
|
+
if (!fs8.existsSync(fullPath)) continue;
|
|
1087
|
+
try {
|
|
1088
|
+
const content = fs8.readFileSync(fullPath, "utf-8");
|
|
1089
|
+
if (totalSize + content.length > MAX_CONTENT_SIZE) break;
|
|
1090
|
+
fileContents[treePath] = content;
|
|
1091
|
+
totalSize += content.length;
|
|
1092
|
+
} catch {
|
|
1093
|
+
continue;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
if (Object.keys(fileContents).length === 0 && fingerprint.fileTree.length === 0) return;
|
|
1097
|
+
const result = await apiRequest(
|
|
1098
|
+
"/api/fingerprint/detect",
|
|
1099
|
+
{
|
|
1100
|
+
method: "POST",
|
|
1101
|
+
body: { fileTree: fingerprint.fileTree, fileContents }
|
|
1102
|
+
}
|
|
1103
|
+
);
|
|
1104
|
+
if (result.languages?.length) {
|
|
1105
|
+
const langSet = new Set(fingerprint.languages);
|
|
1106
|
+
for (const lang of result.languages) langSet.add(lang);
|
|
1107
|
+
fingerprint.languages = [...langSet];
|
|
1108
|
+
}
|
|
1109
|
+
if (result.frameworks?.length) {
|
|
1110
|
+
const fwSet = new Set(fingerprint.frameworks);
|
|
1111
|
+
for (const fw of result.frameworks) fwSet.add(fw);
|
|
1112
|
+
fingerprint.frameworks = [...fwSet];
|
|
1113
|
+
}
|
|
1114
|
+
} catch {
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
923
1117
|
|
|
924
1118
|
// src/scanner/index.ts
|
|
925
|
-
import
|
|
926
|
-
import
|
|
1119
|
+
import fs9 from "fs";
|
|
1120
|
+
import path10 from "path";
|
|
927
1121
|
import crypto3 from "crypto";
|
|
928
1122
|
function scanLocalState(dir) {
|
|
929
1123
|
const items = [];
|
|
930
|
-
const claudeMdPath =
|
|
931
|
-
if (
|
|
1124
|
+
const claudeMdPath = path10.join(dir, "CLAUDE.md");
|
|
1125
|
+
if (fs9.existsSync(claudeMdPath)) {
|
|
932
1126
|
items.push({
|
|
933
1127
|
type: "rule",
|
|
934
1128
|
platform: "claude",
|
|
@@ -937,10 +1131,10 @@ function scanLocalState(dir) {
|
|
|
937
1131
|
path: claudeMdPath
|
|
938
1132
|
});
|
|
939
1133
|
}
|
|
940
|
-
const skillsDir =
|
|
941
|
-
if (
|
|
942
|
-
for (const file of
|
|
943
|
-
const filePath =
|
|
1134
|
+
const skillsDir = path10.join(dir, ".claude", "skills");
|
|
1135
|
+
if (fs9.existsSync(skillsDir)) {
|
|
1136
|
+
for (const file of fs9.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
|
|
1137
|
+
const filePath = path10.join(skillsDir, file);
|
|
944
1138
|
items.push({
|
|
945
1139
|
type: "skill",
|
|
946
1140
|
platform: "claude",
|
|
@@ -950,10 +1144,10 @@ function scanLocalState(dir) {
|
|
|
950
1144
|
});
|
|
951
1145
|
}
|
|
952
1146
|
}
|
|
953
|
-
const mcpJsonPath =
|
|
954
|
-
if (
|
|
1147
|
+
const mcpJsonPath = path10.join(dir, ".mcp.json");
|
|
1148
|
+
if (fs9.existsSync(mcpJsonPath)) {
|
|
955
1149
|
try {
|
|
956
|
-
const mcpJson = JSON.parse(
|
|
1150
|
+
const mcpJson = JSON.parse(fs9.readFileSync(mcpJsonPath, "utf-8"));
|
|
957
1151
|
if (mcpJson.mcpServers) {
|
|
958
1152
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
959
1153
|
items.push({
|
|
@@ -968,8 +1162,8 @@ function scanLocalState(dir) {
|
|
|
968
1162
|
} catch {
|
|
969
1163
|
}
|
|
970
1164
|
}
|
|
971
|
-
const cursorrulesPath =
|
|
972
|
-
if (
|
|
1165
|
+
const cursorrulesPath = path10.join(dir, ".cursorrules");
|
|
1166
|
+
if (fs9.existsSync(cursorrulesPath)) {
|
|
973
1167
|
items.push({
|
|
974
1168
|
type: "rule",
|
|
975
1169
|
platform: "cursor",
|
|
@@ -978,10 +1172,10 @@ function scanLocalState(dir) {
|
|
|
978
1172
|
path: cursorrulesPath
|
|
979
1173
|
});
|
|
980
1174
|
}
|
|
981
|
-
const cursorRulesDir =
|
|
982
|
-
if (
|
|
983
|
-
for (const file of
|
|
984
|
-
const filePath =
|
|
1175
|
+
const cursorRulesDir = path10.join(dir, ".cursor", "rules");
|
|
1176
|
+
if (fs9.existsSync(cursorRulesDir)) {
|
|
1177
|
+
for (const file of fs9.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
|
|
1178
|
+
const filePath = path10.join(cursorRulesDir, file);
|
|
985
1179
|
items.push({
|
|
986
1180
|
type: "rule",
|
|
987
1181
|
platform: "cursor",
|
|
@@ -991,10 +1185,28 @@ function scanLocalState(dir) {
|
|
|
991
1185
|
});
|
|
992
1186
|
}
|
|
993
1187
|
}
|
|
994
|
-
const
|
|
995
|
-
if (
|
|
1188
|
+
const cursorSkillsDir = path10.join(dir, ".cursor", "skills");
|
|
1189
|
+
if (fs9.existsSync(cursorSkillsDir)) {
|
|
1190
|
+
try {
|
|
1191
|
+
for (const slug of fs9.readdirSync(cursorSkillsDir)) {
|
|
1192
|
+
const skillFile = path10.join(cursorSkillsDir, slug, "SKILL.md");
|
|
1193
|
+
if (fs9.existsSync(skillFile)) {
|
|
1194
|
+
items.push({
|
|
1195
|
+
type: "skill",
|
|
1196
|
+
platform: "cursor",
|
|
1197
|
+
name: `${slug}/SKILL.md`,
|
|
1198
|
+
contentHash: hashFile(skillFile),
|
|
1199
|
+
path: skillFile
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
} catch {
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
const cursorMcpPath = path10.join(dir, ".cursor", "mcp.json");
|
|
1207
|
+
if (fs9.existsSync(cursorMcpPath)) {
|
|
996
1208
|
try {
|
|
997
|
-
const mcpJson = JSON.parse(
|
|
1209
|
+
const mcpJson = JSON.parse(fs9.readFileSync(cursorMcpPath, "utf-8"));
|
|
998
1210
|
if (mcpJson.mcpServers) {
|
|
999
1211
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
1000
1212
|
items.push({
|
|
@@ -1038,196 +1250,84 @@ function compareState(serverItems, localItems) {
|
|
|
1038
1250
|
return { installed, missing, outdated, extra };
|
|
1039
1251
|
}
|
|
1040
1252
|
function hashFile(filePath) {
|
|
1041
|
-
const text =
|
|
1253
|
+
const text = fs9.readFileSync(filePath, "utf-8");
|
|
1042
1254
|
return crypto3.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
|
|
1043
1255
|
}
|
|
1044
1256
|
function hashJson(obj) {
|
|
1045
1257
|
return crypto3.createHash("sha256").update(JSON.stringify(obj)).digest("hex");
|
|
1046
1258
|
}
|
|
1047
1259
|
|
|
1048
|
-
// src/api/client.ts
|
|
1049
|
-
init_constants();
|
|
1050
|
-
async function forceRefreshToken() {
|
|
1051
|
-
const auth2 = getStoredAuth();
|
|
1052
|
-
if (!auth2) return null;
|
|
1053
|
-
try {
|
|
1054
|
-
const resp = await fetch(`${API_URL}/api/auth/refresh`, {
|
|
1055
|
-
method: "POST",
|
|
1056
|
-
headers: { "Content-Type": "application/json" },
|
|
1057
|
-
body: JSON.stringify({ refreshToken: auth2.refreshToken })
|
|
1058
|
-
});
|
|
1059
|
-
if (!resp.ok) return null;
|
|
1060
|
-
const { data } = await resp.json();
|
|
1061
|
-
storeAuth({
|
|
1062
|
-
accessToken: data.accessToken,
|
|
1063
|
-
refreshToken: data.refreshToken,
|
|
1064
|
-
expiresIn: data.expiresIn,
|
|
1065
|
-
user: { id: auth2.userId, email: auth2.email }
|
|
1066
|
-
});
|
|
1067
|
-
return data.accessToken;
|
|
1068
|
-
} catch {
|
|
1069
|
-
return null;
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
async function getValidToken() {
|
|
1073
|
-
const auth2 = getStoredAuth();
|
|
1074
|
-
if (!auth2) {
|
|
1075
|
-
throw new Error("Not authenticated. Run `caliber login` first.");
|
|
1076
|
-
}
|
|
1077
|
-
if (!isTokenExpired()) return auth2.accessToken;
|
|
1078
|
-
const refreshed = await forceRefreshToken();
|
|
1079
|
-
if (!refreshed) {
|
|
1080
|
-
throw new Error("Session expired. Run `caliber login` to re-authenticate.");
|
|
1081
|
-
}
|
|
1082
|
-
return refreshed;
|
|
1083
|
-
}
|
|
1084
|
-
async function apiRequest(path19, options = {}) {
|
|
1085
|
-
let token = await getValidToken();
|
|
1086
|
-
let resp = await fetch(`${API_URL}${path19}`, {
|
|
1087
|
-
method: options.method || "GET",
|
|
1088
|
-
headers: {
|
|
1089
|
-
"Content-Type": "application/json",
|
|
1090
|
-
Authorization: `Bearer ${token}`
|
|
1091
|
-
},
|
|
1092
|
-
body: options.body ? JSON.stringify(options.body) : void 0
|
|
1093
|
-
});
|
|
1094
|
-
if (resp.status === 401) {
|
|
1095
|
-
const refreshed = await forceRefreshToken();
|
|
1096
|
-
if (!refreshed) {
|
|
1097
|
-
throw new Error("Session expired. Run `caliber login` to re-authenticate.");
|
|
1098
|
-
}
|
|
1099
|
-
token = refreshed;
|
|
1100
|
-
resp = await fetch(`${API_URL}${path19}`, {
|
|
1101
|
-
method: options.method || "GET",
|
|
1102
|
-
headers: {
|
|
1103
|
-
"Content-Type": "application/json",
|
|
1104
|
-
Authorization: `Bearer ${token}`
|
|
1105
|
-
},
|
|
1106
|
-
body: options.body ? JSON.stringify(options.body) : void 0
|
|
1107
|
-
});
|
|
1108
|
-
}
|
|
1109
|
-
if (!resp.ok) {
|
|
1110
|
-
const error = await resp.json().catch(() => ({ error: resp.statusText }));
|
|
1111
|
-
throw new Error(error.error || `API error: ${resp.status}`);
|
|
1112
|
-
}
|
|
1113
|
-
const json = await resp.json();
|
|
1114
|
-
return json.data;
|
|
1115
|
-
}
|
|
1116
|
-
async function apiStream(path19, body, onChunk, onComplete, onError, onStatus) {
|
|
1117
|
-
let token = await getValidToken();
|
|
1118
|
-
let resp = await fetch(`${API_URL}${path19}`, {
|
|
1119
|
-
method: "POST",
|
|
1120
|
-
headers: {
|
|
1121
|
-
"Content-Type": "application/json",
|
|
1122
|
-
Authorization: `Bearer ${token}`
|
|
1123
|
-
},
|
|
1124
|
-
body: JSON.stringify(body)
|
|
1125
|
-
});
|
|
1126
|
-
if (resp.status === 401) {
|
|
1127
|
-
const refreshed = await forceRefreshToken();
|
|
1128
|
-
if (!refreshed) {
|
|
1129
|
-
throw new Error("Session expired. Run `caliber login` to re-authenticate.");
|
|
1130
|
-
}
|
|
1131
|
-
token = refreshed;
|
|
1132
|
-
resp = await fetch(`${API_URL}${path19}`, {
|
|
1133
|
-
method: "POST",
|
|
1134
|
-
headers: {
|
|
1135
|
-
"Content-Type": "application/json",
|
|
1136
|
-
Authorization: `Bearer ${token}`
|
|
1137
|
-
},
|
|
1138
|
-
body: JSON.stringify(body)
|
|
1139
|
-
});
|
|
1140
|
-
}
|
|
1141
|
-
if (!resp.ok || !resp.body) {
|
|
1142
|
-
throw new Error(`API error: ${resp.status}`);
|
|
1143
|
-
}
|
|
1144
|
-
const reader = resp.body.getReader();
|
|
1145
|
-
const decoder = new TextDecoder();
|
|
1146
|
-
let buffer = "";
|
|
1147
|
-
while (true) {
|
|
1148
|
-
const { done, value } = await reader.read();
|
|
1149
|
-
if (done) break;
|
|
1150
|
-
buffer += decoder.decode(value, { stream: true });
|
|
1151
|
-
const lines = buffer.split("\n");
|
|
1152
|
-
buffer = lines.pop() || "";
|
|
1153
|
-
for (const line of lines) {
|
|
1154
|
-
if (!line.startsWith("data: ")) continue;
|
|
1155
|
-
const data = line.slice(6);
|
|
1156
|
-
if (data === "[DONE]") return;
|
|
1157
|
-
try {
|
|
1158
|
-
const parsed = JSON.parse(data);
|
|
1159
|
-
if (parsed.type === "chunk") onChunk(parsed.content);
|
|
1160
|
-
else if (parsed.type === "status" && onStatus) onStatus(parsed.message);
|
|
1161
|
-
else if (parsed.type === "complete") onComplete({ setup: parsed.setup, explanation: parsed.explanation, raw: parsed.raw });
|
|
1162
|
-
else if (parsed.type === "error") onError(parsed.message);
|
|
1163
|
-
} catch {
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
1260
|
// src/writers/index.ts
|
|
1170
|
-
import
|
|
1261
|
+
import fs14 from "fs";
|
|
1171
1262
|
|
|
1172
1263
|
// src/writers/claude/index.ts
|
|
1173
|
-
import
|
|
1174
|
-
import
|
|
1264
|
+
import fs10 from "fs";
|
|
1265
|
+
import path11 from "path";
|
|
1175
1266
|
function writeClaudeConfig(config) {
|
|
1176
1267
|
const written = [];
|
|
1177
|
-
|
|
1268
|
+
fs10.writeFileSync("CLAUDE.md", config.claudeMd);
|
|
1178
1269
|
written.push("CLAUDE.md");
|
|
1179
1270
|
if (config.skills?.length) {
|
|
1180
|
-
const skillsDir =
|
|
1181
|
-
if (!
|
|
1271
|
+
const skillsDir = path11.join(".claude", "skills");
|
|
1272
|
+
if (!fs10.existsSync(skillsDir)) fs10.mkdirSync(skillsDir, { recursive: true });
|
|
1182
1273
|
for (const skill of config.skills) {
|
|
1183
1274
|
const filename = `${skill.name.replace(/[^a-z0-9-]/gi, "-").toLowerCase()}.md`;
|
|
1184
|
-
const skillPath =
|
|
1185
|
-
|
|
1275
|
+
const skillPath = path11.join(skillsDir, filename);
|
|
1276
|
+
fs10.writeFileSync(skillPath, skill.content);
|
|
1186
1277
|
written.push(skillPath);
|
|
1187
1278
|
}
|
|
1188
1279
|
}
|
|
1189
1280
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
1190
1281
|
let existingServers = {};
|
|
1191
1282
|
try {
|
|
1192
|
-
if (
|
|
1193
|
-
const existing = JSON.parse(
|
|
1283
|
+
if (fs10.existsSync(".mcp.json")) {
|
|
1284
|
+
const existing = JSON.parse(fs10.readFileSync(".mcp.json", "utf-8"));
|
|
1194
1285
|
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
1195
1286
|
}
|
|
1196
1287
|
} catch {
|
|
1197
1288
|
}
|
|
1198
1289
|
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
1199
|
-
|
|
1290
|
+
fs10.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
1200
1291
|
written.push(".mcp.json");
|
|
1201
1292
|
}
|
|
1202
1293
|
return written;
|
|
1203
1294
|
}
|
|
1204
1295
|
|
|
1205
1296
|
// src/writers/cursor/index.ts
|
|
1206
|
-
import
|
|
1207
|
-
import
|
|
1297
|
+
import fs11 from "fs";
|
|
1298
|
+
import path12 from "path";
|
|
1208
1299
|
function writeCursorConfig(config) {
|
|
1209
1300
|
const written = [];
|
|
1210
1301
|
if (config.cursorrules) {
|
|
1211
|
-
|
|
1302
|
+
fs11.writeFileSync(".cursorrules", config.cursorrules);
|
|
1212
1303
|
written.push(".cursorrules");
|
|
1213
1304
|
}
|
|
1214
1305
|
if (config.rules?.length) {
|
|
1215
|
-
const rulesDir =
|
|
1216
|
-
if (!
|
|
1306
|
+
const rulesDir = path12.join(".cursor", "rules");
|
|
1307
|
+
if (!fs11.existsSync(rulesDir)) fs11.mkdirSync(rulesDir, { recursive: true });
|
|
1217
1308
|
for (const rule of config.rules) {
|
|
1218
|
-
const rulePath =
|
|
1219
|
-
|
|
1309
|
+
const rulePath = path12.join(rulesDir, rule.filename);
|
|
1310
|
+
fs11.writeFileSync(rulePath, rule.content);
|
|
1220
1311
|
written.push(rulePath);
|
|
1221
1312
|
}
|
|
1222
1313
|
}
|
|
1314
|
+
if (config.skills?.length) {
|
|
1315
|
+
for (const skill of config.skills) {
|
|
1316
|
+
const skillDir = path12.join(".cursor", "skills", skill.slug);
|
|
1317
|
+
if (!fs11.existsSync(skillDir)) fs11.mkdirSync(skillDir, { recursive: true });
|
|
1318
|
+
const skillPath = path12.join(skillDir, "SKILL.md");
|
|
1319
|
+
fs11.writeFileSync(skillPath, skill.content);
|
|
1320
|
+
written.push(skillPath);
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1223
1323
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
1224
1324
|
const cursorDir = ".cursor";
|
|
1225
|
-
if (!
|
|
1226
|
-
const mcpPath =
|
|
1325
|
+
if (!fs11.existsSync(cursorDir)) fs11.mkdirSync(cursorDir, { recursive: true });
|
|
1326
|
+
const mcpPath = path12.join(cursorDir, "mcp.json");
|
|
1227
1327
|
let existingServers = {};
|
|
1228
1328
|
try {
|
|
1229
|
-
if (
|
|
1230
|
-
const existing = JSON.parse(
|
|
1329
|
+
if (fs11.existsSync(mcpPath)) {
|
|
1330
|
+
const existing = JSON.parse(fs11.readFileSync(mcpPath, "utf-8"));
|
|
1231
1331
|
if (existing.mcpServers) {
|
|
1232
1332
|
existingServers = existing.mcpServers;
|
|
1233
1333
|
}
|
|
@@ -1235,7 +1335,7 @@ function writeCursorConfig(config) {
|
|
|
1235
1335
|
} catch {
|
|
1236
1336
|
}
|
|
1237
1337
|
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
1238
|
-
|
|
1338
|
+
fs11.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
1239
1339
|
written.push(mcpPath);
|
|
1240
1340
|
}
|
|
1241
1341
|
return written;
|
|
@@ -1243,62 +1343,62 @@ function writeCursorConfig(config) {
|
|
|
1243
1343
|
|
|
1244
1344
|
// src/writers/backup.ts
|
|
1245
1345
|
init_constants();
|
|
1246
|
-
import
|
|
1247
|
-
import
|
|
1346
|
+
import fs12 from "fs";
|
|
1347
|
+
import path13 from "path";
|
|
1248
1348
|
function createBackup(files) {
|
|
1249
1349
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1250
|
-
const backupDir =
|
|
1350
|
+
const backupDir = path13.join(BACKUPS_DIR, timestamp);
|
|
1251
1351
|
for (const file of files) {
|
|
1252
|
-
if (!
|
|
1253
|
-
const dest =
|
|
1254
|
-
const destDir =
|
|
1255
|
-
if (!
|
|
1256
|
-
|
|
1352
|
+
if (!fs12.existsSync(file)) continue;
|
|
1353
|
+
const dest = path13.join(backupDir, file);
|
|
1354
|
+
const destDir = path13.dirname(dest);
|
|
1355
|
+
if (!fs12.existsSync(destDir)) {
|
|
1356
|
+
fs12.mkdirSync(destDir, { recursive: true });
|
|
1257
1357
|
}
|
|
1258
|
-
|
|
1358
|
+
fs12.copyFileSync(file, dest);
|
|
1259
1359
|
}
|
|
1260
1360
|
return backupDir;
|
|
1261
1361
|
}
|
|
1262
1362
|
function restoreBackup(backupDir, file) {
|
|
1263
|
-
const backupFile =
|
|
1264
|
-
if (!
|
|
1265
|
-
const destDir =
|
|
1266
|
-
if (!
|
|
1267
|
-
|
|
1363
|
+
const backupFile = path13.join(backupDir, file);
|
|
1364
|
+
if (!fs12.existsSync(backupFile)) return false;
|
|
1365
|
+
const destDir = path13.dirname(file);
|
|
1366
|
+
if (!fs12.existsSync(destDir)) {
|
|
1367
|
+
fs12.mkdirSync(destDir, { recursive: true });
|
|
1268
1368
|
}
|
|
1269
|
-
|
|
1369
|
+
fs12.copyFileSync(backupFile, file);
|
|
1270
1370
|
return true;
|
|
1271
1371
|
}
|
|
1272
1372
|
|
|
1273
1373
|
// src/writers/manifest.ts
|
|
1274
1374
|
init_constants();
|
|
1275
|
-
import
|
|
1375
|
+
import fs13 from "fs";
|
|
1276
1376
|
import crypto4 from "crypto";
|
|
1277
1377
|
function readManifest() {
|
|
1278
1378
|
try {
|
|
1279
|
-
if (!
|
|
1280
|
-
return JSON.parse(
|
|
1379
|
+
if (!fs13.existsSync(MANIFEST_FILE)) return null;
|
|
1380
|
+
return JSON.parse(fs13.readFileSync(MANIFEST_FILE, "utf-8"));
|
|
1281
1381
|
} catch {
|
|
1282
1382
|
return null;
|
|
1283
1383
|
}
|
|
1284
1384
|
}
|
|
1285
1385
|
function writeManifest(manifest) {
|
|
1286
|
-
if (!
|
|
1287
|
-
|
|
1386
|
+
if (!fs13.existsSync(CALIBER_DIR)) {
|
|
1387
|
+
fs13.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
1288
1388
|
}
|
|
1289
|
-
|
|
1389
|
+
fs13.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
|
|
1290
1390
|
}
|
|
1291
1391
|
function fileChecksum(filePath) {
|
|
1292
|
-
const content =
|
|
1392
|
+
const content = fs13.readFileSync(filePath);
|
|
1293
1393
|
return crypto4.createHash("sha256").update(content).digest("hex");
|
|
1294
1394
|
}
|
|
1295
1395
|
|
|
1296
1396
|
// src/writers/index.ts
|
|
1297
1397
|
function writeSetup(setup) {
|
|
1298
1398
|
const filesToWrite = getFilesToWrite(setup);
|
|
1299
|
-
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) =>
|
|
1399
|
+
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs14.existsSync(f));
|
|
1300
1400
|
const existingFiles = [
|
|
1301
|
-
...filesToWrite.filter((f) =>
|
|
1401
|
+
...filesToWrite.filter((f) => fs14.existsSync(f)),
|
|
1302
1402
|
...filesToDelete
|
|
1303
1403
|
];
|
|
1304
1404
|
const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
|
|
@@ -1311,7 +1411,7 @@ function writeSetup(setup) {
|
|
|
1311
1411
|
}
|
|
1312
1412
|
const deleted = [];
|
|
1313
1413
|
for (const filePath of filesToDelete) {
|
|
1314
|
-
|
|
1414
|
+
fs14.unlinkSync(filePath);
|
|
1315
1415
|
deleted.push(filePath);
|
|
1316
1416
|
}
|
|
1317
1417
|
ensureGitignore();
|
|
@@ -1341,8 +1441,8 @@ function undoSetup() {
|
|
|
1341
1441
|
const removed = [];
|
|
1342
1442
|
for (const entry of manifest.entries) {
|
|
1343
1443
|
if (entry.action === "created") {
|
|
1344
|
-
if (
|
|
1345
|
-
|
|
1444
|
+
if (fs14.existsSync(entry.path)) {
|
|
1445
|
+
fs14.unlinkSync(entry.path);
|
|
1346
1446
|
removed.push(entry.path);
|
|
1347
1447
|
}
|
|
1348
1448
|
} else if ((entry.action === "modified" || entry.action === "deleted") && manifest.backupDir) {
|
|
@@ -1352,8 +1452,8 @@ function undoSetup() {
|
|
|
1352
1452
|
}
|
|
1353
1453
|
}
|
|
1354
1454
|
const { MANIFEST_FILE: MANIFEST_FILE2 } = (init_constants(), __toCommonJS(constants_exports));
|
|
1355
|
-
if (
|
|
1356
|
-
|
|
1455
|
+
if (fs14.existsSync(MANIFEST_FILE2)) {
|
|
1456
|
+
fs14.unlinkSync(MANIFEST_FILE2);
|
|
1357
1457
|
}
|
|
1358
1458
|
return { restored, removed };
|
|
1359
1459
|
}
|
|
@@ -1373,40 +1473,123 @@ function getFilesToWrite(setup) {
|
|
|
1373
1473
|
if (setup.cursor.rules) {
|
|
1374
1474
|
for (const r of setup.cursor.rules) files.push(`.cursor/rules/${r.filename}`);
|
|
1375
1475
|
}
|
|
1476
|
+
if (setup.cursor.skills) {
|
|
1477
|
+
for (const s of setup.cursor.skills) files.push(`.cursor/skills/${s.slug}/SKILL.md`);
|
|
1478
|
+
}
|
|
1376
1479
|
if (setup.cursor.mcpServers) files.push(".cursor/mcp.json");
|
|
1377
1480
|
}
|
|
1378
1481
|
return files;
|
|
1379
1482
|
}
|
|
1380
1483
|
function ensureGitignore() {
|
|
1381
1484
|
const gitignorePath = ".gitignore";
|
|
1382
|
-
if (
|
|
1383
|
-
const content =
|
|
1485
|
+
if (fs14.existsSync(gitignorePath)) {
|
|
1486
|
+
const content = fs14.readFileSync(gitignorePath, "utf-8");
|
|
1384
1487
|
if (!content.includes(".caliber/")) {
|
|
1385
|
-
|
|
1488
|
+
fs14.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
|
|
1386
1489
|
}
|
|
1387
1490
|
} else {
|
|
1388
|
-
|
|
1491
|
+
fs14.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
// src/writers/staging.ts
|
|
1496
|
+
init_constants();
|
|
1497
|
+
import fs15 from "fs";
|
|
1498
|
+
import path14 from "path";
|
|
1499
|
+
var STAGED_DIR = path14.join(CALIBER_DIR, "staged");
|
|
1500
|
+
var PROPOSED_DIR = path14.join(STAGED_DIR, "proposed");
|
|
1501
|
+
var CURRENT_DIR = path14.join(STAGED_DIR, "current");
|
|
1502
|
+
function stageFiles(files, projectDir) {
|
|
1503
|
+
cleanupStaging();
|
|
1504
|
+
let newFiles = 0;
|
|
1505
|
+
let modifiedFiles = 0;
|
|
1506
|
+
for (const file of files) {
|
|
1507
|
+
const proposedPath = path14.join(PROPOSED_DIR, file.path);
|
|
1508
|
+
fs15.mkdirSync(path14.dirname(proposedPath), { recursive: true });
|
|
1509
|
+
fs15.writeFileSync(proposedPath, file.content);
|
|
1510
|
+
const originalPath = path14.join(projectDir, file.path);
|
|
1511
|
+
if (fs15.existsSync(originalPath)) {
|
|
1512
|
+
const currentPath = path14.join(CURRENT_DIR, file.path);
|
|
1513
|
+
fs15.mkdirSync(path14.dirname(currentPath), { recursive: true });
|
|
1514
|
+
fs15.copyFileSync(originalPath, currentPath);
|
|
1515
|
+
modifiedFiles++;
|
|
1516
|
+
} else {
|
|
1517
|
+
newFiles++;
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
return { newFiles, modifiedFiles };
|
|
1521
|
+
}
|
|
1522
|
+
function getStagedProposedDir() {
|
|
1523
|
+
return PROPOSED_DIR;
|
|
1524
|
+
}
|
|
1525
|
+
function cleanupStaging() {
|
|
1526
|
+
if (fs15.existsSync(STAGED_DIR)) {
|
|
1527
|
+
fs15.rmSync(STAGED_DIR, { recursive: true, force: true });
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
// src/utils/editor.ts
|
|
1532
|
+
import { execSync as execSync2 } from "child_process";
|
|
1533
|
+
function commandExists(cmd) {
|
|
1534
|
+
try {
|
|
1535
|
+
execSync2(`which ${cmd}`, { stdio: "ignore" });
|
|
1536
|
+
return true;
|
|
1537
|
+
} catch {
|
|
1538
|
+
return false;
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
function openInEditor(dirPath) {
|
|
1542
|
+
const editors = ["cursor", "code"];
|
|
1543
|
+
for (const editor of editors) {
|
|
1544
|
+
if (commandExists(editor)) {
|
|
1545
|
+
try {
|
|
1546
|
+
execSync2(`${editor} --diff "${dirPath}" "${dirPath}"`, { stdio: "ignore" });
|
|
1547
|
+
return true;
|
|
1548
|
+
} catch {
|
|
1549
|
+
try {
|
|
1550
|
+
execSync2(`${editor} "${dirPath}"`, { stdio: "ignore" });
|
|
1551
|
+
return true;
|
|
1552
|
+
} catch {
|
|
1553
|
+
continue;
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
const envEditor = process.env.EDITOR;
|
|
1559
|
+
if (envEditor && commandExists(envEditor)) {
|
|
1560
|
+
try {
|
|
1561
|
+
execSync2(`${envEditor} "${dirPath}"`, { stdio: "ignore" });
|
|
1562
|
+
return true;
|
|
1563
|
+
} catch {
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
try {
|
|
1567
|
+
const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
|
|
1568
|
+
execSync2(`${openCmd} "${dirPath}"`, { stdio: "ignore" });
|
|
1569
|
+
return true;
|
|
1570
|
+
} catch {
|
|
1571
|
+
return false;
|
|
1389
1572
|
}
|
|
1390
1573
|
}
|
|
1391
1574
|
|
|
1392
1575
|
// src/lib/hooks.ts
|
|
1393
|
-
import
|
|
1394
|
-
import
|
|
1395
|
-
var SETTINGS_PATH =
|
|
1576
|
+
import fs16 from "fs";
|
|
1577
|
+
import path15 from "path";
|
|
1578
|
+
var SETTINGS_PATH = path15.join(".claude", "settings.json");
|
|
1396
1579
|
var HOOK_COMMAND = "caliber refresh --quiet";
|
|
1397
1580
|
var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
|
|
1398
1581
|
function readSettings() {
|
|
1399
|
-
if (!
|
|
1582
|
+
if (!fs16.existsSync(SETTINGS_PATH)) return {};
|
|
1400
1583
|
try {
|
|
1401
|
-
return JSON.parse(
|
|
1584
|
+
return JSON.parse(fs16.readFileSync(SETTINGS_PATH, "utf-8"));
|
|
1402
1585
|
} catch {
|
|
1403
1586
|
return {};
|
|
1404
1587
|
}
|
|
1405
1588
|
}
|
|
1406
1589
|
function writeSettings(settings) {
|
|
1407
|
-
const dir =
|
|
1408
|
-
if (!
|
|
1409
|
-
|
|
1590
|
+
const dir = path15.dirname(SETTINGS_PATH);
|
|
1591
|
+
if (!fs16.existsSync(dir)) fs16.mkdirSync(dir, { recursive: true });
|
|
1592
|
+
fs16.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
1410
1593
|
}
|
|
1411
1594
|
function findHookIndex(sessionEnd) {
|
|
1412
1595
|
return sessionEnd.findIndex(
|
|
@@ -1456,27 +1639,27 @@ function removeHook() {
|
|
|
1456
1639
|
|
|
1457
1640
|
// src/lib/state.ts
|
|
1458
1641
|
init_constants();
|
|
1459
|
-
import
|
|
1460
|
-
import
|
|
1461
|
-
import { execSync as
|
|
1462
|
-
var STATE_FILE =
|
|
1642
|
+
import fs17 from "fs";
|
|
1643
|
+
import path16 from "path";
|
|
1644
|
+
import { execSync as execSync3 } from "child_process";
|
|
1645
|
+
var STATE_FILE = path16.join(CALIBER_DIR, ".caliber-state.json");
|
|
1463
1646
|
function readState() {
|
|
1464
1647
|
try {
|
|
1465
|
-
if (!
|
|
1466
|
-
return JSON.parse(
|
|
1648
|
+
if (!fs17.existsSync(STATE_FILE)) return null;
|
|
1649
|
+
return JSON.parse(fs17.readFileSync(STATE_FILE, "utf-8"));
|
|
1467
1650
|
} catch {
|
|
1468
1651
|
return null;
|
|
1469
1652
|
}
|
|
1470
1653
|
}
|
|
1471
1654
|
function writeState(state) {
|
|
1472
|
-
if (!
|
|
1473
|
-
|
|
1655
|
+
if (!fs17.existsSync(CALIBER_DIR)) {
|
|
1656
|
+
fs17.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
1474
1657
|
}
|
|
1475
|
-
|
|
1658
|
+
fs17.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
|
|
1476
1659
|
}
|
|
1477
1660
|
function getCurrentHeadSha() {
|
|
1478
1661
|
try {
|
|
1479
|
-
return
|
|
1662
|
+
return execSync3("git rev-parse HEAD", {
|
|
1480
1663
|
encoding: "utf-8",
|
|
1481
1664
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1482
1665
|
}).trim();
|
|
@@ -1606,6 +1789,7 @@ async function initCommand(options) {
|
|
|
1606
1789
|
const fingerprint = collectFingerprint(process.cwd());
|
|
1607
1790
|
const hash = computeFingerprintHash(fingerprint);
|
|
1608
1791
|
spinner.succeed("Project analyzed");
|
|
1792
|
+
const enrichmentPromise = enrichFingerprintWithLLM(fingerprint, process.cwd());
|
|
1609
1793
|
trackEvent("scan_completed", {
|
|
1610
1794
|
languages: fingerprint.languages,
|
|
1611
1795
|
frameworks: fingerprint.frameworks,
|
|
@@ -1614,6 +1798,7 @@ async function initCommand(options) {
|
|
|
1614
1798
|
has_claude_settings: !!fingerprint.existingConfigs.claudeSettings,
|
|
1615
1799
|
has_cursorrules: !!fingerprint.existingConfigs.cursorrules,
|
|
1616
1800
|
cursor_rules_count: fingerprint.existingConfigs.cursorRules?.length ?? 0,
|
|
1801
|
+
cursor_skills_count: fingerprint.existingConfigs.cursorSkills?.length ?? 0,
|
|
1617
1802
|
skills_count: fingerprint.existingConfigs.claudeSkills?.length ?? 0,
|
|
1618
1803
|
has_claude_mcp_servers: !!fingerprint.existingConfigs.claudeMcpServers,
|
|
1619
1804
|
has_cursor_mcp_servers: !!fingerprint.existingConfigs.cursorMcpServers,
|
|
@@ -1643,6 +1828,11 @@ async function initCommand(options) {
|
|
|
1643
1828
|
localConfigs.push(`.cursor/rules/${rule.filename}`);
|
|
1644
1829
|
}
|
|
1645
1830
|
}
|
|
1831
|
+
if (Array.isArray(ec.cursorSkills)) {
|
|
1832
|
+
for (const skill of ec.cursorSkills) {
|
|
1833
|
+
localConfigs.push(`.cursor/skills/${skill.slug}/SKILL.md`);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1646
1836
|
const localState = scanLocalState(process.cwd());
|
|
1647
1837
|
const claudeMcpServers = localState.filter((i) => i.type === "mcp" && i.platform === "claude").map((i) => i.name);
|
|
1648
1838
|
const cursorMcpServers = localState.filter((i) => i.type === "mcp" && i.platform === "cursor").map((i) => i.name);
|
|
@@ -1698,6 +1888,7 @@ async function initCommand(options) {
|
|
|
1698
1888
|
if (isEmpty) {
|
|
1699
1889
|
fingerprint.description = await promptInput("What will you build in this project?");
|
|
1700
1890
|
}
|
|
1891
|
+
await enrichmentPromise;
|
|
1701
1892
|
console.log(chalk3.hex("#6366f1").bold(" Step 4/6 \u2014 Auditing your configs\n"));
|
|
1702
1893
|
console.log(chalk3.dim(" AI is auditing your CLAUDE.md, skills, and rules against your"));
|
|
1703
1894
|
console.log(chalk3.dim(" project's actual codebase and conventions.\n"));
|
|
@@ -1757,25 +1948,41 @@ async function initCommand(options) {
|
|
|
1757
1948
|
genSpinner.succeed(`Setup generated ${chalk3.dim(`in ${timeStr}`)}`);
|
|
1758
1949
|
printSetupSummary(generatedSetup);
|
|
1759
1950
|
console.log(chalk3.hex("#6366f1").bold(" Step 5/6 \u2014 Review\n"));
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
}
|
|
1767
|
-
|
|
1768
|
-
trackEvent("setup_declined");
|
|
1769
|
-
console.log(chalk3.dim("Setup declined. No files were modified."));
|
|
1770
|
-
return;
|
|
1951
|
+
const setupFiles = collectSetupFiles(generatedSetup);
|
|
1952
|
+
const { newFiles, modifiedFiles } = stageFiles(setupFiles, process.cwd());
|
|
1953
|
+
const stagedDir = getStagedProposedDir();
|
|
1954
|
+
const editorOpened = openInEditor(stagedDir);
|
|
1955
|
+
if (editorOpened) {
|
|
1956
|
+
console.log(chalk3.dim(" Proposed files opened in your editor for review."));
|
|
1957
|
+
} else {
|
|
1958
|
+
console.log(chalk3.dim(" Staged files written to .caliber/staged/proposed/ for review."));
|
|
1771
1959
|
}
|
|
1772
|
-
|
|
1960
|
+
console.log(chalk3.dim(` ${chalk3.green(`${newFiles} new`)} / ${chalk3.yellow(`${modifiedFiles} modified`)} file${newFiles + modifiedFiles !== 1 ? "s" : ""}
|
|
1961
|
+
`));
|
|
1962
|
+
let action = await promptReviewAction();
|
|
1963
|
+
while (action === "refine") {
|
|
1773
1964
|
generatedSetup = await refineLoop(generatedSetup, targetAgent);
|
|
1774
1965
|
if (!generatedSetup) {
|
|
1966
|
+
cleanupStaging();
|
|
1775
1967
|
trackEvent("refinement_cancelled");
|
|
1776
1968
|
console.log(chalk3.dim("Refinement cancelled. No files were modified."));
|
|
1777
1969
|
return;
|
|
1778
1970
|
}
|
|
1971
|
+
const updatedFiles = collectSetupFiles(generatedSetup);
|
|
1972
|
+
const restaged = stageFiles(updatedFiles, process.cwd());
|
|
1973
|
+
if (editorOpened) {
|
|
1974
|
+
console.log(chalk3.dim(" Updated files re-staged. Check your editor for changes."));
|
|
1975
|
+
}
|
|
1976
|
+
console.log(chalk3.dim(` ${chalk3.green(`${restaged.newFiles} new`)} / ${chalk3.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
|
|
1977
|
+
`));
|
|
1978
|
+
printSetupSummary(generatedSetup);
|
|
1979
|
+
action = await promptReviewAction();
|
|
1980
|
+
}
|
|
1981
|
+
cleanupStaging();
|
|
1982
|
+
if (action === "decline") {
|
|
1983
|
+
trackEvent("setup_declined");
|
|
1984
|
+
console.log(chalk3.dim("Setup declined. No files were modified."));
|
|
1985
|
+
return;
|
|
1779
1986
|
}
|
|
1780
1987
|
console.log(chalk3.hex("#6366f1").bold(" Step 6/6 \u2014 Apply & sync\n"));
|
|
1781
1988
|
console.log(chalk3.dim(" Writing config files to your project and syncing to Caliber so"));
|
|
@@ -1926,13 +2133,12 @@ async function promptAgent() {
|
|
|
1926
2133
|
]
|
|
1927
2134
|
});
|
|
1928
2135
|
}
|
|
1929
|
-
async function
|
|
2136
|
+
async function promptReviewAction() {
|
|
1930
2137
|
return select({
|
|
1931
2138
|
message: "What would you like to do?",
|
|
1932
2139
|
choices: [
|
|
1933
2140
|
{ name: "Accept and apply", value: "accept" },
|
|
1934
2141
|
{ name: "Refine via chat", value: "refine" },
|
|
1935
|
-
{ name: "Preview a file", value: "preview" },
|
|
1936
2142
|
{ name: "Decline", value: "decline" }
|
|
1937
2143
|
]
|
|
1938
2144
|
});
|
|
@@ -1949,7 +2155,7 @@ function printSetupSummary(setup) {
|
|
|
1949
2155
|
};
|
|
1950
2156
|
if (claude) {
|
|
1951
2157
|
if (claude.claudeMd) {
|
|
1952
|
-
const icon =
|
|
2158
|
+
const icon = fs18.existsSync("CLAUDE.md") ? chalk3.yellow("~") : chalk3.green("+");
|
|
1953
2159
|
const desc = getDescription("CLAUDE.md");
|
|
1954
2160
|
console.log(` ${icon} ${chalk3.bold("CLAUDE.md")}`);
|
|
1955
2161
|
if (desc) {
|
|
@@ -1961,7 +2167,7 @@ function printSetupSummary(setup) {
|
|
|
1961
2167
|
if (Array.isArray(skills) && skills.length > 0) {
|
|
1962
2168
|
for (const skill of skills) {
|
|
1963
2169
|
const skillPath = `.claude/skills/${skill.name.replace(/[^a-z0-9-]/gi, "-").toLowerCase()}.md`;
|
|
1964
|
-
const icon =
|
|
2170
|
+
const icon = fs18.existsSync(skillPath) ? chalk3.yellow("~") : chalk3.green("+");
|
|
1965
2171
|
const desc = getDescription(skillPath);
|
|
1966
2172
|
console.log(` ${icon} ${chalk3.bold(skillPath)}`);
|
|
1967
2173
|
console.log(chalk3.dim(` ${desc || summarizeSkill(skill)}`));
|
|
@@ -1970,7 +2176,7 @@ function printSetupSummary(setup) {
|
|
|
1970
2176
|
}
|
|
1971
2177
|
const mcpServers = claude.mcpServers;
|
|
1972
2178
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
1973
|
-
const icon =
|
|
2179
|
+
const icon = fs18.existsSync(".mcp.json") ? chalk3.yellow("~") : chalk3.green("+");
|
|
1974
2180
|
const serverNames = Object.keys(mcpServers);
|
|
1975
2181
|
const desc = getDescription(".mcp.json");
|
|
1976
2182
|
console.log(` ${icon} ${chalk3.bold(".mcp.json")}`);
|
|
@@ -1980,17 +2186,33 @@ function printSetupSummary(setup) {
|
|
|
1980
2186
|
}
|
|
1981
2187
|
if (cursor) {
|
|
1982
2188
|
if (cursor.cursorrules) {
|
|
1983
|
-
const icon =
|
|
2189
|
+
const icon = fs18.existsSync(".cursorrules") ? chalk3.yellow("~") : chalk3.green("+");
|
|
1984
2190
|
const desc = getDescription(".cursorrules");
|
|
1985
2191
|
console.log(` ${icon} ${chalk3.bold(".cursorrules")}`);
|
|
1986
2192
|
if (desc) console.log(chalk3.dim(` ${desc}`));
|
|
1987
2193
|
console.log("");
|
|
1988
2194
|
}
|
|
2195
|
+
const cursorSkills = cursor.skills;
|
|
2196
|
+
if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
|
|
2197
|
+
for (const skill of cursorSkills) {
|
|
2198
|
+
const skillPath = `.cursor/skills/${skill.slug}/SKILL.md`;
|
|
2199
|
+
const icon = fs18.existsSync(skillPath) ? chalk3.yellow("~") : chalk3.green("+");
|
|
2200
|
+
const desc = getDescription(skillPath);
|
|
2201
|
+
console.log(` ${icon} ${chalk3.bold(skillPath)}`);
|
|
2202
|
+
if (desc) {
|
|
2203
|
+
console.log(chalk3.dim(` ${desc}`));
|
|
2204
|
+
} else {
|
|
2205
|
+
const firstLine = skill.content.split("\n").filter((l) => l.trim() && !l.trim().startsWith("#") && !l.trim().startsWith("---"))[0];
|
|
2206
|
+
if (firstLine) console.log(chalk3.dim(` ${firstLine.trim().slice(0, 80)}`));
|
|
2207
|
+
}
|
|
2208
|
+
console.log("");
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
1989
2211
|
const rules = cursor.rules;
|
|
1990
2212
|
if (Array.isArray(rules) && rules.length > 0) {
|
|
1991
2213
|
for (const rule of rules) {
|
|
1992
2214
|
const rulePath = `.cursor/rules/${rule.filename}`;
|
|
1993
|
-
const icon =
|
|
2215
|
+
const icon = fs18.existsSync(rulePath) ? chalk3.yellow("~") : chalk3.green("+");
|
|
1994
2216
|
const desc = getDescription(rulePath);
|
|
1995
2217
|
console.log(` ${icon} ${chalk3.bold(rulePath)}`);
|
|
1996
2218
|
if (desc) {
|
|
@@ -2004,7 +2226,7 @@ function printSetupSummary(setup) {
|
|
|
2004
2226
|
}
|
|
2005
2227
|
const mcpServers = cursor.mcpServers;
|
|
2006
2228
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
2007
|
-
const icon =
|
|
2229
|
+
const icon = fs18.existsSync(".cursor/mcp.json") ? chalk3.yellow("~") : chalk3.green("+");
|
|
2008
2230
|
const serverNames = Object.keys(mcpServers);
|
|
2009
2231
|
const desc = getDescription(".cursor/mcp.json");
|
|
2010
2232
|
console.log(` ${icon} ${chalk3.bold(".cursor/mcp.json")}`);
|
|
@@ -2045,6 +2267,12 @@ function collectSetupFiles(setup) {
|
|
|
2045
2267
|
}
|
|
2046
2268
|
if (cursor) {
|
|
2047
2269
|
if (cursor.cursorrules) files.push({ path: ".cursorrules", content: cursor.cursorrules });
|
|
2270
|
+
const cursorSkills = cursor.skills;
|
|
2271
|
+
if (Array.isArray(cursorSkills)) {
|
|
2272
|
+
for (const skill of cursorSkills) {
|
|
2273
|
+
files.push({ path: `.cursor/skills/${skill.slug}/SKILL.md`, content: skill.content });
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2048
2276
|
const rules = cursor.rules;
|
|
2049
2277
|
if (Array.isArray(rules)) {
|
|
2050
2278
|
for (const rule of rules) {
|
|
@@ -2057,60 +2285,6 @@ function collectSetupFiles(setup) {
|
|
|
2057
2285
|
}
|
|
2058
2286
|
return files;
|
|
2059
2287
|
}
|
|
2060
|
-
function previewNewFile(filePath, content, maxLines = 40) {
|
|
2061
|
-
const lines = content.split("\n");
|
|
2062
|
-
const displayLines = lines.slice(0, maxLines);
|
|
2063
|
-
console.log("");
|
|
2064
|
-
console.log(` ${chalk3.green("+ new")} ${chalk3.bold(filePath)}`);
|
|
2065
|
-
console.log(chalk3.dim(" \u2500".repeat(30)));
|
|
2066
|
-
for (const line of displayLines) {
|
|
2067
|
-
console.log(` ${chalk3.green("+")} ${line}`);
|
|
2068
|
-
}
|
|
2069
|
-
if (lines.length > maxLines) {
|
|
2070
|
-
console.log("");
|
|
2071
|
-
console.log(chalk3.dim(` ... ${lines.length - maxLines} more lines (${lines.length} total)`));
|
|
2072
|
-
}
|
|
2073
|
-
console.log("");
|
|
2074
|
-
}
|
|
2075
|
-
function previewDiff(filePath, oldContent, newContent) {
|
|
2076
|
-
const patch = createTwoFilesPatch(filePath, filePath, oldContent, newContent, "current", "proposed", { context: 3 });
|
|
2077
|
-
const patchLines = patch.split("\n").slice(2);
|
|
2078
|
-
console.log("");
|
|
2079
|
-
console.log(` ${chalk3.yellow("~ modified")} ${chalk3.bold(filePath)}`);
|
|
2080
|
-
console.log(chalk3.dim(" \u2500".repeat(30)));
|
|
2081
|
-
for (const line of patchLines) {
|
|
2082
|
-
if (line.startsWith("@@")) {
|
|
2083
|
-
console.log(` ${chalk3.cyan(line)}`);
|
|
2084
|
-
} else if (line.startsWith("+")) {
|
|
2085
|
-
console.log(` ${chalk3.green(line)}`);
|
|
2086
|
-
} else if (line.startsWith("-")) {
|
|
2087
|
-
console.log(` ${chalk3.red(line)}`);
|
|
2088
|
-
} else {
|
|
2089
|
-
console.log(` ${chalk3.dim(line)}`);
|
|
2090
|
-
}
|
|
2091
|
-
}
|
|
2092
|
-
console.log("");
|
|
2093
|
-
}
|
|
2094
|
-
async function promptFilePreview(setup) {
|
|
2095
|
-
const files = collectSetupFiles(setup);
|
|
2096
|
-
if (files.length === 0) {
|
|
2097
|
-
console.log(chalk3.dim("\n No files to preview.\n"));
|
|
2098
|
-
return;
|
|
2099
|
-
}
|
|
2100
|
-
const choices = files.map((f, i) => {
|
|
2101
|
-
const exists = fs16.existsSync(f.path);
|
|
2102
|
-
const icon = exists ? chalk3.yellow("~") : chalk3.green("+");
|
|
2103
|
-
return { name: `${icon} ${f.path}`, value: i };
|
|
2104
|
-
});
|
|
2105
|
-
const choice = await select({ message: "Which file to preview?", choices });
|
|
2106
|
-
const file = files[choice];
|
|
2107
|
-
if (fs16.existsSync(file.path)) {
|
|
2108
|
-
const existing = fs16.readFileSync(file.path, "utf-8");
|
|
2109
|
-
previewDiff(file.path, existing, file.content);
|
|
2110
|
-
} else {
|
|
2111
|
-
previewNewFile(file.path, file.content);
|
|
2112
|
-
}
|
|
2113
|
-
}
|
|
2114
2288
|
|
|
2115
2289
|
// src/commands/undo.ts
|
|
2116
2290
|
import chalk4 from "chalk";
|
|
@@ -2146,7 +2320,7 @@ function undoCommand() {
|
|
|
2146
2320
|
|
|
2147
2321
|
// src/commands/status.ts
|
|
2148
2322
|
import chalk5 from "chalk";
|
|
2149
|
-
import
|
|
2323
|
+
import fs19 from "fs";
|
|
2150
2324
|
async function statusCommand(options) {
|
|
2151
2325
|
const auth2 = getStoredAuth();
|
|
2152
2326
|
const manifest = readManifest();
|
|
@@ -2171,7 +2345,7 @@ async function statusCommand(options) {
|
|
|
2171
2345
|
}
|
|
2172
2346
|
console.log(` Files managed: ${chalk5.cyan(manifest.entries.length.toString())}`);
|
|
2173
2347
|
for (const entry of manifest.entries) {
|
|
2174
|
-
const exists =
|
|
2348
|
+
const exists = fs19.existsSync(entry.path);
|
|
2175
2349
|
const icon = exists ? chalk5.green("\u2713") : chalk5.red("\u2717");
|
|
2176
2350
|
console.log(` ${icon} ${entry.path} (${entry.action})`);
|
|
2177
2351
|
}
|
|
@@ -2319,7 +2493,7 @@ function detectLocalPlatforms() {
|
|
|
2319
2493
|
}
|
|
2320
2494
|
function getSkillPath(platform, slug) {
|
|
2321
2495
|
if (platform === "cursor") {
|
|
2322
|
-
return join(".cursor", "
|
|
2496
|
+
return join(".cursor", "skills", slug, "SKILL.md");
|
|
2323
2497
|
}
|
|
2324
2498
|
return join(".claude", "skills", `${slug}.md`);
|
|
2325
2499
|
}
|
|
@@ -2806,13 +2980,13 @@ async function diffCommand(options) {
|
|
|
2806
2980
|
}
|
|
2807
2981
|
|
|
2808
2982
|
// src/commands/refresh.ts
|
|
2809
|
-
import
|
|
2810
|
-
import
|
|
2983
|
+
import fs21 from "fs";
|
|
2984
|
+
import path18 from "path";
|
|
2811
2985
|
import chalk11 from "chalk";
|
|
2812
2986
|
import ora8 from "ora";
|
|
2813
2987
|
|
|
2814
2988
|
// src/lib/git-diff.ts
|
|
2815
|
-
import { execSync as
|
|
2989
|
+
import { execSync as execSync4 } from "child_process";
|
|
2816
2990
|
var MAX_DIFF_BYTES = 1e5;
|
|
2817
2991
|
var DOC_PATTERNS = [
|
|
2818
2992
|
"CLAUDE.md",
|
|
@@ -2826,7 +3000,7 @@ function excludeArgs() {
|
|
|
2826
3000
|
}
|
|
2827
3001
|
function safeExec(cmd) {
|
|
2828
3002
|
try {
|
|
2829
|
-
return
|
|
3003
|
+
return execSync4(cmd, {
|
|
2830
3004
|
encoding: "utf-8",
|
|
2831
3005
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2832
3006
|
maxBuffer: 10 * 1024 * 1024
|
|
@@ -2884,37 +3058,37 @@ function collectDiff(lastSha) {
|
|
|
2884
3058
|
}
|
|
2885
3059
|
|
|
2886
3060
|
// src/writers/refresh.ts
|
|
2887
|
-
import
|
|
2888
|
-
import
|
|
3061
|
+
import fs20 from "fs";
|
|
3062
|
+
import path17 from "path";
|
|
2889
3063
|
function writeRefreshDocs(docs) {
|
|
2890
3064
|
const written = [];
|
|
2891
3065
|
if (docs.claudeMd) {
|
|
2892
|
-
|
|
3066
|
+
fs20.writeFileSync("CLAUDE.md", docs.claudeMd);
|
|
2893
3067
|
written.push("CLAUDE.md");
|
|
2894
3068
|
}
|
|
2895
3069
|
if (docs.readmeMd) {
|
|
2896
|
-
|
|
3070
|
+
fs20.writeFileSync("README.md", docs.readmeMd);
|
|
2897
3071
|
written.push("README.md");
|
|
2898
3072
|
}
|
|
2899
3073
|
if (docs.cursorrules) {
|
|
2900
|
-
|
|
3074
|
+
fs20.writeFileSync(".cursorrules", docs.cursorrules);
|
|
2901
3075
|
written.push(".cursorrules");
|
|
2902
3076
|
}
|
|
2903
3077
|
if (docs.cursorRules) {
|
|
2904
|
-
const rulesDir =
|
|
2905
|
-
if (!
|
|
3078
|
+
const rulesDir = path17.join(".cursor", "rules");
|
|
3079
|
+
if (!fs20.existsSync(rulesDir)) fs20.mkdirSync(rulesDir, { recursive: true });
|
|
2906
3080
|
for (const rule of docs.cursorRules) {
|
|
2907
|
-
const filePath =
|
|
2908
|
-
|
|
3081
|
+
const filePath = path17.join(rulesDir, rule.filename);
|
|
3082
|
+
fs20.writeFileSync(filePath, rule.content);
|
|
2909
3083
|
written.push(filePath);
|
|
2910
3084
|
}
|
|
2911
3085
|
}
|
|
2912
3086
|
if (docs.claudeSkills) {
|
|
2913
|
-
const skillsDir =
|
|
2914
|
-
if (!
|
|
3087
|
+
const skillsDir = path17.join(".claude", "skills");
|
|
3088
|
+
if (!fs20.existsSync(skillsDir)) fs20.mkdirSync(skillsDir, { recursive: true });
|
|
2915
3089
|
for (const skill of docs.claudeSkills) {
|
|
2916
|
-
const filePath =
|
|
2917
|
-
|
|
3090
|
+
const filePath = path17.join(skillsDir, skill.filename);
|
|
3091
|
+
fs20.writeFileSync(filePath, skill.content);
|
|
2918
3092
|
written.push(filePath);
|
|
2919
3093
|
}
|
|
2920
3094
|
}
|
|
@@ -2928,11 +3102,11 @@ function log(quiet, ...args) {
|
|
|
2928
3102
|
function discoverGitRepos(parentDir) {
|
|
2929
3103
|
const repos = [];
|
|
2930
3104
|
try {
|
|
2931
|
-
const entries =
|
|
3105
|
+
const entries = fs21.readdirSync(parentDir, { withFileTypes: true });
|
|
2932
3106
|
for (const entry of entries) {
|
|
2933
3107
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
2934
|
-
const childPath =
|
|
2935
|
-
if (
|
|
3108
|
+
const childPath = path18.join(parentDir, entry.name);
|
|
3109
|
+
if (fs21.existsSync(path18.join(childPath, ".git"))) {
|
|
2936
3110
|
repos.push(childPath);
|
|
2937
3111
|
}
|
|
2938
3112
|
}
|
|
@@ -3035,7 +3209,7 @@ async function refreshCommand(options) {
|
|
|
3035
3209
|
`));
|
|
3036
3210
|
const originalDir = process.cwd();
|
|
3037
3211
|
for (const repo of repos) {
|
|
3038
|
-
const repoName =
|
|
3212
|
+
const repoName = path18.basename(repo);
|
|
3039
3213
|
try {
|
|
3040
3214
|
process.chdir(repo);
|
|
3041
3215
|
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
@@ -3177,9 +3351,9 @@ async function reviewCommand(message, options) {
|
|
|
3177
3351
|
}
|
|
3178
3352
|
|
|
3179
3353
|
// src/cli.ts
|
|
3180
|
-
var __dirname2 =
|
|
3354
|
+
var __dirname2 = path19.dirname(fileURLToPath3(import.meta.url));
|
|
3181
3355
|
var pkg3 = JSON.parse(
|
|
3182
|
-
|
|
3356
|
+
fs22.readFileSync(path19.resolve(__dirname2, "..", "package.json"), "utf-8")
|
|
3183
3357
|
);
|
|
3184
3358
|
var program = new Command();
|
|
3185
3359
|
var displayVersion = process.env.CALIBER_LOCAL ? `${pkg3.version}-local` : pkg3.version;
|
|
@@ -3201,22 +3375,22 @@ hooks.command("remove").description("Remove auto-refresh SessionEnd hook").actio
|
|
|
3201
3375
|
hooks.command("status").description("Check if auto-refresh hook is installed").action(hooksStatusCommand);
|
|
3202
3376
|
|
|
3203
3377
|
// src/utils/version-check.ts
|
|
3204
|
-
import
|
|
3205
|
-
import
|
|
3378
|
+
import fs23 from "fs";
|
|
3379
|
+
import path20 from "path";
|
|
3206
3380
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
3207
|
-
import { execSync as
|
|
3381
|
+
import { execSync as execSync5 } from "child_process";
|
|
3208
3382
|
import chalk14 from "chalk";
|
|
3209
3383
|
import ora10 from "ora";
|
|
3210
3384
|
import confirm3 from "@inquirer/confirm";
|
|
3211
|
-
var __dirname_vc =
|
|
3385
|
+
var __dirname_vc = path20.dirname(fileURLToPath4(import.meta.url));
|
|
3212
3386
|
var pkg4 = JSON.parse(
|
|
3213
|
-
|
|
3387
|
+
fs23.readFileSync(path20.resolve(__dirname_vc, "..", "package.json"), "utf-8")
|
|
3214
3388
|
);
|
|
3215
3389
|
function getInstalledVersion() {
|
|
3216
3390
|
try {
|
|
3217
|
-
const globalRoot =
|
|
3218
|
-
const pkgPath =
|
|
3219
|
-
return JSON.parse(
|
|
3391
|
+
const globalRoot = execSync5("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
3392
|
+
const pkgPath = path20.join(globalRoot, "@caliber-ai", "cli", "package.json");
|
|
3393
|
+
return JSON.parse(fs23.readFileSync(pkgPath, "utf-8")).version;
|
|
3220
3394
|
} catch {
|
|
3221
3395
|
return null;
|
|
3222
3396
|
}
|
|
@@ -3259,7 +3433,7 @@ Update available: ${current} -> ${latest}`)
|
|
|
3259
3433
|
}
|
|
3260
3434
|
const spinner = ora10("Updating @caliber-ai/cli...").start();
|
|
3261
3435
|
try {
|
|
3262
|
-
|
|
3436
|
+
execSync5(`npm install -g @caliber-ai/cli@${latest} --prefer-online`, { stdio: "pipe", timeout: 6e4 });
|
|
3263
3437
|
const installed = getInstalledVersion();
|
|
3264
3438
|
if (installed !== latest) {
|
|
3265
3439
|
spinner.fail(`Update incomplete \u2014 got ${installed ?? "unknown"}, expected ${latest}`);
|
|
@@ -3272,7 +3446,7 @@ Update available: ${current} -> ${latest}`)
|
|
|
3272
3446
|
console.log(chalk14.dim(`
|
|
3273
3447
|
Restarting: caliber ${args.join(" ")}
|
|
3274
3448
|
`));
|
|
3275
|
-
|
|
3449
|
+
execSync5(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
|
|
3276
3450
|
stdio: "inherit",
|
|
3277
3451
|
env: { ...process.env, CALIBER_SKIP_UPDATE_CHECK: "1" }
|
|
3278
3452
|
});
|