@caliber-ai/cli 0.19.0 → 0.20.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 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, originalPath, 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, spawn } from "child_process";
1533
1533
  function commandExists(cmd) {
1534
1534
  try {
1535
1535
  execSync2(`which ${cmd}`, { stdio: "ignore" });
@@ -1538,40 +1538,37 @@ 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.originalPath) {
1553
+ spawn(cmd, ["--diff", file.originalPath, file.proposedPath], {
1554
+ stdio: "ignore",
1555
+ detached: true
1556
+ }).unref();
1557
+ } else {
1558
+ spawn(cmd, [file.proposedPath], {
1559
+ stdio: "ignore",
1560
+ detached: true
1561
+ }).unref();
1562
+ }
1563
1563
  } catch {
1564
+ continue;
1564
1565
  }
1565
1566
  }
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
1567
  }
1574
1568
 
1569
+ // src/commands/init.ts
1570
+ import { createTwoFilesPatch } from "diff";
1571
+
1575
1572
  // src/lib/hooks.ts
1576
1573
  import fs16 from "fs";
1577
1574
  import path15 from "path";
@@ -1949,16 +1946,11 @@ async function initCommand(options) {
1949
1946
  printSetupSummary(generatedSetup);
1950
1947
  console.log(chalk3.hex("#6366f1").bold(" Step 5/6 \u2014 Review\n"));
1951
1948
  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" : ""}
1949
+ const staged = stageFiles(setupFiles, process.cwd());
1950
+ console.log(chalk3.dim(` ${chalk3.green(`${staged.newFiles} new`)} / ${chalk3.yellow(`${staged.modifiedFiles} modified`)} file${staged.newFiles + staged.modifiedFiles !== 1 ? "s" : ""}
1961
1951
  `));
1952
+ const reviewMethod = await promptReviewMethod();
1953
+ openReview(reviewMethod, staged.stagedFiles);
1962
1954
  let action = await promptReviewAction();
1963
1955
  while (action === "refine") {
1964
1956
  generatedSetup = await refineLoop(generatedSetup, targetAgent);
@@ -1970,11 +1962,9 @@ async function initCommand(options) {
1970
1962
  }
1971
1963
  const updatedFiles = collectSetupFiles(generatedSetup);
1972
1964
  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
1965
  console.log(chalk3.dim(` ${chalk3.green(`${restaged.newFiles} new`)} / ${chalk3.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
1977
1966
  `));
1967
+ openReview(reviewMethod, restaged.stagedFiles);
1978
1968
  printSetupSummary(generatedSetup);
1979
1969
  action = await promptReviewAction();
1980
1970
  }
@@ -2133,6 +2123,51 @@ async function promptAgent() {
2133
2123
  ]
2134
2124
  });
2135
2125
  }
2126
+ async function promptReviewMethod() {
2127
+ const available = detectAvailableEditors();
2128
+ if (available.length === 1) return "terminal";
2129
+ const choices = available.map((method) => {
2130
+ switch (method) {
2131
+ case "cursor":
2132
+ return { name: "Cursor (diff view)", value: "cursor" };
2133
+ case "vscode":
2134
+ return { name: "VS Code (diff view)", value: "vscode" };
2135
+ case "terminal":
2136
+ return { name: "Terminal", value: "terminal" };
2137
+ }
2138
+ });
2139
+ return select({ message: "How would you like to review the changes?", choices });
2140
+ }
2141
+ function openReview(method, stagedFiles) {
2142
+ if (method === "cursor" || method === "vscode") {
2143
+ openDiffsInEditor(method, stagedFiles.map((f) => ({
2144
+ originalPath: f.originalPath,
2145
+ proposedPath: f.proposedPath
2146
+ })));
2147
+ console.log(chalk3.dim(" Diffs opened in your editor.\n"));
2148
+ } else {
2149
+ for (const file of stagedFiles) {
2150
+ console.log(chalk3.bold(` ${file.relativePath}`));
2151
+ if (file.currentPath) {
2152
+ const current = fs18.readFileSync(file.currentPath, "utf-8");
2153
+ const proposed = fs18.readFileSync(file.proposedPath, "utf-8");
2154
+ const patch = createTwoFilesPatch(file.relativePath, file.relativePath, current, proposed, "current", "proposed");
2155
+ for (const line of patch.split("\n").slice(2)) {
2156
+ if (line.startsWith("+")) console.log(chalk3.green(` ${line}`));
2157
+ else if (line.startsWith("-")) console.log(chalk3.red(` ${line}`));
2158
+ else console.log(chalk3.dim(` ${line}`));
2159
+ }
2160
+ } else {
2161
+ console.log(chalk3.green(" (new file)"));
2162
+ const content = fs18.readFileSync(file.proposedPath, "utf-8");
2163
+ const preview = content.split("\n").slice(0, 20);
2164
+ for (const line of preview) console.log(chalk3.green(` +${line}`));
2165
+ if (content.split("\n").length > 20) console.log(chalk3.dim(` ... ${content.split("\n").length - 20} more lines`));
2166
+ }
2167
+ console.log("");
2168
+ }
2169
+ }
2170
+ }
2136
2171
  async function promptReviewAction() {
2137
2172
  return select({
2138
2173
  message: "What would you like to do?",