@caliber-ai/cli 0.20.2 → 0.21.1
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 +260 -296
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -84,7 +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
|
|
87
|
+
import fs17 from "fs";
|
|
88
88
|
|
|
89
89
|
// src/auth/token-store.ts
|
|
90
90
|
init_constants();
|
|
@@ -1115,219 +1115,77 @@ async function enrichFingerprintWithLLM(fingerprint, dir) {
|
|
|
1115
1115
|
}
|
|
1116
1116
|
}
|
|
1117
1117
|
|
|
1118
|
-
// src/scanner/index.ts
|
|
1119
|
-
import fs9 from "fs";
|
|
1120
|
-
import path10 from "path";
|
|
1121
|
-
import crypto3 from "crypto";
|
|
1122
|
-
function scanLocalState(dir) {
|
|
1123
|
-
const items = [];
|
|
1124
|
-
const claudeMdPath = path10.join(dir, "CLAUDE.md");
|
|
1125
|
-
if (fs9.existsSync(claudeMdPath)) {
|
|
1126
|
-
items.push({
|
|
1127
|
-
type: "rule",
|
|
1128
|
-
platform: "claude",
|
|
1129
|
-
name: "CLAUDE.md",
|
|
1130
|
-
contentHash: hashFile(claudeMdPath),
|
|
1131
|
-
path: claudeMdPath
|
|
1132
|
-
});
|
|
1133
|
-
}
|
|
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);
|
|
1138
|
-
items.push({
|
|
1139
|
-
type: "skill",
|
|
1140
|
-
platform: "claude",
|
|
1141
|
-
name: file,
|
|
1142
|
-
contentHash: hashFile(filePath),
|
|
1143
|
-
path: filePath
|
|
1144
|
-
});
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
const mcpJsonPath = path10.join(dir, ".mcp.json");
|
|
1148
|
-
if (fs9.existsSync(mcpJsonPath)) {
|
|
1149
|
-
try {
|
|
1150
|
-
const mcpJson = JSON.parse(fs9.readFileSync(mcpJsonPath, "utf-8"));
|
|
1151
|
-
if (mcpJson.mcpServers) {
|
|
1152
|
-
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
1153
|
-
items.push({
|
|
1154
|
-
type: "mcp",
|
|
1155
|
-
platform: "claude",
|
|
1156
|
-
name,
|
|
1157
|
-
contentHash: hashJson(mcpJson.mcpServers[name]),
|
|
1158
|
-
path: mcpJsonPath
|
|
1159
|
-
});
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
} catch {
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
const cursorrulesPath = path10.join(dir, ".cursorrules");
|
|
1166
|
-
if (fs9.existsSync(cursorrulesPath)) {
|
|
1167
|
-
items.push({
|
|
1168
|
-
type: "rule",
|
|
1169
|
-
platform: "cursor",
|
|
1170
|
-
name: ".cursorrules",
|
|
1171
|
-
contentHash: hashFile(cursorrulesPath),
|
|
1172
|
-
path: cursorrulesPath
|
|
1173
|
-
});
|
|
1174
|
-
}
|
|
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);
|
|
1179
|
-
items.push({
|
|
1180
|
-
type: "rule",
|
|
1181
|
-
platform: "cursor",
|
|
1182
|
-
name: file,
|
|
1183
|
-
contentHash: hashFile(filePath),
|
|
1184
|
-
path: filePath
|
|
1185
|
-
});
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
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)) {
|
|
1208
|
-
try {
|
|
1209
|
-
const mcpJson = JSON.parse(fs9.readFileSync(cursorMcpPath, "utf-8"));
|
|
1210
|
-
if (mcpJson.mcpServers) {
|
|
1211
|
-
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
1212
|
-
items.push({
|
|
1213
|
-
type: "mcp",
|
|
1214
|
-
platform: "cursor",
|
|
1215
|
-
name,
|
|
1216
|
-
contentHash: hashJson(mcpJson.mcpServers[name]),
|
|
1217
|
-
path: cursorMcpPath
|
|
1218
|
-
});
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
} catch {
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
return items;
|
|
1225
|
-
}
|
|
1226
|
-
function compareState(serverItems, localItems) {
|
|
1227
|
-
const installed = [];
|
|
1228
|
-
const missing = [];
|
|
1229
|
-
const outdated = [];
|
|
1230
|
-
const extra = [];
|
|
1231
|
-
const localMap = /* @__PURE__ */ new Map();
|
|
1232
|
-
for (const item of localItems) {
|
|
1233
|
-
localMap.set(`${item.type}:${item.platform}:${item.name}`, item);
|
|
1234
|
-
}
|
|
1235
|
-
for (const server of serverItems) {
|
|
1236
|
-
const key = `${server.type}:${server.platform}:${server.name}`;
|
|
1237
|
-
const local = localMap.get(key);
|
|
1238
|
-
localMap.delete(key);
|
|
1239
|
-
if (!local) {
|
|
1240
|
-
missing.push(server);
|
|
1241
|
-
} else if (local.contentHash !== server.content_hash) {
|
|
1242
|
-
outdated.push({ server, local });
|
|
1243
|
-
} else {
|
|
1244
|
-
installed.push({ server, local });
|
|
1245
|
-
}
|
|
1246
|
-
}
|
|
1247
|
-
for (const local of localMap.values()) {
|
|
1248
|
-
extra.push(local);
|
|
1249
|
-
}
|
|
1250
|
-
return { installed, missing, outdated, extra };
|
|
1251
|
-
}
|
|
1252
|
-
function hashFile(filePath) {
|
|
1253
|
-
const text = fs9.readFileSync(filePath, "utf-8");
|
|
1254
|
-
return crypto3.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
|
|
1255
|
-
}
|
|
1256
|
-
function hashJson(obj) {
|
|
1257
|
-
return crypto3.createHash("sha256").update(JSON.stringify(obj)).digest("hex");
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
1118
|
// src/writers/index.ts
|
|
1261
|
-
import
|
|
1119
|
+
import fs13 from "fs";
|
|
1262
1120
|
|
|
1263
1121
|
// src/writers/claude/index.ts
|
|
1264
|
-
import
|
|
1265
|
-
import
|
|
1122
|
+
import fs9 from "fs";
|
|
1123
|
+
import path10 from "path";
|
|
1266
1124
|
function writeClaudeConfig(config) {
|
|
1267
1125
|
const written = [];
|
|
1268
|
-
|
|
1126
|
+
fs9.writeFileSync("CLAUDE.md", config.claudeMd);
|
|
1269
1127
|
written.push("CLAUDE.md");
|
|
1270
1128
|
if (config.skills?.length) {
|
|
1271
|
-
const skillsDir =
|
|
1272
|
-
if (!
|
|
1129
|
+
const skillsDir = path10.join(".claude", "skills");
|
|
1130
|
+
if (!fs9.existsSync(skillsDir)) fs9.mkdirSync(skillsDir, { recursive: true });
|
|
1273
1131
|
for (const skill of config.skills) {
|
|
1274
1132
|
const filename = `${skill.name.replace(/[^a-z0-9-]/gi, "-").toLowerCase()}.md`;
|
|
1275
|
-
const skillPath =
|
|
1276
|
-
|
|
1133
|
+
const skillPath = path10.join(skillsDir, filename);
|
|
1134
|
+
fs9.writeFileSync(skillPath, skill.content);
|
|
1277
1135
|
written.push(skillPath);
|
|
1278
1136
|
}
|
|
1279
1137
|
}
|
|
1280
1138
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
1281
1139
|
let existingServers = {};
|
|
1282
1140
|
try {
|
|
1283
|
-
if (
|
|
1284
|
-
const existing = JSON.parse(
|
|
1141
|
+
if (fs9.existsSync(".mcp.json")) {
|
|
1142
|
+
const existing = JSON.parse(fs9.readFileSync(".mcp.json", "utf-8"));
|
|
1285
1143
|
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
1286
1144
|
}
|
|
1287
1145
|
} catch {
|
|
1288
1146
|
}
|
|
1289
1147
|
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
1290
|
-
|
|
1148
|
+
fs9.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
1291
1149
|
written.push(".mcp.json");
|
|
1292
1150
|
}
|
|
1293
1151
|
return written;
|
|
1294
1152
|
}
|
|
1295
1153
|
|
|
1296
1154
|
// src/writers/cursor/index.ts
|
|
1297
|
-
import
|
|
1298
|
-
import
|
|
1155
|
+
import fs10 from "fs";
|
|
1156
|
+
import path11 from "path";
|
|
1299
1157
|
function writeCursorConfig(config) {
|
|
1300
1158
|
const written = [];
|
|
1301
1159
|
if (config.cursorrules) {
|
|
1302
|
-
|
|
1160
|
+
fs10.writeFileSync(".cursorrules", config.cursorrules);
|
|
1303
1161
|
written.push(".cursorrules");
|
|
1304
1162
|
}
|
|
1305
1163
|
if (config.rules?.length) {
|
|
1306
|
-
const rulesDir =
|
|
1307
|
-
if (!
|
|
1164
|
+
const rulesDir = path11.join(".cursor", "rules");
|
|
1165
|
+
if (!fs10.existsSync(rulesDir)) fs10.mkdirSync(rulesDir, { recursive: true });
|
|
1308
1166
|
for (const rule of config.rules) {
|
|
1309
|
-
const rulePath =
|
|
1310
|
-
|
|
1167
|
+
const rulePath = path11.join(rulesDir, rule.filename);
|
|
1168
|
+
fs10.writeFileSync(rulePath, rule.content);
|
|
1311
1169
|
written.push(rulePath);
|
|
1312
1170
|
}
|
|
1313
1171
|
}
|
|
1314
1172
|
if (config.skills?.length) {
|
|
1315
1173
|
for (const skill of config.skills) {
|
|
1316
|
-
const skillDir =
|
|
1317
|
-
if (!
|
|
1318
|
-
const skillPath =
|
|
1319
|
-
|
|
1174
|
+
const skillDir = path11.join(".cursor", "skills", skill.slug);
|
|
1175
|
+
if (!fs10.existsSync(skillDir)) fs10.mkdirSync(skillDir, { recursive: true });
|
|
1176
|
+
const skillPath = path11.join(skillDir, "SKILL.md");
|
|
1177
|
+
fs10.writeFileSync(skillPath, skill.content);
|
|
1320
1178
|
written.push(skillPath);
|
|
1321
1179
|
}
|
|
1322
1180
|
}
|
|
1323
1181
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
1324
1182
|
const cursorDir = ".cursor";
|
|
1325
|
-
if (!
|
|
1326
|
-
const mcpPath =
|
|
1183
|
+
if (!fs10.existsSync(cursorDir)) fs10.mkdirSync(cursorDir, { recursive: true });
|
|
1184
|
+
const mcpPath = path11.join(cursorDir, "mcp.json");
|
|
1327
1185
|
let existingServers = {};
|
|
1328
1186
|
try {
|
|
1329
|
-
if (
|
|
1330
|
-
const existing = JSON.parse(
|
|
1187
|
+
if (fs10.existsSync(mcpPath)) {
|
|
1188
|
+
const existing = JSON.parse(fs10.readFileSync(mcpPath, "utf-8"));
|
|
1331
1189
|
if (existing.mcpServers) {
|
|
1332
1190
|
existingServers = existing.mcpServers;
|
|
1333
1191
|
}
|
|
@@ -1335,7 +1193,7 @@ function writeCursorConfig(config) {
|
|
|
1335
1193
|
} catch {
|
|
1336
1194
|
}
|
|
1337
1195
|
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
1338
|
-
|
|
1196
|
+
fs10.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
1339
1197
|
written.push(mcpPath);
|
|
1340
1198
|
}
|
|
1341
1199
|
return written;
|
|
@@ -1343,62 +1201,62 @@ function writeCursorConfig(config) {
|
|
|
1343
1201
|
|
|
1344
1202
|
// src/writers/backup.ts
|
|
1345
1203
|
init_constants();
|
|
1346
|
-
import
|
|
1347
|
-
import
|
|
1204
|
+
import fs11 from "fs";
|
|
1205
|
+
import path12 from "path";
|
|
1348
1206
|
function createBackup(files) {
|
|
1349
1207
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1350
|
-
const backupDir =
|
|
1208
|
+
const backupDir = path12.join(BACKUPS_DIR, timestamp);
|
|
1351
1209
|
for (const file of files) {
|
|
1352
|
-
if (!
|
|
1353
|
-
const dest =
|
|
1354
|
-
const destDir =
|
|
1355
|
-
if (!
|
|
1356
|
-
|
|
1210
|
+
if (!fs11.existsSync(file)) continue;
|
|
1211
|
+
const dest = path12.join(backupDir, file);
|
|
1212
|
+
const destDir = path12.dirname(dest);
|
|
1213
|
+
if (!fs11.existsSync(destDir)) {
|
|
1214
|
+
fs11.mkdirSync(destDir, { recursive: true });
|
|
1357
1215
|
}
|
|
1358
|
-
|
|
1216
|
+
fs11.copyFileSync(file, dest);
|
|
1359
1217
|
}
|
|
1360
1218
|
return backupDir;
|
|
1361
1219
|
}
|
|
1362
1220
|
function restoreBackup(backupDir, file) {
|
|
1363
|
-
const backupFile =
|
|
1364
|
-
if (!
|
|
1365
|
-
const destDir =
|
|
1366
|
-
if (!
|
|
1367
|
-
|
|
1221
|
+
const backupFile = path12.join(backupDir, file);
|
|
1222
|
+
if (!fs11.existsSync(backupFile)) return false;
|
|
1223
|
+
const destDir = path12.dirname(file);
|
|
1224
|
+
if (!fs11.existsSync(destDir)) {
|
|
1225
|
+
fs11.mkdirSync(destDir, { recursive: true });
|
|
1368
1226
|
}
|
|
1369
|
-
|
|
1227
|
+
fs11.copyFileSync(backupFile, file);
|
|
1370
1228
|
return true;
|
|
1371
1229
|
}
|
|
1372
1230
|
|
|
1373
1231
|
// src/writers/manifest.ts
|
|
1374
1232
|
init_constants();
|
|
1375
|
-
import
|
|
1376
|
-
import
|
|
1233
|
+
import fs12 from "fs";
|
|
1234
|
+
import crypto3 from "crypto";
|
|
1377
1235
|
function readManifest() {
|
|
1378
1236
|
try {
|
|
1379
|
-
if (!
|
|
1380
|
-
return JSON.parse(
|
|
1237
|
+
if (!fs12.existsSync(MANIFEST_FILE)) return null;
|
|
1238
|
+
return JSON.parse(fs12.readFileSync(MANIFEST_FILE, "utf-8"));
|
|
1381
1239
|
} catch {
|
|
1382
1240
|
return null;
|
|
1383
1241
|
}
|
|
1384
1242
|
}
|
|
1385
1243
|
function writeManifest(manifest) {
|
|
1386
|
-
if (!
|
|
1387
|
-
|
|
1244
|
+
if (!fs12.existsSync(CALIBER_DIR)) {
|
|
1245
|
+
fs12.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
1388
1246
|
}
|
|
1389
|
-
|
|
1247
|
+
fs12.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
|
|
1390
1248
|
}
|
|
1391
1249
|
function fileChecksum(filePath) {
|
|
1392
|
-
const content =
|
|
1393
|
-
return
|
|
1250
|
+
const content = fs12.readFileSync(filePath);
|
|
1251
|
+
return crypto3.createHash("sha256").update(content).digest("hex");
|
|
1394
1252
|
}
|
|
1395
1253
|
|
|
1396
1254
|
// src/writers/index.ts
|
|
1397
1255
|
function writeSetup(setup) {
|
|
1398
1256
|
const filesToWrite = getFilesToWrite(setup);
|
|
1399
|
-
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) =>
|
|
1257
|
+
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs13.existsSync(f));
|
|
1400
1258
|
const existingFiles = [
|
|
1401
|
-
...filesToWrite.filter((f) =>
|
|
1259
|
+
...filesToWrite.filter((f) => fs13.existsSync(f)),
|
|
1402
1260
|
...filesToDelete
|
|
1403
1261
|
];
|
|
1404
1262
|
const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
|
|
@@ -1411,7 +1269,7 @@ function writeSetup(setup) {
|
|
|
1411
1269
|
}
|
|
1412
1270
|
const deleted = [];
|
|
1413
1271
|
for (const filePath of filesToDelete) {
|
|
1414
|
-
|
|
1272
|
+
fs13.unlinkSync(filePath);
|
|
1415
1273
|
deleted.push(filePath);
|
|
1416
1274
|
}
|
|
1417
1275
|
ensureGitignore();
|
|
@@ -1441,8 +1299,8 @@ function undoSetup() {
|
|
|
1441
1299
|
const removed = [];
|
|
1442
1300
|
for (const entry of manifest.entries) {
|
|
1443
1301
|
if (entry.action === "created") {
|
|
1444
|
-
if (
|
|
1445
|
-
|
|
1302
|
+
if (fs13.existsSync(entry.path)) {
|
|
1303
|
+
fs13.unlinkSync(entry.path);
|
|
1446
1304
|
removed.push(entry.path);
|
|
1447
1305
|
}
|
|
1448
1306
|
} else if ((entry.action === "modified" || entry.action === "deleted") && manifest.backupDir) {
|
|
@@ -1452,8 +1310,8 @@ function undoSetup() {
|
|
|
1452
1310
|
}
|
|
1453
1311
|
}
|
|
1454
1312
|
const { MANIFEST_FILE: MANIFEST_FILE2 } = (init_constants(), __toCommonJS(constants_exports));
|
|
1455
|
-
if (
|
|
1456
|
-
|
|
1313
|
+
if (fs13.existsSync(MANIFEST_FILE2)) {
|
|
1314
|
+
fs13.unlinkSync(MANIFEST_FILE2);
|
|
1457
1315
|
}
|
|
1458
1316
|
return { restored, removed };
|
|
1459
1317
|
}
|
|
@@ -1482,37 +1340,37 @@ function getFilesToWrite(setup) {
|
|
|
1482
1340
|
}
|
|
1483
1341
|
function ensureGitignore() {
|
|
1484
1342
|
const gitignorePath = ".gitignore";
|
|
1485
|
-
if (
|
|
1486
|
-
const content =
|
|
1343
|
+
if (fs13.existsSync(gitignorePath)) {
|
|
1344
|
+
const content = fs13.readFileSync(gitignorePath, "utf-8");
|
|
1487
1345
|
if (!content.includes(".caliber/")) {
|
|
1488
|
-
|
|
1346
|
+
fs13.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
|
|
1489
1347
|
}
|
|
1490
1348
|
} else {
|
|
1491
|
-
|
|
1349
|
+
fs13.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
|
|
1492
1350
|
}
|
|
1493
1351
|
}
|
|
1494
1352
|
|
|
1495
1353
|
// src/writers/staging.ts
|
|
1496
1354
|
init_constants();
|
|
1497
|
-
import
|
|
1498
|
-
import
|
|
1499
|
-
var STAGED_DIR =
|
|
1500
|
-
var PROPOSED_DIR =
|
|
1501
|
-
var CURRENT_DIR =
|
|
1355
|
+
import fs14 from "fs";
|
|
1356
|
+
import path13 from "path";
|
|
1357
|
+
var STAGED_DIR = path13.join(CALIBER_DIR, "staged");
|
|
1358
|
+
var PROPOSED_DIR = path13.join(STAGED_DIR, "proposed");
|
|
1359
|
+
var CURRENT_DIR = path13.join(STAGED_DIR, "current");
|
|
1502
1360
|
function stageFiles(files, projectDir) {
|
|
1503
1361
|
cleanupStaging();
|
|
1504
1362
|
let newFiles = 0;
|
|
1505
1363
|
let modifiedFiles = 0;
|
|
1506
1364
|
const stagedFiles = [];
|
|
1507
1365
|
for (const file of files) {
|
|
1508
|
-
const proposedPath =
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
const originalPath =
|
|
1512
|
-
if (
|
|
1513
|
-
const currentPath =
|
|
1514
|
-
|
|
1515
|
-
|
|
1366
|
+
const proposedPath = path13.join(PROPOSED_DIR, file.path);
|
|
1367
|
+
fs14.mkdirSync(path13.dirname(proposedPath), { recursive: true });
|
|
1368
|
+
fs14.writeFileSync(proposedPath, file.content);
|
|
1369
|
+
const originalPath = path13.join(projectDir, file.path);
|
|
1370
|
+
if (fs14.existsSync(originalPath)) {
|
|
1371
|
+
const currentPath = path13.join(CURRENT_DIR, file.path);
|
|
1372
|
+
fs14.mkdirSync(path13.dirname(currentPath), { recursive: true });
|
|
1373
|
+
fs14.copyFileSync(originalPath, currentPath);
|
|
1516
1374
|
modifiedFiles++;
|
|
1517
1375
|
stagedFiles.push({ relativePath: file.path, proposedPath, currentPath, originalPath, isNew: false });
|
|
1518
1376
|
} else {
|
|
@@ -1523,8 +1381,8 @@ function stageFiles(files, projectDir) {
|
|
|
1523
1381
|
return { newFiles, modifiedFiles, stagedFiles };
|
|
1524
1382
|
}
|
|
1525
1383
|
function cleanupStaging() {
|
|
1526
|
-
if (
|
|
1527
|
-
|
|
1384
|
+
if (fs14.existsSync(STAGED_DIR)) {
|
|
1385
|
+
fs14.rmSync(STAGED_DIR, { recursive: true, force: true });
|
|
1528
1386
|
}
|
|
1529
1387
|
}
|
|
1530
1388
|
|
|
@@ -1570,23 +1428,23 @@ function openDiffsInEditor(editor, files) {
|
|
|
1570
1428
|
import { createTwoFilesPatch } from "diff";
|
|
1571
1429
|
|
|
1572
1430
|
// src/lib/hooks.ts
|
|
1573
|
-
import
|
|
1574
|
-
import
|
|
1575
|
-
var SETTINGS_PATH =
|
|
1431
|
+
import fs15 from "fs";
|
|
1432
|
+
import path14 from "path";
|
|
1433
|
+
var SETTINGS_PATH = path14.join(".claude", "settings.json");
|
|
1576
1434
|
var HOOK_COMMAND = "caliber refresh --quiet";
|
|
1577
1435
|
var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
|
|
1578
1436
|
function readSettings() {
|
|
1579
|
-
if (!
|
|
1437
|
+
if (!fs15.existsSync(SETTINGS_PATH)) return {};
|
|
1580
1438
|
try {
|
|
1581
|
-
return JSON.parse(
|
|
1439
|
+
return JSON.parse(fs15.readFileSync(SETTINGS_PATH, "utf-8"));
|
|
1582
1440
|
} catch {
|
|
1583
1441
|
return {};
|
|
1584
1442
|
}
|
|
1585
1443
|
}
|
|
1586
1444
|
function writeSettings(settings) {
|
|
1587
|
-
const dir =
|
|
1588
|
-
if (!
|
|
1589
|
-
|
|
1445
|
+
const dir = path14.dirname(SETTINGS_PATH);
|
|
1446
|
+
if (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
|
|
1447
|
+
fs15.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
1590
1448
|
}
|
|
1591
1449
|
function findHookIndex(sessionEnd) {
|
|
1592
1450
|
return sessionEnd.findIndex(
|
|
@@ -1636,23 +1494,23 @@ function removeHook() {
|
|
|
1636
1494
|
|
|
1637
1495
|
// src/lib/state.ts
|
|
1638
1496
|
init_constants();
|
|
1639
|
-
import
|
|
1640
|
-
import
|
|
1497
|
+
import fs16 from "fs";
|
|
1498
|
+
import path15 from "path";
|
|
1641
1499
|
import { execSync as execSync3 } from "child_process";
|
|
1642
|
-
var STATE_FILE =
|
|
1500
|
+
var STATE_FILE = path15.join(CALIBER_DIR, ".caliber-state.json");
|
|
1643
1501
|
function readState() {
|
|
1644
1502
|
try {
|
|
1645
|
-
if (!
|
|
1646
|
-
return JSON.parse(
|
|
1503
|
+
if (!fs16.existsSync(STATE_FILE)) return null;
|
|
1504
|
+
return JSON.parse(fs16.readFileSync(STATE_FILE, "utf-8"));
|
|
1647
1505
|
} catch {
|
|
1648
1506
|
return null;
|
|
1649
1507
|
}
|
|
1650
1508
|
}
|
|
1651
1509
|
function writeState(state) {
|
|
1652
|
-
if (!
|
|
1653
|
-
|
|
1510
|
+
if (!fs16.existsSync(CALIBER_DIR)) {
|
|
1511
|
+
fs16.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
1654
1512
|
}
|
|
1655
|
-
|
|
1513
|
+
fs16.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
|
|
1656
1514
|
}
|
|
1657
1515
|
function getCurrentHeadSha() {
|
|
1658
1516
|
try {
|
|
@@ -1757,8 +1615,8 @@ async function initCommand(options) {
|
|
|
1757
1615
|
console.log(chalk3.dim(" Configure your coding agent environment\n"));
|
|
1758
1616
|
console.log(chalk3.bold(" What is Caliber?\n"));
|
|
1759
1617
|
console.log(chalk3.dim(" Caliber audits your AI agent configurations and suggests targeted"));
|
|
1760
|
-
console.log(chalk3.dim(" improvements. It analyzes CLAUDE.md, .cursorrules,
|
|
1761
|
-
console.log(chalk3.dim("
|
|
1618
|
+
console.log(chalk3.dim(" improvements. It analyzes CLAUDE.md, .cursorrules, and skills"));
|
|
1619
|
+
console.log(chalk3.dim(" against your actual codebase \u2014 keeping what works, fixing"));
|
|
1762
1620
|
console.log(chalk3.dim(" what's stale, and adding what's missing.\n"));
|
|
1763
1621
|
console.log(chalk3.bold(" How it works:\n"));
|
|
1764
1622
|
console.log(chalk3.dim(" 1. Scan Analyze your code, dependencies, and file structure"));
|
|
@@ -1797,10 +1655,6 @@ async function initCommand(options) {
|
|
|
1797
1655
|
cursor_rules_count: fingerprint.existingConfigs.cursorRules?.length ?? 0,
|
|
1798
1656
|
cursor_skills_count: fingerprint.existingConfigs.cursorSkills?.length ?? 0,
|
|
1799
1657
|
skills_count: fingerprint.existingConfigs.claudeSkills?.length ?? 0,
|
|
1800
|
-
has_claude_mcp_servers: !!fingerprint.existingConfigs.claudeMcpServers,
|
|
1801
|
-
has_cursor_mcp_servers: !!fingerprint.existingConfigs.cursorMcpServers,
|
|
1802
|
-
claude_mcp_server_count: fingerprint.existingConfigs.claudeMcpServers ? Object.keys(fingerprint.existingConfigs.claudeMcpServers).length : 0,
|
|
1803
|
-
cursor_mcp_server_count: fingerprint.existingConfigs.cursorMcpServers ? Object.keys(fingerprint.existingConfigs.cursorMcpServers).length : 0,
|
|
1804
1658
|
file_count: fingerprint.fileTree.length
|
|
1805
1659
|
});
|
|
1806
1660
|
console.log(chalk3.dim(` Languages: ${fingerprint.languages.join(", ") || "none detected"}`));
|
|
@@ -1808,7 +1662,7 @@ async function initCommand(options) {
|
|
|
1808
1662
|
console.log(chalk3.dim(` Files: ${fingerprint.fileTree.length} found
|
|
1809
1663
|
`));
|
|
1810
1664
|
console.log(chalk3.hex("#6366f1").bold(" Step 3/6 \u2014 Match project\n"));
|
|
1811
|
-
console.log(chalk3.dim(" Scanning for existing rules, skills,
|
|
1665
|
+
console.log(chalk3.dim(" Scanning for existing rules, skills, and teammate setups.\n"));
|
|
1812
1666
|
const matchSpinner = ora2("Scanning for existing setup...").start();
|
|
1813
1667
|
const localConfigs = [];
|
|
1814
1668
|
const ec = fingerprint.existingConfigs;
|
|
@@ -1830,15 +1684,6 @@ async function initCommand(options) {
|
|
|
1830
1684
|
localConfigs.push(`.cursor/skills/${skill.slug}/SKILL.md`);
|
|
1831
1685
|
}
|
|
1832
1686
|
}
|
|
1833
|
-
const localState = scanLocalState(process.cwd());
|
|
1834
|
-
const claudeMcpServers = localState.filter((i) => i.type === "mcp" && i.platform === "claude").map((i) => i.name);
|
|
1835
|
-
const cursorMcpServers = localState.filter((i) => i.type === "mcp" && i.platform === "cursor").map((i) => i.name);
|
|
1836
|
-
if (claudeMcpServers.length > 0) {
|
|
1837
|
-
localConfigs.push(`.mcp.json (${claudeMcpServers.join(", ")} \u2014 will merge)`);
|
|
1838
|
-
}
|
|
1839
|
-
if (cursorMcpServers.length > 0) {
|
|
1840
|
-
localConfigs.push(`.cursor/mcp.json (${cursorMcpServers.join(", ")} \u2014 will merge)`);
|
|
1841
|
-
}
|
|
1842
1687
|
let existingSetup = null;
|
|
1843
1688
|
let existingProjectId = null;
|
|
1844
1689
|
let hasTeammateSetup = false;
|
|
@@ -1893,7 +1738,7 @@ async function initCommand(options) {
|
|
|
1893
1738
|
let generatedSetup = null;
|
|
1894
1739
|
let rawOutput;
|
|
1895
1740
|
trackEvent("generation_started", { target_agent: targetAgent });
|
|
1896
|
-
const hasExistingConfig = !!(ec.claudeMd || ec.claudeSettings || ec.claudeSkills?.length || ec.cursorrules || ec.cursorRules?.length
|
|
1741
|
+
const hasExistingConfig = !!(ec.claudeMd || ec.claudeSettings || ec.claudeSkills?.length || ec.cursorrules || ec.cursorRules?.length);
|
|
1897
1742
|
const genStartTime = Date.now();
|
|
1898
1743
|
const genSpinner = ora2("Generating setup...").start();
|
|
1899
1744
|
const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES, { showElapsedTime: true });
|
|
@@ -2152,25 +1997,24 @@ function openReview(method, stagedFiles) {
|
|
|
2152
1997
|
console.log(chalk3.dim(" Diffs opened in your editor.\n"));
|
|
2153
1998
|
} else {
|
|
2154
1999
|
for (const file of stagedFiles) {
|
|
2155
|
-
console.log(chalk3.bold(` ${file.relativePath}`));
|
|
2156
2000
|
if (file.currentPath) {
|
|
2157
|
-
const
|
|
2158
|
-
const
|
|
2159
|
-
const patch = createTwoFilesPatch(file.relativePath, file.relativePath,
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2001
|
+
const currentLines = fs17.readFileSync(file.currentPath, "utf-8").split("\n");
|
|
2002
|
+
const proposedLines = fs17.readFileSync(file.proposedPath, "utf-8").split("\n");
|
|
2003
|
+
const patch = createTwoFilesPatch(file.relativePath, file.relativePath, currentLines.join("\n"), proposedLines.join("\n"));
|
|
2004
|
+
let added = 0, removed = 0;
|
|
2005
|
+
for (const line of patch.split("\n")) {
|
|
2006
|
+
if (line.startsWith("+") && !line.startsWith("+++")) added++;
|
|
2007
|
+
if (line.startsWith("-") && !line.startsWith("---")) removed++;
|
|
2164
2008
|
}
|
|
2009
|
+
console.log(` ${chalk3.yellow("~")} ${file.relativePath} ${chalk3.green(`+${added}`)} ${chalk3.red(`-${removed}`)}`);
|
|
2165
2010
|
} else {
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
const preview = content.split("\n").slice(0, 20);
|
|
2169
|
-
for (const line of preview) console.log(chalk3.green(` +${line}`));
|
|
2170
|
-
if (content.split("\n").length > 20) console.log(chalk3.dim(` ... ${content.split("\n").length - 20} more lines`));
|
|
2011
|
+
const lines = fs17.readFileSync(file.proposedPath, "utf-8").split("\n").length;
|
|
2012
|
+
console.log(` ${chalk3.green("+")} ${file.relativePath} ${chalk3.dim(`${lines} lines`)}`);
|
|
2171
2013
|
}
|
|
2172
|
-
console.log("");
|
|
2173
2014
|
}
|
|
2015
|
+
console.log("");
|
|
2016
|
+
console.log(chalk3.dim(` Files staged at .caliber/staged/ for manual inspection.
|
|
2017
|
+
`));
|
|
2174
2018
|
}
|
|
2175
2019
|
}
|
|
2176
2020
|
async function promptReviewAction() {
|
|
@@ -2195,7 +2039,7 @@ function printSetupSummary(setup) {
|
|
|
2195
2039
|
};
|
|
2196
2040
|
if (claude) {
|
|
2197
2041
|
if (claude.claudeMd) {
|
|
2198
|
-
const icon =
|
|
2042
|
+
const icon = fs17.existsSync("CLAUDE.md") ? chalk3.yellow("~") : chalk3.green("+");
|
|
2199
2043
|
const desc = getDescription("CLAUDE.md");
|
|
2200
2044
|
console.log(` ${icon} ${chalk3.bold("CLAUDE.md")}`);
|
|
2201
2045
|
if (desc) {
|
|
@@ -2207,26 +2051,17 @@ function printSetupSummary(setup) {
|
|
|
2207
2051
|
if (Array.isArray(skills) && skills.length > 0) {
|
|
2208
2052
|
for (const skill of skills) {
|
|
2209
2053
|
const skillPath = `.claude/skills/${skill.name.replace(/[^a-z0-9-]/gi, "-").toLowerCase()}.md`;
|
|
2210
|
-
const icon =
|
|
2054
|
+
const icon = fs17.existsSync(skillPath) ? chalk3.yellow("~") : chalk3.green("+");
|
|
2211
2055
|
const desc = getDescription(skillPath);
|
|
2212
2056
|
console.log(` ${icon} ${chalk3.bold(skillPath)}`);
|
|
2213
2057
|
console.log(chalk3.dim(` ${desc || summarizeSkill(skill)}`));
|
|
2214
2058
|
console.log("");
|
|
2215
2059
|
}
|
|
2216
2060
|
}
|
|
2217
|
-
const mcpServers = claude.mcpServers;
|
|
2218
|
-
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
2219
|
-
const icon = fs18.existsSync(".mcp.json") ? chalk3.yellow("~") : chalk3.green("+");
|
|
2220
|
-
const serverNames = Object.keys(mcpServers);
|
|
2221
|
-
const desc = getDescription(".mcp.json");
|
|
2222
|
-
console.log(` ${icon} ${chalk3.bold(".mcp.json")}`);
|
|
2223
|
-
console.log(chalk3.dim(` ${desc || `servers: ${serverNames.join(", ")}`}`));
|
|
2224
|
-
console.log("");
|
|
2225
|
-
}
|
|
2226
2061
|
}
|
|
2227
2062
|
if (cursor) {
|
|
2228
2063
|
if (cursor.cursorrules) {
|
|
2229
|
-
const icon =
|
|
2064
|
+
const icon = fs17.existsSync(".cursorrules") ? chalk3.yellow("~") : chalk3.green("+");
|
|
2230
2065
|
const desc = getDescription(".cursorrules");
|
|
2231
2066
|
console.log(` ${icon} ${chalk3.bold(".cursorrules")}`);
|
|
2232
2067
|
if (desc) console.log(chalk3.dim(` ${desc}`));
|
|
@@ -2236,7 +2071,7 @@ function printSetupSummary(setup) {
|
|
|
2236
2071
|
if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
|
|
2237
2072
|
for (const skill of cursorSkills) {
|
|
2238
2073
|
const skillPath = `.cursor/skills/${skill.slug}/SKILL.md`;
|
|
2239
|
-
const icon =
|
|
2074
|
+
const icon = fs17.existsSync(skillPath) ? chalk3.yellow("~") : chalk3.green("+");
|
|
2240
2075
|
const desc = getDescription(skillPath);
|
|
2241
2076
|
console.log(` ${icon} ${chalk3.bold(skillPath)}`);
|
|
2242
2077
|
if (desc) {
|
|
@@ -2252,7 +2087,7 @@ function printSetupSummary(setup) {
|
|
|
2252
2087
|
if (Array.isArray(rules) && rules.length > 0) {
|
|
2253
2088
|
for (const rule of rules) {
|
|
2254
2089
|
const rulePath = `.cursor/rules/${rule.filename}`;
|
|
2255
|
-
const icon =
|
|
2090
|
+
const icon = fs17.existsSync(rulePath) ? chalk3.yellow("~") : chalk3.green("+");
|
|
2256
2091
|
const desc = getDescription(rulePath);
|
|
2257
2092
|
console.log(` ${icon} ${chalk3.bold(rulePath)}`);
|
|
2258
2093
|
if (desc) {
|
|
@@ -2264,15 +2099,6 @@ function printSetupSummary(setup) {
|
|
|
2264
2099
|
console.log("");
|
|
2265
2100
|
}
|
|
2266
2101
|
}
|
|
2267
|
-
const mcpServers = cursor.mcpServers;
|
|
2268
|
-
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
2269
|
-
const icon = fs18.existsSync(".cursor/mcp.json") ? chalk3.yellow("~") : chalk3.green("+");
|
|
2270
|
-
const serverNames = Object.keys(mcpServers);
|
|
2271
|
-
const desc = getDescription(".cursor/mcp.json");
|
|
2272
|
-
console.log(` ${icon} ${chalk3.bold(".cursor/mcp.json")}`);
|
|
2273
|
-
console.log(chalk3.dim(` ${desc || `servers: ${serverNames.join(", ")}`}`));
|
|
2274
|
-
console.log("");
|
|
2275
|
-
}
|
|
2276
2102
|
}
|
|
2277
2103
|
if (Array.isArray(deletions) && deletions.length > 0) {
|
|
2278
2104
|
for (const del of deletions) {
|
|
@@ -2301,9 +2127,6 @@ function collectSetupFiles(setup) {
|
|
|
2301
2127
|
files.push({ path: skillPath, content: skill.content });
|
|
2302
2128
|
}
|
|
2303
2129
|
}
|
|
2304
|
-
if (claude.mcpServers && Object.keys(claude.mcpServers).length > 0) {
|
|
2305
|
-
files.push({ path: ".mcp.json", content: JSON.stringify({ mcpServers: claude.mcpServers }, null, 2) });
|
|
2306
|
-
}
|
|
2307
2130
|
}
|
|
2308
2131
|
if (cursor) {
|
|
2309
2132
|
if (cursor.cursorrules) files.push({ path: ".cursorrules", content: cursor.cursorrules });
|
|
@@ -2319,9 +2142,6 @@ function collectSetupFiles(setup) {
|
|
|
2319
2142
|
files.push({ path: `.cursor/rules/${rule.filename}`, content: rule.content });
|
|
2320
2143
|
}
|
|
2321
2144
|
}
|
|
2322
|
-
if (cursor.mcpServers && Object.keys(cursor.mcpServers).length > 0) {
|
|
2323
|
-
files.push({ path: ".cursor/mcp.json", content: JSON.stringify({ mcpServers: cursor.mcpServers }, null, 2) });
|
|
2324
|
-
}
|
|
2325
2145
|
}
|
|
2326
2146
|
return files;
|
|
2327
2147
|
}
|
|
@@ -2361,6 +2181,150 @@ function undoCommand() {
|
|
|
2361
2181
|
// src/commands/status.ts
|
|
2362
2182
|
import chalk5 from "chalk";
|
|
2363
2183
|
import fs19 from "fs";
|
|
2184
|
+
|
|
2185
|
+
// src/scanner/index.ts
|
|
2186
|
+
import fs18 from "fs";
|
|
2187
|
+
import path16 from "path";
|
|
2188
|
+
import crypto4 from "crypto";
|
|
2189
|
+
function scanLocalState(dir) {
|
|
2190
|
+
const items = [];
|
|
2191
|
+
const claudeMdPath = path16.join(dir, "CLAUDE.md");
|
|
2192
|
+
if (fs18.existsSync(claudeMdPath)) {
|
|
2193
|
+
items.push({
|
|
2194
|
+
type: "rule",
|
|
2195
|
+
platform: "claude",
|
|
2196
|
+
name: "CLAUDE.md",
|
|
2197
|
+
contentHash: hashFile(claudeMdPath),
|
|
2198
|
+
path: claudeMdPath
|
|
2199
|
+
});
|
|
2200
|
+
}
|
|
2201
|
+
const skillsDir = path16.join(dir, ".claude", "skills");
|
|
2202
|
+
if (fs18.existsSync(skillsDir)) {
|
|
2203
|
+
for (const file of fs18.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
|
|
2204
|
+
const filePath = path16.join(skillsDir, file);
|
|
2205
|
+
items.push({
|
|
2206
|
+
type: "skill",
|
|
2207
|
+
platform: "claude",
|
|
2208
|
+
name: file,
|
|
2209
|
+
contentHash: hashFile(filePath),
|
|
2210
|
+
path: filePath
|
|
2211
|
+
});
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
const mcpJsonPath = path16.join(dir, ".mcp.json");
|
|
2215
|
+
if (fs18.existsSync(mcpJsonPath)) {
|
|
2216
|
+
try {
|
|
2217
|
+
const mcpJson = JSON.parse(fs18.readFileSync(mcpJsonPath, "utf-8"));
|
|
2218
|
+
if (mcpJson.mcpServers) {
|
|
2219
|
+
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
2220
|
+
items.push({
|
|
2221
|
+
type: "mcp",
|
|
2222
|
+
platform: "claude",
|
|
2223
|
+
name,
|
|
2224
|
+
contentHash: hashJson(mcpJson.mcpServers[name]),
|
|
2225
|
+
path: mcpJsonPath
|
|
2226
|
+
});
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
} catch {
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
const cursorrulesPath = path16.join(dir, ".cursorrules");
|
|
2233
|
+
if (fs18.existsSync(cursorrulesPath)) {
|
|
2234
|
+
items.push({
|
|
2235
|
+
type: "rule",
|
|
2236
|
+
platform: "cursor",
|
|
2237
|
+
name: ".cursorrules",
|
|
2238
|
+
contentHash: hashFile(cursorrulesPath),
|
|
2239
|
+
path: cursorrulesPath
|
|
2240
|
+
});
|
|
2241
|
+
}
|
|
2242
|
+
const cursorRulesDir = path16.join(dir, ".cursor", "rules");
|
|
2243
|
+
if (fs18.existsSync(cursorRulesDir)) {
|
|
2244
|
+
for (const file of fs18.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
|
|
2245
|
+
const filePath = path16.join(cursorRulesDir, file);
|
|
2246
|
+
items.push({
|
|
2247
|
+
type: "rule",
|
|
2248
|
+
platform: "cursor",
|
|
2249
|
+
name: file,
|
|
2250
|
+
contentHash: hashFile(filePath),
|
|
2251
|
+
path: filePath
|
|
2252
|
+
});
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
const cursorSkillsDir = path16.join(dir, ".cursor", "skills");
|
|
2256
|
+
if (fs18.existsSync(cursorSkillsDir)) {
|
|
2257
|
+
try {
|
|
2258
|
+
for (const slug of fs18.readdirSync(cursorSkillsDir)) {
|
|
2259
|
+
const skillFile = path16.join(cursorSkillsDir, slug, "SKILL.md");
|
|
2260
|
+
if (fs18.existsSync(skillFile)) {
|
|
2261
|
+
items.push({
|
|
2262
|
+
type: "skill",
|
|
2263
|
+
platform: "cursor",
|
|
2264
|
+
name: `${slug}/SKILL.md`,
|
|
2265
|
+
contentHash: hashFile(skillFile),
|
|
2266
|
+
path: skillFile
|
|
2267
|
+
});
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
} catch {
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
const cursorMcpPath = path16.join(dir, ".cursor", "mcp.json");
|
|
2274
|
+
if (fs18.existsSync(cursorMcpPath)) {
|
|
2275
|
+
try {
|
|
2276
|
+
const mcpJson = JSON.parse(fs18.readFileSync(cursorMcpPath, "utf-8"));
|
|
2277
|
+
if (mcpJson.mcpServers) {
|
|
2278
|
+
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
2279
|
+
items.push({
|
|
2280
|
+
type: "mcp",
|
|
2281
|
+
platform: "cursor",
|
|
2282
|
+
name,
|
|
2283
|
+
contentHash: hashJson(mcpJson.mcpServers[name]),
|
|
2284
|
+
path: cursorMcpPath
|
|
2285
|
+
});
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
} catch {
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
return items;
|
|
2292
|
+
}
|
|
2293
|
+
function compareState(serverItems, localItems) {
|
|
2294
|
+
const installed = [];
|
|
2295
|
+
const missing = [];
|
|
2296
|
+
const outdated = [];
|
|
2297
|
+
const extra = [];
|
|
2298
|
+
const localMap = /* @__PURE__ */ new Map();
|
|
2299
|
+
for (const item of localItems) {
|
|
2300
|
+
localMap.set(`${item.type}:${item.platform}:${item.name}`, item);
|
|
2301
|
+
}
|
|
2302
|
+
for (const server of serverItems) {
|
|
2303
|
+
const key = `${server.type}:${server.platform}:${server.name}`;
|
|
2304
|
+
const local = localMap.get(key);
|
|
2305
|
+
localMap.delete(key);
|
|
2306
|
+
if (!local) {
|
|
2307
|
+
missing.push(server);
|
|
2308
|
+
} else if (local.contentHash !== server.content_hash) {
|
|
2309
|
+
outdated.push({ server, local });
|
|
2310
|
+
} else {
|
|
2311
|
+
installed.push({ server, local });
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
for (const local of localMap.values()) {
|
|
2315
|
+
extra.push(local);
|
|
2316
|
+
}
|
|
2317
|
+
return { installed, missing, outdated, extra };
|
|
2318
|
+
}
|
|
2319
|
+
function hashFile(filePath) {
|
|
2320
|
+
const text = fs18.readFileSync(filePath, "utf-8");
|
|
2321
|
+
return crypto4.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
|
|
2322
|
+
}
|
|
2323
|
+
function hashJson(obj) {
|
|
2324
|
+
return crypto4.createHash("sha256").update(JSON.stringify(obj)).digest("hex");
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
// src/commands/status.ts
|
|
2364
2328
|
async function statusCommand(options) {
|
|
2365
2329
|
const auth2 = getStoredAuth();
|
|
2366
2330
|
const manifest = readManifest();
|