@caliber-ai/cli 0.19.0 → 0.20.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 CHANGED
@@ -1503,6 +1503,7 @@ function stageFiles(files, projectDir) {
1503
1503
  cleanupStaging();
1504
1504
  let newFiles = 0;
1505
1505
  let modifiedFiles = 0;
1506
+ const stagedFiles = [];
1506
1507
  for (const file of files) {
1507
1508
  const proposedPath = path14.join(PROPOSED_DIR, file.path);
1508
1509
  fs15.mkdirSync(path14.dirname(proposedPath), { recursive: true });
@@ -1513,14 +1514,13 @@ function stageFiles(files, projectDir) {
1513
1514
  fs15.mkdirSync(path14.dirname(currentPath), { recursive: true });
1514
1515
  fs15.copyFileSync(originalPath, currentPath);
1515
1516
  modifiedFiles++;
1517
+ stagedFiles.push({ relativePath: file.path, proposedPath, currentPath, isNew: false });
1516
1518
  } else {
1517
1519
  newFiles++;
1520
+ stagedFiles.push({ relativePath: file.path, proposedPath, isNew: true });
1518
1521
  }
1519
1522
  }
1520
- return { newFiles, modifiedFiles };
1521
- }
1522
- function getStagedProposedDir() {
1523
- return PROPOSED_DIR;
1523
+ return { newFiles, modifiedFiles, stagedFiles };
1524
1524
  }
