@caliber-ai/cli 0.13.0 → 0.14.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 -123
- package/dist/bin.js.map +1 -1
- package/package.json +4 -2
package/dist/bin.js
CHANGED
|
@@ -1185,20 +1185,8 @@ function writeClaudeConfig(config) {
|
|
|
1185
1185
|
const written = [];
|
|
1186
1186
|
fs9.writeFileSync("CLAUDE.md", config.claudeMd);
|
|
1187
1187
|
written.push("CLAUDE.md");
|
|
1188
|
-
const claudeDir = ".claude";
|
|
1189
|
-
if (!fs9.existsSync(claudeDir)) fs9.mkdirSync(claudeDir, { recursive: true });
|
|
1190
|
-
fs9.writeFileSync(
|
|
1191
|
-
path10.join(claudeDir, "settings.json"),
|
|
1192
|
-
JSON.stringify(config.settings, null, 2)
|
|
1193
|
-
);
|
|
1194
|
-
written.push(path10.join(claudeDir, "settings.json"));
|
|
1195
|
-
fs9.writeFileSync(
|
|
1196
|
-
path10.join(claudeDir, "settings.local.json"),
|
|
1197
|
-
JSON.stringify(config.settingsLocal, null, 2)
|
|
1198
|
-
);
|
|
1199
|
-
written.push(path10.join(claudeDir, "settings.local.json"));
|
|
1200
1188
|
if (config.skills?.length) {
|
|
1201
|
-
const skillsDir = path10.join(
|
|
1189
|
+
const skillsDir = path10.join(".claude", "skills");
|
|
1202
1190
|
if (!fs9.existsSync(skillsDir)) fs9.mkdirSync(skillsDir, { recursive: true });
|
|
1203
1191
|
for (const skill of config.skills) {
|
|
1204
1192
|
const filename = `${skill.name.replace(/[^a-z0-9-]/gi, "-").toLowerCase()}.md`;
|
|
@@ -1212,9 +1200,7 @@ function writeClaudeConfig(config) {
|
|
|
1212
1200
|
try {
|
|
1213
1201
|
if (fs9.existsSync(".mcp.json")) {
|
|
1214
1202
|
const existing = JSON.parse(fs9.readFileSync(".mcp.json", "utf-8"));
|
|
1215
|
-
if (existing.mcpServers)
|
|
1216
|
-
existingServers = existing.mcpServers;
|
|
1217
|
-
}
|
|
1203
|
+
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
1218
1204
|
}
|
|
1219
1205
|
} catch {
|
|
1220
1206
|
}
|
|
@@ -1319,7 +1305,11 @@ function fileChecksum(filePath) {
|
|
|
1319
1305
|
// src/writers/index.ts
|
|
1320
1306
|
function writeSetup(setup) {
|
|
1321
1307
|
const filesToWrite = getFilesToWrite(setup);
|
|
1322
|
-
const
|
|
1308
|
+
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs13.existsSync(f));
|
|
1309
|
+
const existingFiles = [
|
|
1310
|
+
...filesToWrite.filter((f) => fs13.existsSync(f)),
|
|
1311
|
+
...filesToDelete
|
|
1312
|
+
];
|
|
1323
1313
|
const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
|
|
1324
1314
|
const written = [];
|
|
1325
1315
|
if ((setup.targetAgent === "claude" || setup.targetAgent === "both") && setup.claude) {
|
|
@@ -1328,15 +1318,28 @@ function writeSetup(setup) {
|
|
|
1328
1318
|
if ((setup.targetAgent === "cursor" || setup.targetAgent === "both") && setup.cursor) {
|
|
1329
1319
|
written.push(...writeCursorConfig(setup.cursor));
|
|
1330
1320
|
}
|
|
1321
|
+
const deleted = [];
|
|
1322
|
+
for (const filePath of filesToDelete) {
|
|
1323
|
+
fs13.unlinkSync(filePath);
|
|
1324
|
+
deleted.push(filePath);
|
|
1325
|
+
}
|
|
1331
1326
|
ensureGitignore();
|
|
1332
|
-
const entries =
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1327
|
+
const entries = [
|
|
1328
|
+
...written.map((file) => ({
|
|
1329
|
+
path: file,
|
|
1330
|
+
action: existingFiles.includes(file) ? "modified" : "created",
|
|
1331
|
+
checksum: fileChecksum(file),
|
|
1332
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1333
|
+
})),
|
|
1334
|
+
...deleted.map((file) => ({
|
|
1335
|
+
path: file,
|
|
1336
|
+
action: "deleted",
|
|
1337
|
+
checksum: "",
|
|
1338
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1339
|
+
}))
|
|
1340
|
+
];
|
|
1338
1341
|
writeManifest({ version: 1, backupDir, entries });
|
|
1339
|
-
return { written, backupDir };
|
|
1342
|
+
return { written, deleted, backupDir };
|
|
1340
1343
|
}
|
|
1341
1344
|
function undoSetup() {
|
|
1342
1345
|
const manifest = readManifest();
|
|
@@ -1351,7 +1354,7 @@ function undoSetup() {
|
|
|
1351
1354
|
fs13.unlinkSync(entry.path);
|
|
1352
1355
|
removed.push(entry.path);
|
|
1353
1356
|
}
|
|
1354
|
-
} else if (entry.action === "modified" && manifest.backupDir) {
|
|
1357
|
+
} else if ((entry.action === "modified" || entry.action === "deleted") && manifest.backupDir) {
|
|
1355
1358
|
if (restoreBackup(manifest.backupDir, entry.path)) {
|
|
1356
1359
|
restored.push(entry.path);
|
|
1357
1360
|
}
|
|
@@ -1366,7 +1369,7 @@ function undoSetup() {
|
|
|
1366
1369
|
function getFilesToWrite(setup) {
|
|
1367
1370
|
const files = [];
|
|
1368
1371
|
if ((setup.targetAgent === "claude" || setup.targetAgent === "both") && setup.claude) {
|
|
1369
|
-
files.push("CLAUDE.md"
|
|
1372
|
+
files.push("CLAUDE.md");
|
|
1370
1373
|
if (setup.claude.mcpServers) files.push(".mcp.json");
|
|
1371
1374
|
if (setup.claude.skills) {
|
|
1372
1375
|
for (const s of setup.claude.skills) {
|
|
@@ -1582,10 +1585,10 @@ async function initCommand(options) {
|
|
|
1582
1585
|
`));
|
|
1583
1586
|
console.log(chalk3.dim(" Configure your coding agent environment\n"));
|
|
1584
1587
|
console.log(chalk3.bold(" What is Caliber?\n"));
|
|
1585
|
-
console.log(chalk3.dim(" Caliber
|
|
1586
|
-
console.log(chalk3.dim("
|
|
1587
|
-
console.log(chalk3.dim("
|
|
1588
|
-
console.log(chalk3.dim("
|
|
1588
|
+
console.log(chalk3.dim(" Caliber audits your AI agent configurations and suggests targeted"));
|
|
1589
|
+
console.log(chalk3.dim(" improvements. It analyzes CLAUDE.md, .cursorrules, skills, and MCP"));
|
|
1590
|
+
console.log(chalk3.dim(" servers against your actual codebase \u2014 keeping what works, fixing"));
|
|
1591
|
+
console.log(chalk3.dim(" what's stale, and adding what's missing.\n"));
|
|
1589
1592
|
console.log(chalk3.bold(" How it works:\n"));
|
|
1590
1593
|
console.log(chalk3.dim(" 1. Scan Analyze your code, dependencies, and file structure"));
|
|
1591
1594
|
console.log(chalk3.dim(" 2. Match Detect existing configs and check for teammate setups"));
|
|
@@ -1704,15 +1707,15 @@ async function initCommand(options) {
|
|
|
1704
1707
|
if (isEmpty) {
|
|
1705
1708
|
fingerprint.description = await promptInput("What will you build in this project?");
|
|
1706
1709
|
}
|
|
1707
|
-
console.log(chalk3.hex("#6366f1").bold(" Step 4/6 \u2014
|
|
1708
|
-
console.log(chalk3.dim(" AI is
|
|
1709
|
-
console.log(chalk3.dim("
|
|
1710
|
+
console.log(chalk3.hex("#6366f1").bold(" Step 4/6 \u2014 Auditing your configs\n"));
|
|
1711
|
+
console.log(chalk3.dim(" AI is auditing your CLAUDE.md, skills, and rules against your"));
|
|
1712
|
+
console.log(chalk3.dim(" project's actual codebase and conventions.\n"));
|
|
1710
1713
|
console.log(chalk3.dim(" This usually takes 1\u20133 minutes on first run.\n"));
|
|
1711
1714
|
let generatedSetup = null;
|
|
1712
|
-
let setupExplanation;
|
|
1713
1715
|
let rawOutput;
|
|
1714
1716
|
trackEvent("generation_started", { target_agent: targetAgent });
|
|
1715
1717
|
const hasExistingConfig = !!(ec.claudeMd || ec.claudeSettings || ec.claudeSkills?.length || ec.cursorrules || ec.cursorRules?.length || ec.claudeMcpServers || ec.cursorMcpServers);
|
|
1718
|
+
const genStartTime = Date.now();
|
|
1716
1719
|
const genSpinner = ora2("Generating setup...").start();
|
|
1717
1720
|
const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES, { showElapsedTime: true });
|
|
1718
1721
|
genMessages.start();
|
|
@@ -1729,7 +1732,6 @@ async function initCommand(options) {
|
|
|
1729
1732
|
},
|
|
1730
1733
|
(payload) => {
|
|
1731
1734
|
generatedSetup = payload.setup;
|
|
1732
|
-
setupExplanation = payload.explanation;
|
|
1733
1735
|
rawOutput = payload.raw;
|
|
1734
1736
|
},
|
|
1735
1737
|
(error) => {
|
|
@@ -1757,21 +1759,19 @@ async function initCommand(options) {
|
|
|
1757
1759
|
}
|
|
1758
1760
|
throw new Error("__exit__");
|
|
1759
1761
|
}
|
|
1760
|
-
|
|
1762
|
+
const elapsedMs = Date.now() - genStartTime;
|
|
1763
|
+
const mins = Math.floor(elapsedMs / 6e4);
|
|
1764
|
+
const secs = Math.floor(elapsedMs % 6e4 / 1e3);
|
|
1765
|
+
const timeStr = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
|
|
1766
|
+
genSpinner.succeed(`Setup generated ${chalk3.dim(`in ${timeStr}`)}`);
|
|
1761
1767
|
printSetupSummary(generatedSetup);
|
|
1762
1768
|
console.log(chalk3.hex("#6366f1").bold(" Step 5/6 \u2014 Review\n"));
|
|
1763
|
-
console.log(chalk3.dim(" Review the proposed files
|
|
1764
|
-
console.log(chalk3.dim(" or
|
|
1765
|
-
let
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
printExplanation(setupExplanation);
|
|
1770
|
-
} else {
|
|
1771
|
-
console.log(chalk3.dim("\nNo explanation available for this setup.\n"));
|
|
1772
|
-
}
|
|
1773
|
-
explained = true;
|
|
1774
|
-
action = await promptAction(explained);
|
|
1769
|
+
console.log(chalk3.dim(" Review the proposed files. You can accept, refine via chat,"));
|
|
1770
|
+
console.log(chalk3.dim(" or preview any file to see exactly what will be written.\n"));
|
|
1771
|
+
let action = await promptAction();
|
|
1772
|
+
while (action === "preview") {
|
|
1773
|
+
await promptFilePreview(generatedSetup);
|
|
1774
|
+
action = await promptAction();
|
|
1775
1775
|
}
|
|
1776
1776
|
if (action === "decline") {
|
|
1777
1777
|
trackEvent("setup_declined");
|
|
@@ -1798,11 +1798,21 @@ async function initCommand(options) {
|
|
|
1798
1798
|
try {
|
|
1799
1799
|
const result = writeSetup(generatedSetup);
|
|
1800
1800
|
writeSpinner.succeed("Config files written");
|
|
1801
|
-
trackEvent("setup_applied", {
|
|
1801
|
+
trackEvent("setup_applied", {
|
|
1802
|
+
files_written: result.written.length,
|
|
1803
|
+
files_deleted: result.deleted.length,
|
|
1804
|
+
target_agent: targetAgent
|
|
1805
|
+
});
|
|
1802
1806
|
console.log(chalk3.bold("\nFiles created/updated:"));
|
|
1803
1807
|
for (const file of result.written) {
|
|
1804
1808
|
console.log(` ${chalk3.green("\u2713")} ${file}`);
|
|
1805
1809
|
}
|
|
1810
|
+
if (result.deleted.length > 0) {
|
|
1811
|
+
console.log(chalk3.bold("\nFiles removed:"));
|
|
1812
|
+
for (const file of result.deleted) {
|
|
1813
|
+
console.log(` ${chalk3.red("\u2717")} ${file}`);
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1806
1816
|
if (result.backupDir) {
|
|
1807
1817
|
console.log(chalk3.dim(`
|
|
1808
1818
|
Backups saved to ${result.backupDir}`));
|
|
@@ -1856,6 +1866,11 @@ async function initCommand(options) {
|
|
|
1856
1866
|
}
|
|
1857
1867
|
console.log(chalk3.bold.green("\nSetup complete! Your coding agent is now configured."));
|
|
1858
1868
|
console.log(chalk3.dim("Run `caliber undo` to revert changes.\n"));
|
|
1869
|
+
console.log(chalk3.bold(" Next steps:\n"));
|
|
1870
|
+
console.log(` ${chalk3.hex("#6366f1")("caliber health")} Check your config quality and fix issues`);
|
|
1871
|
+
console.log(` ${chalk3.hex("#6366f1")("caliber recommend")} Discover additional skills for your stack`);
|
|
1872
|
+
console.log(` ${chalk3.hex("#6366f1")("caliber diff")} See what changed since last setup`);
|
|
1873
|
+
console.log("");
|
|
1859
1874
|
}
|
|
1860
1875
|
async function refineLoop(currentSetup, _targetAgent) {
|
|
1861
1876
|
const history = [];
|
|
@@ -1920,93 +1935,170 @@ async function promptAgent() {
|
|
|
1920
1935
|
]
|
|
1921
1936
|
});
|
|
1922
1937
|
}
|
|
1923
|
-
async function promptAction(
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
const icon = fs16.existsSync(filePath) ? chalk3.yellow("~") : chalk3.green("+");
|
|
1934
|
-
const description = desc ? chalk3.dim(`\u2014 ${desc}`) : "";
|
|
1935
|
-
return ` ${icon} ${filePath} ${description}`;
|
|
1938
|
+
async function promptAction() {
|
|
1939
|
+
return select({
|
|
1940
|
+
message: "What would you like to do?",
|
|
1941
|
+
choices: [
|
|
1942
|
+
{ name: "Accept and apply", value: "accept" },
|
|
1943
|
+
{ name: "Refine via chat", value: "refine" },
|
|
1944
|
+
{ name: "Preview a file", value: "preview" },
|
|
1945
|
+
{ name: "Decline", value: "decline" }
|
|
1946
|
+
]
|
|
1947
|
+
});
|
|
1936
1948
|
}
|
|
1937
1949
|
function printSetupSummary(setup) {
|
|
1938
1950
|
const claude = setup.claude;
|
|
1939
1951
|
const cursor = setup.cursor;
|
|
1940
|
-
const
|
|
1952
|
+
const fileDescriptions = setup.fileDescriptions;
|
|
1953
|
+
const deletions = setup.deletions;
|
|
1941
1954
|
console.log("");
|
|
1955
|
+
console.log(chalk3.bold(" Proposed changes:\n"));
|
|
1956
|
+
const getDescription = (filePath) => {
|
|
1957
|
+
return fileDescriptions?.[filePath];
|
|
1958
|
+
};
|
|
1942
1959
|
if (claude) {
|
|
1943
1960
|
if (claude.claudeMd) {
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
}
|
|
1949
|
-
|
|
1950
|
-
console.log(
|
|
1961
|
+
const icon = fs16.existsSync("CLAUDE.md") ? chalk3.yellow("~") : chalk3.green("+");
|
|
1962
|
+
const desc = getDescription("CLAUDE.md");
|
|
1963
|
+
console.log(` ${icon} ${chalk3.bold("CLAUDE.md")}`);
|
|
1964
|
+
if (desc) {
|
|
1965
|
+
console.log(chalk3.dim(` ${desc}`));
|
|
1966
|
+
}
|
|
1967
|
+
console.log("");
|
|
1951
1968
|
}
|
|
1952
1969
|
const skills = claude.skills;
|
|
1953
1970
|
if (Array.isArray(skills) && skills.length > 0) {
|
|
1954
1971
|
for (const skill of skills) {
|
|
1955
1972
|
const skillPath = `.claude/skills/${skill.name.replace(/[^a-z0-9-]/gi, "-").toLowerCase()}.md`;
|
|
1956
|
-
|
|
1973
|
+
const icon = fs16.existsSync(skillPath) ? chalk3.yellow("~") : chalk3.green("+");
|
|
1974
|
+
const desc = getDescription(skillPath);
|
|
1975
|
+
console.log(` ${icon} ${chalk3.bold(skillPath)}`);
|
|
1976
|
+
console.log(chalk3.dim(` ${desc || summarizeSkill(skill)}`));
|
|
1977
|
+
console.log("");
|
|
1957
1978
|
}
|
|
1958
1979
|
}
|
|
1959
1980
|
const mcpServers = claude.mcpServers;
|
|
1960
1981
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
1961
|
-
|
|
1982
|
+
const icon = fs16.existsSync(".mcp.json") ? chalk3.yellow("~") : chalk3.green("+");
|
|
1983
|
+
const serverNames = Object.keys(mcpServers);
|
|
1984
|
+
const desc = getDescription(".mcp.json");
|
|
1985
|
+
console.log(` ${icon} ${chalk3.bold(".mcp.json")}`);
|
|
1986
|
+
console.log(chalk3.dim(` ${desc || `servers: ${serverNames.join(", ")}`}`));
|
|
1987
|
+
console.log("");
|
|
1962
1988
|
}
|
|
1963
1989
|
}
|
|
1964
1990
|
if (cursor) {
|
|
1965
1991
|
if (cursor.cursorrules) {
|
|
1966
|
-
|
|
1992
|
+
const icon = fs16.existsSync(".cursorrules") ? chalk3.yellow("~") : chalk3.green("+");
|
|
1993
|
+
const desc = getDescription(".cursorrules");
|
|
1994
|
+
console.log(` ${icon} ${chalk3.bold(".cursorrules")}`);
|
|
1995
|
+
if (desc) console.log(chalk3.dim(` ${desc}`));
|
|
1996
|
+
console.log("");
|
|
1967
1997
|
}
|
|
1968
1998
|
const rules = cursor.rules;
|
|
1969
1999
|
if (Array.isArray(rules) && rules.length > 0) {
|
|
1970
2000
|
for (const rule of rules) {
|
|
1971
2001
|
const rulePath = `.cursor/rules/${rule.filename}`;
|
|
1972
|
-
|
|
2002
|
+
const icon = fs16.existsSync(rulePath) ? chalk3.yellow("~") : chalk3.green("+");
|
|
2003
|
+
const desc = getDescription(rulePath);
|
|
2004
|
+
console.log(` ${icon} ${chalk3.bold(rulePath)}`);
|
|
2005
|
+
if (desc) {
|
|
2006
|
+
console.log(chalk3.dim(` ${desc}`));
|
|
2007
|
+
} else {
|
|
2008
|
+
const firstLine = rule.content.split("\n").filter((l) => l.trim() && !l.trim().startsWith("#"))[0];
|
|
2009
|
+
if (firstLine) console.log(chalk3.dim(` ${firstLine.trim().slice(0, 80)}`));
|
|
2010
|
+
}
|
|
2011
|
+
console.log("");
|
|
1973
2012
|
}
|
|
1974
2013
|
}
|
|
1975
2014
|
const mcpServers = cursor.mcpServers;
|
|
1976
2015
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
1977
|
-
|
|
2016
|
+
const icon = fs16.existsSync(".cursor/mcp.json") ? chalk3.yellow("~") : chalk3.green("+");
|
|
2017
|
+
const serverNames = Object.keys(mcpServers);
|
|
2018
|
+
const desc = getDescription(".cursor/mcp.json");
|
|
2019
|
+
console.log(` ${icon} ${chalk3.bold(".cursor/mcp.json")}`);
|
|
2020
|
+
console.log(chalk3.dim(` ${desc || `servers: ${serverNames.join(", ")}`}`));
|
|
2021
|
+
console.log("");
|
|
1978
2022
|
}
|
|
1979
2023
|
}
|
|
1980
|
-
|
|
1981
|
-
|
|
2024
|
+
if (Array.isArray(deletions) && deletions.length > 0) {
|
|
2025
|
+
for (const del of deletions) {
|
|
2026
|
+
console.log(` ${chalk3.red("-")} ${chalk3.bold(del.filePath)}`);
|
|
2027
|
+
console.log(chalk3.dim(` ${del.reason}`));
|
|
2028
|
+
console.log("");
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
console.log(` ${chalk3.green("+")} ${chalk3.dim("new")} ${chalk3.yellow("~")} ${chalk3.dim("modified")} ${chalk3.red("-")} ${chalk3.dim("removed")}`);
|
|
1982
2032
|
console.log("");
|
|
1983
2033
|
}
|
|
1984
|
-
function
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
2034
|
+
function summarizeSkill(skill) {
|
|
2035
|
+
const lines = skill.content.split("\n").filter((l) => l.trim() && !l.trim().startsWith("#"));
|
|
2036
|
+
return lines[0]?.trim().slice(0, 80) || skill.name;
|
|
2037
|
+
}
|
|
2038
|
+
function collectSetupFiles(setup) {
|
|
2039
|
+
const files = [];
|
|
2040
|
+
const claude = setup.claude;
|
|
2041
|
+
const cursor = setup.cursor;
|
|
2042
|
+
if (claude) {
|
|
2043
|
+
if (claude.claudeMd) files.push({ path: "CLAUDE.md", content: claude.claudeMd });
|
|
2044
|
+
const skills = claude.skills;
|
|
2045
|
+
if (Array.isArray(skills)) {
|
|
2046
|
+
for (const skill of skills) {
|
|
2047
|
+
const skillPath = `.claude/skills/${skill.name.replace(/[^a-z0-9-]/gi, "-").toLowerCase()}.md`;
|
|
2048
|
+
files.push({ path: skillPath, content: skill.content });
|
|
2049
|
+
}
|
|
1994
2050
|
}
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
const name = itemMatch[1];
|
|
1998
|
-
const desc = itemMatch[2].replace(/^\s*[-—:]\s*/, "");
|
|
1999
|
-
console.log(` ${chalk3.dim("\u25B8")} ${chalk3.white(name)} ${chalk3.dim(desc)}`);
|
|
2000
|
-
continue;
|
|
2051
|
+
if (claude.mcpServers && Object.keys(claude.mcpServers).length > 0) {
|
|
2052
|
+
files.push({ path: ".mcp.json", content: JSON.stringify({ mcpServers: claude.mcpServers }, null, 2) });
|
|
2001
2053
|
}
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2054
|
+
}
|
|
2055
|
+
if (cursor) {
|
|
2056
|
+
if (cursor.cursorrules) files.push({ path: ".cursorrules", content: cursor.cursorrules });
|
|
2057
|
+
const rules = cursor.rules;
|
|
2058
|
+
if (Array.isArray(rules)) {
|
|
2059
|
+
for (const rule of rules) {
|
|
2060
|
+
files.push({ path: `.cursor/rules/${rule.filename}`, content: rule.content });
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
if (cursor.mcpServers && Object.keys(cursor.mcpServers).length > 0) {
|
|
2064
|
+
files.push({ path: ".cursor/mcp.json", content: JSON.stringify({ mcpServers: cursor.mcpServers }, null, 2) });
|
|
2006
2065
|
}
|
|
2007
|
-
console.log(` ${chalk3.dim(trimmed)}`);
|
|
2008
2066
|
}
|
|
2067
|
+
return files;
|
|
2068
|
+
}
|
|
2069
|
+
function previewFileContent(filePath, content, maxLines = 25) {
|
|
2070
|
+
const lines = content.split("\n");
|
|
2071
|
+
const displayLines = lines.slice(0, maxLines);
|
|
2072
|
+
const maxWidth = Math.max(60, ...displayLines.map((l) => l.length + 4));
|
|
2073
|
+
const width = Math.min(maxWidth, 80);
|
|
2009
2074
|
console.log("");
|
|
2075
|
+
console.log(` ${chalk3.dim("\u250C\u2500")} ${chalk3.bold(filePath)} ${chalk3.dim("\u2500".repeat(Math.max(0, width - filePath.length - 5)) + "\u2510")}`);
|
|
2076
|
+
console.log(` ${chalk3.dim("\u2502")}${" ".repeat(width - 1)}${chalk3.dim("\u2502")}`);
|
|
2077
|
+
for (const line of displayLines) {
|
|
2078
|
+
const truncated = line.length > width - 5 ? line.slice(0, width - 8) + "..." : line;
|
|
2079
|
+
const padding = " ".repeat(Math.max(0, width - truncated.length - 3));
|
|
2080
|
+
console.log(` ${chalk3.dim("\u2502")} ${truncated}${padding}${chalk3.dim("\u2502")}`);
|
|
2081
|
+
}
|
|
2082
|
+
if (lines.length > maxLines) {
|
|
2083
|
+
console.log(` ${chalk3.dim("\u2502")}${" ".repeat(width - 1)}${chalk3.dim("\u2502")}`);
|
|
2084
|
+
const note = `(showing first ${maxLines} lines, full file is ${lines.length} lines)`;
|
|
2085
|
+
const notePadding = " ".repeat(Math.max(0, width - note.length - 3));
|
|
2086
|
+
console.log(` ${chalk3.dim("\u2502")} ${chalk3.dim(note)}${notePadding}${chalk3.dim("\u2502")}`);
|
|
2087
|
+
}
|
|
2088
|
+
console.log(` ${chalk3.dim("\u2514" + "\u2500".repeat(width - 1) + "\u2518")}`);
|
|
2089
|
+
console.log("");
|
|
2090
|
+
}
|
|
2091
|
+
async function promptFilePreview(setup) {
|
|
2092
|
+
const files = collectSetupFiles(setup);
|
|
2093
|
+
if (files.length === 0) {
|
|
2094
|
+
console.log(chalk3.dim("\n No files to preview.\n"));
|
|
2095
|
+
return;
|
|
2096
|
+
}
|
|
2097
|
+
const choice = await select({
|
|
2098
|
+
message: "Which file to preview?",
|
|
2099
|
+
choices: files.map((f, i) => ({ name: f.path, value: i }))
|
|
2100
|
+
});
|
|
2101
|
+
previewFileContent(files[choice].path, files[choice].content);
|
|
2010
2102
|
}
|
|
2011
2103
|
|
|
2012
2104
|
// src/commands/undo.ts
|
|
@@ -2416,6 +2508,7 @@ function printRecommendations(recs) {
|
|
|
2416
2508
|
// src/commands/health.ts
|
|
2417
2509
|
import chalk9 from "chalk";
|
|
2418
2510
|
import ora6 from "ora";
|
|
2511
|
+
import confirm2 from "@inquirer/confirm";
|
|
2419
2512
|
async function healthCommand(options) {
|
|
2420
2513
|
const auth2 = getStoredAuth();
|
|
2421
2514
|
if (!auth2) {
|
|
@@ -2450,30 +2543,44 @@ async function healthCommand(options) {
|
|
|
2450
2543
|
return;
|
|
2451
2544
|
}
|
|
2452
2545
|
printReport(report);
|
|
2453
|
-
|
|
2454
|
-
|
|
2546
|
+
const shouldFix = options.fix || report.recommendations.length > 0 && !options.json && await confirm2({ message: "Would you like to fix these issues?", default: true });
|
|
2547
|
+
if (shouldFix) {
|
|
2548
|
+
const planSpinner = ora6("Generating fix plan...").start();
|
|
2455
2549
|
const plan = await apiRequest(
|
|
2456
2550
|
`/api/context/reports/${report.id}/fix-plan`,
|
|
2457
2551
|
{ method: "POST", body: { projectId } }
|
|
2458
2552
|
);
|
|
2553
|
+
planSpinner.succeed("Fix plan ready");
|
|
2459
2554
|
if (!plan?.actions.length) {
|
|
2460
|
-
console.log(chalk9.dim("No fixes needed."));
|
|
2555
|
+
console.log(chalk9.dim(" No fixes needed."));
|
|
2461
2556
|
return;
|
|
2462
2557
|
}
|
|
2463
|
-
console.log(chalk9.bold("\
|
|
2558
|
+
console.log(chalk9.bold("\n Fix Plan:\n"));
|
|
2464
2559
|
for (const action of plan.actions) {
|
|
2465
|
-
|
|
2466
|
-
|
|
2560
|
+
if (action.type === "remove") {
|
|
2561
|
+
console.log(` ${chalk9.red("\u2717 Remove")} ${action.items.join(", ")}`);
|
|
2562
|
+
if (action.reason) console.log(chalk9.dim(` ${action.reason}`));
|
|
2563
|
+
} else if (action.type === "add") {
|
|
2564
|
+
console.log(` ${chalk9.green("+ Add")} ${action.items.join(", ")}`);
|
|
2565
|
+
if (action.reason) console.log(chalk9.dim(` ${action.reason}`));
|
|
2566
|
+
}
|
|
2567
|
+
console.log("");
|
|
2568
|
+
}
|
|
2569
|
+
const scoreAfterStr = plan.estimatedScoreAfter != null ? String(plan.estimatedScoreAfter) : `${report.score + plan.estimatedScoreImprovement}`;
|
|
2570
|
+
console.log(` Expected: ${report.score} \u2192 ${chalk9.green(scoreAfterStr)}/100
|
|
2571
|
+
`);
|
|
2572
|
+
const shouldExecute = options.fix || await confirm2({ message: "Apply this fix plan?", default: true });
|
|
2573
|
+
if (!shouldExecute) {
|
|
2574
|
+
console.log(chalk9.dim(" Fix cancelled."));
|
|
2575
|
+
return;
|
|
2467
2576
|
}
|
|
2468
|
-
|
|
2469
|
-
Estimated improvement: +${plan.estimatedScoreImprovement} points`));
|
|
2470
|
-
const fixSpinner = ora6("Executing fix plan...").start();
|
|
2577
|
+
const fixSpinner = ora6("Applying fixes...").start();
|
|
2471
2578
|
try {
|
|
2472
2579
|
const result = await apiRequest(
|
|
2473
2580
|
`/api/context/reports/${report.id}/fix-execute`,
|
|
2474
2581
|
{ method: "POST", body: { projectId } }
|
|
2475
2582
|
);
|
|
2476
|
-
fixSpinner.succeed("
|
|
2583
|
+
fixSpinner.succeed("Fixes applied");
|
|
2477
2584
|
console.log("");
|
|
2478
2585
|
console.log(` Score: ${result.scoreBefore} \u2192 ${chalk9.green(String(result.scoreAfter))} (${chalk9.green(`+${result.improvement}`)})`);
|
|
2479
2586
|
if (result.itemsRemoved) console.log(` Removed: ${result.itemsRemoved} items`);
|
|
@@ -2486,6 +2593,13 @@ Estimated improvement: +${plan.estimatedScoreImprovement} points`));
|
|
|
2486
2593
|
}
|
|
2487
2594
|
}
|
|
2488
2595
|
}
|
|
2596
|
+
var CATEGORY_DESCRIPTIONS = {
|
|
2597
|
+
tooling: "build/test/lint commands (helps agents the most)",
|
|
2598
|
+
essential: "constraints agents can't discover from code",
|
|
2599
|
+
convention: "style/naming rules (linters handle this; low value)",
|
|
2600
|
+
overview: "project summaries (agents discover this by reading code)",
|
|
2601
|
+
workflow: "development workflows and processes"
|
|
2602
|
+
};
|
|
2489
2603
|
function printReport(report) {
|
|
2490
2604
|
const gradeColors = {
|
|
2491
2605
|
A: chalk9.green,
|
|
@@ -2498,24 +2612,43 @@ function printReport(report) {
|
|
|
2498
2612
|
console.log(chalk9.bold("\n Context Health Report\n"));
|
|
2499
2613
|
console.log(` Grade: ${gradeColor(report.grade)} Score: ${gradeColor(String(report.score))}/100
|
|
2500
2614
|
`);
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
const
|
|
2615
|
+
const categories = Object.entries(report.category_breakdown);
|
|
2616
|
+
if (categories.length) {
|
|
2617
|
+
console.log(chalk9.bold(" Category Breakdown:\n"));
|
|
2618
|
+
const itemsByCategory = /* @__PURE__ */ new Map();
|
|
2619
|
+
if (report.item_classifications) {
|
|
2620
|
+
for (const item of report.item_classifications) {
|
|
2621
|
+
const cat = item.category.toLowerCase();
|
|
2622
|
+
if (!itemsByCategory.has(cat)) itemsByCategory.set(cat, []);
|
|
2623
|
+
itemsByCategory.get(cat).push(item);
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2505
2626
|
for (const [name, data] of categories) {
|
|
2506
|
-
const
|
|
2507
|
-
const
|
|
2508
|
-
console.log(` ${
|
|
2627
|
+
const desc = CATEGORY_DESCRIPTIONS[name] || "";
|
|
2628
|
+
const header = desc ? `${chalk9.bold(name)} ${chalk9.dim(`\u2014 ${desc}`)}` : chalk9.bold(name);
|
|
2629
|
+
console.log(` ${header}`);
|
|
2630
|
+
const items = itemsByCategory.get(name);
|
|
2631
|
+
if (items?.length) {
|
|
2632
|
+
for (const item of items) {
|
|
2633
|
+
const icon = item.impactScore >= 0 ? chalk9.green("\u2713") : chalk9.red("\u2717");
|
|
2634
|
+
const snippet = item.recommendation.length > 70 ? item.recommendation.slice(0, 67) + "..." : item.recommendation;
|
|
2635
|
+
console.log(` ${icon} ${item.itemName.padEnd(22)} ${chalk9.dim(`"${snippet}"`)}`);
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
console.log(chalk9.dim(` ${data.count} items \xB7 ${data.tokens} tokens
|
|
2639
|
+
`));
|
|
2509
2640
|
}
|
|
2510
|
-
console.log("");
|
|
2511
2641
|
}
|
|
2512
2642
|
if (report.recommendations.length) {
|
|
2513
|
-
console.log(chalk9.
|
|
2643
|
+
console.log(chalk9.bold(" Recommendations:\n"));
|
|
2514
2644
|
for (const rec of report.recommendations) {
|
|
2515
2645
|
const icon = rec.priority === "high" ? chalk9.red("!") : rec.priority === "medium" ? chalk9.yellow("~") : chalk9.dim("-");
|
|
2516
2646
|
console.log(` ${icon} ${rec.description}`);
|
|
2647
|
+
if (rec.reasoning) {
|
|
2648
|
+
console.log(chalk9.dim(` ${rec.reasoning}`));
|
|
2649
|
+
}
|
|
2650
|
+
console.log("");
|
|
2517
2651
|
}
|
|
2518
|
-
console.log("");
|
|
2519
2652
|
}
|
|
2520
2653
|
}
|
|
2521
2654
|
|
|
@@ -3041,7 +3174,7 @@ import chalk14 from "chalk";
|
|
|
3041
3174
|
import readline2 from "readline";
|
|
3042
3175
|
import ora10 from "ora";
|
|
3043
3176
|
import select2 from "@inquirer/select";
|
|
3044
|
-
import
|
|
3177
|
+
import confirm3 from "@inquirer/confirm";
|
|
3045
3178
|
function prompt(question) {
|
|
3046
3179
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
3047
3180
|
return new Promise((resolve2) => {
|
|
@@ -3121,7 +3254,7 @@ async function reviewCommand(message, options) {
|
|
|
3121
3254
|
console.log(` Most useful: ${bestPart || chalk14.dim("(skipped)")}`);
|
|
3122
3255
|
console.log(` Could be better: ${biggestGap || chalk14.dim("(skipped)")}`);
|
|
3123
3256
|
console.log(` Would recommend: ${wouldRecommend}`);
|
|
3124
|
-
const shouldSubmit = await
|
|
3257
|
+
const shouldSubmit = await confirm3({ message: "Submit this review?", default: true });
|
|
3125
3258
|
if (!shouldSubmit) {
|
|
3126
3259
|
console.log(chalk14.dim("\n Review cancelled.\n"));
|
|
3127
3260
|
return;
|
|
@@ -3135,7 +3268,8 @@ var pkg3 = JSON.parse(
|
|
|
3135
3268
|
fs20.readFileSync(path17.resolve(__dirname2, "..", "package.json"), "utf-8")
|
|
3136
3269
|
);
|
|
3137
3270
|
var program = new Command();
|
|
3138
|
-
|
|
3271
|
+
var displayVersion = process.env.CALIBER_LOCAL ? `${pkg3.version}-local` : pkg3.version;
|
|
3272
|
+
program.name(process.env.CALIBER_LOCAL ? "caloc" : "caliber").description("Configure your coding agent environment").version(displayVersion);
|
|
3139
3273
|
program.command("init").description("Initialize coding agent setup for this project").option("--agent <type>", "Target agent: claude, cursor, or both").option("--dry-run", "Preview changes without writing files").option("--force", "Overwrite existing setup without prompting").action(initCommand);
|
|
3140
3274
|
program.command("undo").description("Revert all config changes made by Caliber").action(undoCommand);
|
|
3141
3275
|
program.command("status").description("Show current Caliber setup status").option("--json", "Output as JSON").action(statusCommand);
|
|
@@ -3160,13 +3294,13 @@ import { fileURLToPath as fileURLToPath4 } from "url";
|
|
|
3160
3294
|
import { execSync as execSync4 } from "child_process";
|
|
3161
3295
|
import chalk15 from "chalk";
|
|
3162
3296
|
import ora11 from "ora";
|
|
3163
|
-
import
|
|
3297
|
+
import confirm4 from "@inquirer/confirm";
|
|
3164
3298
|
var __dirname_vc = path18.dirname(fileURLToPath4(import.meta.url));
|
|
3165
3299
|
var pkg4 = JSON.parse(
|
|
3166
3300
|
fs21.readFileSync(path18.resolve(__dirname_vc, "..", "package.json"), "utf-8")
|
|
3167
3301
|
);
|
|
3168
3302
|
async function promptYesNo(question) {
|
|
3169
|
-
return
|
|
3303
|
+
return confirm4({ message: question, default: true });
|
|
3170
3304
|
}
|
|
3171
3305
|
async function checkForUpdates() {
|
|
3172
3306
|
if (process.env.CALIBER_SKIP_UPDATE_CHECK) return;
|
|
@@ -3231,6 +3365,9 @@ Restarting: caliber ${args.join(" ")}
|
|
|
3231
3365
|
}
|
|
3232
3366
|
|
|
3233
3367
|
// src/bin.ts
|
|
3368
|
+
if (process.env.CALIBER_LOCAL) {
|
|
3369
|
+
process.env.CALIBER_SKIP_UPDATE_CHECK = "1";
|
|
3370
|
+
}
|
|
3234
3371
|
var firstRun = isFirstRun();
|
|
3235
3372
|
getDeviceId();
|
|
3236
3373
|
if (firstRun) {
|