@caliber-ai/cli 0.14.0 → 0.15.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 +117 -100
- package/dist/bin.js.map +1 -1
- package/package.json +3 -1
package/dist/bin.js
CHANGED
|
@@ -85,6 +85,7 @@ import ora2 from "ora";
|
|
|
85
85
|
import readline from "readline";
|
|
86
86
|
import select from "@inquirer/select";
|
|
87
87
|
import fs16 from "fs";
|
|
88
|
+
import { createTwoFilesPatch } from "diff";
|
|
88
89
|
|
|
89
90
|
// src/auth/token-store.ts
|
|
90
91
|
init_constants();
|
|
@@ -1185,20 +1186,8 @@ function writeClaudeConfig(config) {
|
|
|
1185
1186
|
const written = [];
|
|
1186
1187
|
fs9.writeFileSync("CLAUDE.md", config.claudeMd);
|
|
1187
1188
|
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
1189
|
if (config.skills?.length) {
|
|
1201
|
-
const skillsDir = path10.join(
|
|
1190
|
+
const skillsDir = path10.join(".claude", "skills");
|
|
1202
1191
|
if (!fs9.existsSync(skillsDir)) fs9.mkdirSync(skillsDir, { recursive: true });
|
|
1203
1192
|
for (const skill of config.skills) {
|
|
1204
1193
|
const filename = `${skill.name.replace(/[^a-z0-9-]/gi, "-").toLowerCase()}.md`;
|
|
@@ -1212,9 +1201,7 @@ function writeClaudeConfig(config) {
|
|
|
1212
1201
|
try {
|
|
1213
1202
|
if (fs9.existsSync(".mcp.json")) {
|
|
1214
1203
|
const existing = JSON.parse(fs9.readFileSync(".mcp.json", "utf-8"));
|
|
1215
|
-
if (existing.mcpServers)
|
|
1216
|
-
existingServers = existing.mcpServers;
|
|
1217
|
-
}
|
|
1204
|
+
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
1218
1205
|
}
|
|
1219
1206
|
} catch {
|
|
1220
1207
|
}
|
|
@@ -1319,7 +1306,11 @@ function fileChecksum(filePath) {
|
|
|
1319
1306
|
// src/writers/index.ts
|
|
1320
1307
|
function writeSetup(setup) {
|
|
1321
1308
|
const filesToWrite = getFilesToWrite(setup);
|
|
1322
|
-
const
|
|
1309
|
+
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs13.existsSync(f));
|
|
1310
|
+
const existingFiles = [
|
|
1311
|
+
...filesToWrite.filter((f) => fs13.existsSync(f)),
|
|
1312
|
+
...filesToDelete
|
|
1313
|
+
];
|
|
1323
1314
|
const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
|
|
1324
1315
|
const written = [];
|
|
1325
1316
|
if ((setup.targetAgent === "claude" || setup.targetAgent === "both") && setup.claude) {
|
|
@@ -1328,15 +1319,28 @@ function writeSetup(setup) {
|
|
|
1328
1319
|
if ((setup.targetAgent === "cursor" || setup.targetAgent === "both") && setup.cursor) {
|
|
1329
1320
|
written.push(...writeCursorConfig(setup.cursor));
|
|
1330
1321
|
}
|
|
1322
|
+
const deleted = [];
|
|
1323
|
+
for (const filePath of filesToDelete) {
|
|
1324
|
+
fs13.unlinkSync(filePath);
|
|
1325
|
+
deleted.push(filePath);
|
|
1326
|
+
}
|
|
1331
1327
|
ensureGitignore();
|
|
1332
|
-
const entries =
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1328
|
+
const entries = [
|
|
1329
|
+
...written.map((file) => ({
|
|
1330
|
+
path: file,
|
|
1331
|
+
action: existingFiles.includes(file) ? "modified" : "created",
|
|
1332
|
+
checksum: fileChecksum(file),
|
|
1333
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1334
|
+
})),
|
|
1335
|
+
...deleted.map((file) => ({
|
|
1336
|
+
path: file,
|
|
1337
|
+
action: "deleted",
|
|
1338
|
+
checksum: "",
|
|
1339
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1340
|
+
}))
|
|
1341
|
+
];
|
|
1338
1342
|
writeManifest({ version: 1, backupDir, entries });
|
|
1339
|
-
return { written, backupDir };
|
|
1343
|
+
return { written, deleted, backupDir };
|
|
1340
1344
|
}
|
|
1341
1345
|
function undoSetup() {
|
|
1342
1346
|
const manifest = readManifest();
|
|
@@ -1351,7 +1355,7 @@ function undoSetup() {
|
|
|
1351
1355
|
fs13.unlinkSync(entry.path);
|
|
1352
1356
|
removed.push(entry.path);
|
|
1353
1357
|
}
|
|
1354
|
-
} else if (entry.action === "modified" && manifest.backupDir) {
|
|
1358
|
+
} else if ((entry.action === "modified" || entry.action === "deleted") && manifest.backupDir) {
|
|
1355
1359
|
if (restoreBackup(manifest.backupDir, entry.path)) {
|
|
1356
1360
|
restored.push(entry.path);
|
|
1357
1361
|
}
|
|
@@ -1366,7 +1370,7 @@ function undoSetup() {
|
|
|
1366
1370
|
function getFilesToWrite(setup) {
|
|
1367
1371
|
const files = [];
|
|
1368
1372
|
if ((setup.targetAgent === "claude" || setup.targetAgent === "both") && setup.claude) {
|
|
1369
|
-
files.push("CLAUDE.md"
|
|
1373
|
+
files.push("CLAUDE.md");
|
|
1370
1374
|
if (setup.claude.mcpServers) files.push(".mcp.json");
|
|
1371
1375
|
if (setup.claude.skills) {
|
|
1372
1376
|
for (const s of setup.claude.skills) {
|
|
@@ -1582,10 +1586,10 @@ async function initCommand(options) {
|
|
|
1582
1586
|
`));
|
|
1583
1587
|
console.log(chalk3.dim(" Configure your coding agent environment\n"));
|
|
1584
1588
|
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("
|
|
1589
|
+
console.log(chalk3.dim(" Caliber audits your AI agent configurations and suggests targeted"));
|
|
1590
|
+
console.log(chalk3.dim(" improvements. It analyzes CLAUDE.md, .cursorrules, skills, and MCP"));
|
|
1591
|
+
console.log(chalk3.dim(" servers against your actual codebase \u2014 keeping what works, fixing"));
|
|
1592
|
+
console.log(chalk3.dim(" what's stale, and adding what's missing.\n"));
|
|
1589
1593
|
console.log(chalk3.bold(" How it works:\n"));
|
|
1590
1594
|
console.log(chalk3.dim(" 1. Scan Analyze your code, dependencies, and file structure"));
|
|
1591
1595
|
console.log(chalk3.dim(" 2. Match Detect existing configs and check for teammate setups"));
|
|
@@ -1704,9 +1708,9 @@ async function initCommand(options) {
|
|
|
1704
1708
|
if (isEmpty) {
|
|
1705
1709
|
fingerprint.description = await promptInput("What will you build in this project?");
|
|
1706
1710
|
}
|
|
1707
|
-
console.log(chalk3.hex("#6366f1").bold(" Step 4/6 \u2014
|
|
1708
|
-
console.log(chalk3.dim(" AI is
|
|
1709
|
-
console.log(chalk3.dim("
|
|
1711
|
+
console.log(chalk3.hex("#6366f1").bold(" Step 4/6 \u2014 Auditing your configs\n"));
|
|
1712
|
+
console.log(chalk3.dim(" AI is auditing your CLAUDE.md, skills, and rules against your"));
|
|
1713
|
+
console.log(chalk3.dim(" project's actual codebase and conventions.\n"));
|
|
1710
1714
|
console.log(chalk3.dim(" This usually takes 1\u20133 minutes on first run.\n"));
|
|
1711
1715
|
let generatedSetup = null;
|
|
1712
1716
|
let rawOutput;
|
|
@@ -1795,11 +1799,21 @@ async function initCommand(options) {
|
|
|
1795
1799
|
try {
|
|
1796
1800
|
const result = writeSetup(generatedSetup);
|
|
1797
1801
|
writeSpinner.succeed("Config files written");
|
|
1798
|
-
trackEvent("setup_applied", {
|
|
1802
|
+
trackEvent("setup_applied", {
|
|
1803
|
+
files_written: result.written.length,
|
|
1804
|
+
files_deleted: result.deleted.length,
|
|
1805
|
+
target_agent: targetAgent
|
|
1806
|
+
});
|
|
1799
1807
|
console.log(chalk3.bold("\nFiles created/updated:"));
|
|
1800
1808
|
for (const file of result.written) {
|
|
1801
1809
|
console.log(` ${chalk3.green("\u2713")} ${file}`);
|
|
1802
1810
|
}
|
|
1811
|
+
if (result.deleted.length > 0) {
|
|
1812
|
+
console.log(chalk3.bold("\nFiles removed:"));
|
|
1813
|
+
for (const file of result.deleted) {
|
|
1814
|
+
console.log(` ${chalk3.red("\u2717")} ${file}`);
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1803
1817
|
if (result.backupDir) {
|
|
1804
1818
|
console.log(chalk3.dim(`
|
|
1805
1819
|
Backups saved to ${result.backupDir}`));
|
|
@@ -1936,39 +1950,20 @@ async function promptAction() {
|
|
|
1936
1950
|
function printSetupSummary(setup) {
|
|
1937
1951
|
const claude = setup.claude;
|
|
1938
1952
|
const cursor = setup.cursor;
|
|
1953
|
+
const fileDescriptions = setup.fileDescriptions;
|
|
1954
|
+
const deletions = setup.deletions;
|
|
1939
1955
|
console.log("");
|
|
1940
|
-
console.log(chalk3.bold("
|
|
1956
|
+
console.log(chalk3.bold(" Proposed changes:\n"));
|
|
1957
|
+
const getDescription = (filePath) => {
|
|
1958
|
+
return fileDescriptions?.[filePath];
|
|
1959
|
+
};
|
|
1941
1960
|
if (claude) {
|
|
1942
1961
|
if (claude.claudeMd) {
|
|
1943
1962
|
const icon = fs16.existsSync("CLAUDE.md") ? chalk3.yellow("~") : chalk3.green("+");
|
|
1944
|
-
const
|
|
1963
|
+
const desc = getDescription("CLAUDE.md");
|
|
1945
1964
|
console.log(` ${icon} ${chalk3.bold("CLAUDE.md")}`);
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
if (sections.length > 0) {
|
|
1949
|
-
console.log(chalk3.dim(` Sections: ${sections.join(", ")}`));
|
|
1950
|
-
}
|
|
1951
|
-
console.log("");
|
|
1952
|
-
}
|
|
1953
|
-
if (claude.settings) {
|
|
1954
|
-
const icon = fs16.existsSync(".claude/settings.json") ? chalk3.yellow("~") : chalk3.green("+");
|
|
1955
|
-
const settings = claude.settings;
|
|
1956
|
-
const perms = settings.permissions?.allow || [];
|
|
1957
|
-
console.log(` ${icon} ${chalk3.bold(".claude/settings.json")}`);
|
|
1958
|
-
console.log(chalk3.dim(" Pre-approved shell commands so Claude doesn't ask permission each time."));
|
|
1959
|
-
if (perms.length > 0) {
|
|
1960
|
-
console.log(chalk3.dim(` Allows: ${summarizePermissions(perms)}`));
|
|
1961
|
-
}
|
|
1962
|
-
console.log("");
|
|
1963
|
-
}
|
|
1964
|
-
if (claude.settingsLocal) {
|
|
1965
|
-
const icon = fs16.existsSync(".claude/settings.local.json") ? chalk3.yellow("~") : chalk3.green("+");
|
|
1966
|
-
const settings = claude.settingsLocal;
|
|
1967
|
-
const perms = settings.permissions?.allow || [];
|
|
1968
|
-
console.log(` ${icon} ${chalk3.bold(".claude/settings.local.json")}`);
|
|
1969
|
-
console.log(chalk3.dim(" Your personal permission overrides (not shared with teammates)."));
|
|
1970
|
-
if (perms.length > 0) {
|
|
1971
|
-
console.log(chalk3.dim(` Allows: ${summarizePermissions(perms)}`));
|
|
1965
|
+
if (desc) {
|
|
1966
|
+
console.log(chalk3.dim(` ${desc}`));
|
|
1972
1967
|
}
|
|
1973
1968
|
console.log("");
|
|
1974
1969
|
}
|
|
@@ -1977,8 +1972,9 @@ function printSetupSummary(setup) {
|
|
|
1977
1972
|
for (const skill of skills) {
|
|
1978
1973
|
const skillPath = `.claude/skills/${skill.name.replace(/[^a-z0-9-]/gi, "-").toLowerCase()}.md`;
|
|
1979
1974
|
const icon = fs16.existsSync(skillPath) ? chalk3.yellow("~") : chalk3.green("+");
|
|
1975
|
+
const desc = getDescription(skillPath);
|
|
1980
1976
|
console.log(` ${icon} ${chalk3.bold(skillPath)}`);
|
|
1981
|
-
console.log(chalk3.dim(` ${summarizeSkill(skill)}`));
|
|
1977
|
+
console.log(chalk3.dim(` ${desc || summarizeSkill(skill)}`));
|
|
1982
1978
|
console.log("");
|
|
1983
1979
|
}
|
|
1984
1980
|
}
|
|
@@ -1986,18 +1982,18 @@ function printSetupSummary(setup) {
|
|
|
1986
1982
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
1987
1983
|
const icon = fs16.existsSync(".mcp.json") ? chalk3.yellow("~") : chalk3.green("+");
|
|
1988
1984
|
const serverNames = Object.keys(mcpServers);
|
|
1985
|
+
const desc = getDescription(".mcp.json");
|
|
1989
1986
|
console.log(` ${icon} ${chalk3.bold(".mcp.json")}`);
|
|
1990
|
-
console.log(chalk3.dim(
|
|
1991
|
-
console.log(chalk3.dim(` Servers: ${serverNames.join(", ")}`));
|
|
1987
|
+
console.log(chalk3.dim(` ${desc || `servers: ${serverNames.join(", ")}`}`));
|
|
1992
1988
|
console.log("");
|
|
1993
1989
|
}
|
|
1994
1990
|
}
|
|
1995
1991
|
if (cursor) {
|
|
1996
1992
|
if (cursor.cursorrules) {
|
|
1997
1993
|
const icon = fs16.existsSync(".cursorrules") ? chalk3.yellow("~") : chalk3.green("+");
|
|
1994
|
+
const desc = getDescription(".cursorrules");
|
|
1998
1995
|
console.log(` ${icon} ${chalk3.bold(".cursorrules")}`);
|
|
1999
|
-
console.log(chalk3.dim(
|
|
2000
|
-
console.log(chalk3.dim(" and conventions. Cursor reads this at the start of every session."));
|
|
1996
|
+
if (desc) console.log(chalk3.dim(` ${desc}`));
|
|
2001
1997
|
console.log("");
|
|
2002
1998
|
}
|
|
2003
1999
|
const rules = cursor.rules;
|
|
@@ -2005,9 +2001,14 @@ function printSetupSummary(setup) {
|
|
|
2005
2001
|
for (const rule of rules) {
|
|
2006
2002
|
const rulePath = `.cursor/rules/${rule.filename}`;
|
|
2007
2003
|
const icon = fs16.existsSync(rulePath) ? chalk3.yellow("~") : chalk3.green("+");
|
|
2004
|
+
const desc = getDescription(rulePath);
|
|
2008
2005
|
console.log(` ${icon} ${chalk3.bold(rulePath)}`);
|
|
2009
|
-
|
|
2010
|
-
|
|
2006
|
+
if (desc) {
|
|
2007
|
+
console.log(chalk3.dim(` ${desc}`));
|
|
2008
|
+
} else {
|
|
2009
|
+
const firstLine = rule.content.split("\n").filter((l) => l.trim() && !l.trim().startsWith("#"))[0];
|
|
2010
|
+
if (firstLine) console.log(chalk3.dim(` ${firstLine.trim().slice(0, 80)}`));
|
|
2011
|
+
}
|
|
2011
2012
|
console.log("");
|
|
2012
2013
|
}
|
|
2013
2014
|
}
|
|
@@ -2015,24 +2016,22 @@ function printSetupSummary(setup) {
|
|
|
2015
2016
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
2016
2017
|
const icon = fs16.existsSync(".cursor/mcp.json") ? chalk3.yellow("~") : chalk3.green("+");
|
|
2017
2018
|
const serverNames = Object.keys(mcpServers);
|
|
2019
|
+
const desc = getDescription(".cursor/mcp.json");
|
|
2018
2020
|
console.log(` ${icon} ${chalk3.bold(".cursor/mcp.json")}`);
|
|
2019
|
-
console.log(chalk3.dim(
|
|
2020
|
-
console.log(chalk3.dim(` Servers: ${serverNames.join(", ")}`));
|
|
2021
|
+
console.log(chalk3.dim(` ${desc || `servers: ${serverNames.join(", ")}`}`));
|
|
2021
2022
|
console.log("");
|
|
2022
2023
|
}
|
|
2023
2024
|
}
|
|
2024
|
-
|
|
2025
|
+
if (Array.isArray(deletions) && deletions.length > 0) {
|
|
2026
|
+
for (const del of deletions) {
|
|
2027
|
+
console.log(` ${chalk3.red("-")} ${chalk3.bold(del.filePath)}`);
|
|
2028
|
+
console.log(chalk3.dim(` ${del.reason}`));
|
|
2029
|
+
console.log("");
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
console.log(` ${chalk3.green("+")} ${chalk3.dim("new")} ${chalk3.yellow("~")} ${chalk3.dim("modified")} ${chalk3.red("-")} ${chalk3.dim("removed")}`);
|
|
2025
2033
|
console.log("");
|
|
2026
2034
|
}
|
|
2027
|
-
function extractClaudeMdSections(md) {
|
|
2028
|
-
return md.split("\n").filter((line) => /^##\s+/.test(line)).map((line) => line.replace(/^##\s+/, "").trim());
|
|
2029
|
-
}
|
|
2030
|
-
function summarizePermissions(permissions, maxShow = 3) {
|
|
2031
|
-
if (permissions.length === 0) return "None";
|
|
2032
|
-
const shown = permissions.slice(0, maxShow).join(", ");
|
|
2033
|
-
const remaining = permissions.length - maxShow;
|
|
2034
|
-
return remaining > 0 ? `${shown} (+${remaining} more)` : shown;
|
|
2035
|
-
}
|
|
2036
2035
|
function summarizeSkill(skill) {
|
|
2037
2036
|
const lines = skill.content.split("\n").filter((l) => l.trim() && !l.trim().startsWith("#"));
|
|
2038
2037
|
return lines[0]?.trim().slice(0, 80) || skill.name;
|
|
@@ -2043,8 +2042,6 @@ function collectSetupFiles(setup) {
|
|
|
2043
2042
|
const cursor = setup.cursor;
|
|
2044
2043
|
if (claude) {
|
|
2045
2044
|
if (claude.claudeMd) files.push({ path: "CLAUDE.md", content: claude.claudeMd });
|
|
2046
|
-
if (claude.settings) files.push({ path: ".claude/settings.json", content: JSON.stringify(claude.settings, null, 2) });
|
|
2047
|
-
if (claude.settingsLocal) files.push({ path: ".claude/settings.local.json", content: JSON.stringify(claude.settingsLocal, null, 2) });
|
|
2048
2045
|
const skills = claude.skills;
|
|
2049
2046
|
if (Array.isArray(skills)) {
|
|
2050
2047
|
for (const skill of skills) {
|
|
@@ -2070,26 +2067,38 @@ function collectSetupFiles(setup) {
|
|
|
2070
2067
|
}
|
|
2071
2068
|
return files;
|
|
2072
2069
|
}
|
|
2073
|
-
function
|
|
2070
|
+
function previewNewFile(filePath, content, maxLines = 40) {
|
|
2074
2071
|
const lines = content.split("\n");
|
|
2075
2072
|
const displayLines = lines.slice(0, maxLines);
|
|
2076
|
-
const maxWidth = Math.max(60, ...displayLines.map((l) => l.length + 4));
|
|
2077
|
-
const width = Math.min(maxWidth, 80);
|
|
2078
2073
|
console.log("");
|
|
2079
|
-
console.log(` ${chalk3.
|
|
2080
|
-
console.log(
|
|
2074
|
+
console.log(` ${chalk3.green("+ new")} ${chalk3.bold(filePath)}`);
|
|
2075
|
+
console.log(chalk3.dim(" \u2500".repeat(30)));
|
|
2081
2076
|
for (const line of displayLines) {
|
|
2082
|
-
|
|
2083
|
-
const padding = " ".repeat(Math.max(0, width - truncated.length - 3));
|
|
2084
|
-
console.log(` ${chalk3.dim("\u2502")} ${truncated}${padding}${chalk3.dim("\u2502")}`);
|
|
2077
|
+
console.log(` ${chalk3.green("+")} ${line}`);
|
|
2085
2078
|
}
|
|
2086
2079
|
if (lines.length > maxLines) {
|
|
2087
|
-
console.log(
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2080
|
+
console.log("");
|
|
2081
|
+
console.log(chalk3.dim(` ... ${lines.length - maxLines} more lines (${lines.length} total)`));
|
|
2082
|
+
}
|
|
2083
|
+
console.log("");
|
|
2084
|
+
}
|
|
2085
|
+
function previewDiff(filePath, oldContent, newContent) {
|
|
2086
|
+
const patch = createTwoFilesPatch(filePath, filePath, oldContent, newContent, "current", "proposed", { context: 3 });
|
|
2087
|
+
const patchLines = patch.split("\n").slice(2);
|
|
2088
|
+
console.log("");
|
|
2089
|
+
console.log(` ${chalk3.yellow("~ modified")} ${chalk3.bold(filePath)}`);
|
|
2090
|
+
console.log(chalk3.dim(" \u2500".repeat(30)));
|
|
2091
|
+
for (const line of patchLines) {
|
|
2092
|
+
if (line.startsWith("@@")) {
|
|
2093
|
+
console.log(` ${chalk3.cyan(line)}`);
|
|
2094
|
+
} else if (line.startsWith("+")) {
|
|
2095
|
+
console.log(` ${chalk3.green(line)}`);
|
|
2096
|
+
} else if (line.startsWith("-")) {
|
|
2097
|
+
console.log(` ${chalk3.red(line)}`);
|
|
2098
|
+
} else {
|
|
2099
|
+
console.log(` ${chalk3.dim(line)}`);
|
|
2100
|
+
}
|
|
2091
2101
|
}
|
|
2092
|
-
console.log(` ${chalk3.dim("\u2514" + "\u2500".repeat(width - 1) + "\u2518")}`);
|
|
2093
2102
|
console.log("");
|
|
2094
2103
|
}
|
|
2095
2104
|
async function promptFilePreview(setup) {
|
|
@@ -2098,11 +2107,19 @@ async function promptFilePreview(setup) {
|
|
|
2098
2107
|
console.log(chalk3.dim("\n No files to preview.\n"));
|
|
2099
2108
|
return;
|
|
2100
2109
|
}
|
|
2101
|
-
const
|
|
2102
|
-
|
|
2103
|
-
|
|
2110
|
+
const choices = files.map((f, i) => {
|
|
2111
|
+
const exists = fs16.existsSync(f.path);
|
|
2112
|
+
const icon = exists ? chalk3.yellow("~") : chalk3.green("+");
|
|
2113
|
+
return { name: `${icon} ${f.path}`, value: i };
|
|
2104
2114
|
});
|
|
2105
|
-
|
|
2115
|
+
const choice = await select({ message: "Which file to preview?", choices });
|
|
2116
|
+
const file = files[choice];
|
|
2117
|
+
if (fs16.existsSync(file.path)) {
|
|
2118
|
+
const existing = fs16.readFileSync(file.path, "utf-8");
|
|
2119
|
+
previewDiff(file.path, existing, file.content);
|
|
2120
|
+
} else {
|
|
2121
|
+
previewNewFile(file.path, file.content);
|
|
2122
|
+
}
|
|
2106
2123
|
}
|
|
2107
2124
|
|
|
2108
2125
|
// src/commands/undo.ts
|