1525
1525
  function cleanupStaging() {
1526
1526
  if (fs15.existsSync(STAGED_DIR)) {
@@ -1529,7 +1529,7 @@ function cleanupStaging() {
1529
1529
  }
1530
1530
 
1531
1531
  // src/utils/editor.ts
1532
- import { execSync as execSync2 } from "child_process";
1532
+ import { execSync as execSync2, spawnSync } from "child_process";
1533
1533
  function commandExists(cmd) {
1534
1534
  try {
1535
1535
  execSync2(`which ${cmd}`, { stdio: "ignore" });
@@ -1538,40 +1538,31 @@ function commandExists(cmd) {
1538
1538
  return false;
1539
1539
  }
1540
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)) {
1541
+ function detectAvailableEditors() {
1542
+ const methods = [];
1543
+ if (commandExists("cursor")) methods.push("cursor");
1544
+ if (commandExists("code")) methods.push("vscode");
1545
+ methods.push("terminal");
1546
+ return methods;
1547
+ }
1548
+ function openDiffsInEditor(editor, files) {
1549
+ const cmd = editor === "cursor" ? "cursor" : "code";
1550
+ for (const file of files) {
1560
1551
  try {
1561
- execSync2(`${envEditor} "${dirPath}"`, { stdio: "ignore" });
1562
- return true;
1552
+ if (file.currentPath) {
1553
+ spawnSync(cmd, ["--diff", file.currentPath, file.proposedPath], { stdio: "ignore" });
1554
+ } else {
1555
+ spawnSync(cmd, [file.proposedPath], { stdio: "ignore" });
1556
+ }
1563
1557
  } catch {
1558
+ continue;
1564
1559
  }
1565
1560
  }
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;
1572
- }
1573
1561
  }
1574
1562
 
1563
+ // src/commands/init.ts
1564
+ import { createTwoFilesPatch } from "diff";
1565
+
1575
1566
  // src/lib/hooks.ts
1576
1567
  import fs16 from "fs";
1577
1568
  import path15 from "path";
@@ -1949,16 +1940,11 @@ async function initCommand(options) {
1949
1940
  printSetupSummary(generatedSetup);
1950
1941
  console.log(chalk3.hex("#6366f1").bold(" Step 5/6 \u2014 Review\n"));
1951
1942
  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."));
1959
- }
1960
- console.log(chalk3.dim(` ${chalk3.green(`${newFiles} new`)} / ${chalk3.yellow(`${modifiedFiles} modified`)} file${newFiles + modifiedFiles !== 1 ? "s" : ""}
1943
+ const staged = stageFiles(setupFiles, process.cwd());
1944
+ console.log(chalk3.dim(` ${chalk3.green(`${staged.newFiles} new`)} / ${chalk3.yellow(`${staged.modifiedFiles} modified`)} file${staged.newFiles + staged.modifiedFiles !== 1 ? "s" : ""}
1961
1945
  `));
1946
+ const reviewMethod = await promptReviewMethod();
1947
+ openReview(reviewMethod, staged.stagedFiles);
1962
1948
  let action = await promptReviewAction();
1963
1949
  while (action === "refine") {
1964
1950
  generatedSetup = await refineLoop(generatedSetup, targetAgent);
@@ -1970,11 +1956,9 @@ async function initCommand(options) {
1970
1956
  }
1971
1957
  const updatedFiles = collectSetupFiles(generatedSetup);
1972
1958
  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
1959
  console.log(chalk3.dim(` ${chalk3.green(`${restaged.newFiles} new`)} / ${chalk3.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
1977
1960
  `));
1961
+ openReview(reviewMethod, restaged.stagedFiles);
1978
1962
  printSetupSummary(generatedSetup);
1979
1963
  action = await promptReviewAction();
1980
1964
  }
@@ -2133,6 +2117,51 @@ async function promptAgent() {
2133
2117
  ]
2134
2118
  });
2135
2119
  }
2120
+ async function promptReviewMethod() {
2121
+ const available = detectAvailableEditors();
2122
+ if (available.length === 1) return "terminal";
2123
+ const choices = available.map((method) => {
2124
+ switch (method) {
2125
+ case "cursor":
2126
+ return { name: "Cursor (diff view)", value: "cursor" };
2127
+ case "vscode":
2128
+ return { name: "VS Code (diff view)", value: "vscode" };
2129
+ case "terminal":
2130
+ return { name: "Terminal", value: "terminal" };
2131
+ }
2132
+ });
2133
+ return select({ message: "How would you like to review the changes?", choices });
2134
+ }
2135
+ function openReview(method, stagedFiles) {
2136
+ if (method === "cursor" || method === "vscode") {
2137
+ openDiffsInEditor(method, stagedFiles.map((f) => ({
2138
+ currentPath: f.currentPath,
2139
+ proposedPath: f.proposedPath
2140
+ })));
2141
+ console.log(chalk3.dim(" Diffs opened in your editor.\n"));
2142
+ } else {
2143
+ for (const file of stagedFiles) {
2144
+ console.log(chalk3.bold(` ${file.relativePath}`));
2145
+ if (file.currentPath) {
2146
+ const current = fs18.readFileSync(file.currentPath, "utf-8");
2147
+ const proposed = fs18.readFileSync(file.proposedPath, "utf-8");
2148
+ const patch = createTwoFilesPatch(file.relativePath, file.relativePath, current, proposed, "current", "proposed");
2149
+ for (const line of patch.split("\n").slice(2)) {
2150
+ if (line.startsWith("+")) console.log(chalk3.green(` ${line}`));
2151
+ else if (line.startsWith("-")) console.log(chalk3.red(` ${line}`));
2152
+ else console.log(chalk3.dim(` ${line}`));
2153
+ }
2154
+ } else {
2155
+ console.log(chalk3.green(" (new file)"));
2156
+ const content = fs18.readFileSync(file.proposedPath, "utf-8");
2157
+ const preview = content.split("\n").slice(0, 20);
2158
+ for (const line of preview) console.log(chalk3.green(` +${line}`));
2159
+ if (content.split("\n").length > 20) console.log(chalk3.dim(` ... ${content.split("\n").length - 20} more lines`));
2160
+ }
2161
+ console.log("");
2162
+ }
2163
+ }
2164
+ }
2136
2165
  async function promptReviewAction() {
2137
2166
  return select({
2138
2167
  message: "What would you like to do?",